Skip to content

Only activate language servers when running in virtual workspaces #17519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/1 Enhancements/17519.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Declare limited support when running in virtual workspaces by only supporting language servers.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"supported": false
},
"virtualWorkspaces": {
"supported": false,
"description": "Limited support on the web."
"supported": "limited",
"description": "Only Partial IntelliSense supported."
}
},
"languageServerVersion": "0.5.30",
Expand Down
14 changes: 12 additions & 2 deletions src/client/activation/activationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,16 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
@inject(IActiveResourceService) private readonly activeResourceService: IActiveResourceService,
@inject(IExperimentService) private readonly experiments: IExperimentService,
@inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService,
) {}
) {
if (this.workspaceService.isVirtualWorkspace) {
this.activationServices = this.activationServices.filter(
(service) => service.supportedWorkspaceTypes.virtualWorkspace,
);
this.singleActivationServices = this.singleActivationServices.filter(
(service) => service.supportedWorkspaceTypes.virtualWorkspace,
);
}
}

public dispose(): void {
while (this.disposables.length > 0) {
Expand All @@ -56,8 +65,9 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
await this.initialize();

// Activate all activation services together.

await Promise.all([
Promise.all(this.singleActivationServices.map((item) => item.activate())),
...this.singleActivationServices.map((item) => item.activate()),
this.activateWorkspace(this.activeResourceService.getActiveResource()),
]);
}
Expand Down
3 changes: 2 additions & 1 deletion src/client/activation/activationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export class LanguageServerExtensionActivationService

private activatedServer?: IActivatedServer;

public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true };

private readonly workspaceService: IWorkspaceService;

private readonly configurationService: IConfigurationService;
Expand Down Expand Up @@ -234,7 +236,6 @@ export class LanguageServerExtensionActivationService
}

this.sendTelemetryForChosenLanguageServer(serverType).ignoreErrors();

await this.logStartup(serverType);
let server = this.serviceContainer.get<ILanguageServerActivator>(ILanguageServerActivator, serverType);
try {
Expand Down
11 changes: 8 additions & 3 deletions src/client/activation/common/analysisOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
import { Disposable, Event, EventEmitter, WorkspaceFolder } from 'vscode';
import { DocumentFilter, LanguageClientOptions, RevealOutputChannelOn } from 'vscode-languageclient/node';
import { IWorkspaceService } from '../../common/application/types';

import { PYTHON, PYTHON_LANGUAGE } from '../../common/constants';
import { IOutputChannel, Resource } from '../../common/types';
Expand All @@ -15,7 +16,10 @@ export abstract class LanguageServerAnalysisOptionsBase implements ILanguageServ
protected readonly didChange = new EventEmitter<void>();
private readonly output: IOutputChannel;

protected constructor(lsOutputChannel: ILanguageServerOutputChannel) {
protected constructor(
lsOutputChannel: ILanguageServerOutputChannel,
protected readonly workspace: IWorkspaceService,
) {
this.output = lsOutputChannel.channel;
}

Expand Down Expand Up @@ -51,7 +55,7 @@ export abstract class LanguageServerAnalysisOptionsBase implements ILanguageServ
}

protected getDocumentFilters(_workspaceFolder?: WorkspaceFolder): DocumentFilter[] {
return PYTHON;
return this.workspace.isVirtualWorkspace ? [{ language: PYTHON_LANGUAGE }] : PYTHON;
}
Comment on lines 57 to 59
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support all virtual workspaces schemes.


protected async getInitializationOptions(): Promise<any> {
Expand All @@ -66,8 +70,9 @@ export abstract class LanguageServerAnalysisOptionsWithEnv extends LanguageServe
protected constructor(
private readonly envVarsProvider: IEnvironmentVariablesProvider,
lsOutputChannel: ILanguageServerOutputChannel,
workspace: IWorkspaceService,
) {
super(lsOutputChannel);
super(lsOutputChannel, workspace);
}

public async initialize(_resource: Resource, _interpreter: PythonEnvironment | undefined) {
Expand Down
2 changes: 2 additions & 0 deletions src/client/activation/common/loadLanguageServerExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { IExtensionSingleActivationService } from '../types';

@injectable()
export class LoadLanguageServerExtension implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true };

constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry,
Expand Down
1 change: 1 addition & 0 deletions src/client/activation/extensionSurvey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const WAIT_TIME_TO_SHOW_SURVEY = 1000 * 60 * 60 * 3; // 3 hours

@injectable()
export class ExtensionSurveyPrompt implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true };
constructor(
@inject(IApplicationShell) private appShell: IApplicationShell,
@inject(IBrowserService) private browserService: IBrowserService,
Expand Down
4 changes: 2 additions & 2 deletions src/client/activation/jedi/analysisOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export class JediLanguageServerAnalysisOptions extends LanguageServerAnalysisOpt
@inject(IEnvironmentVariablesProvider) envVarsProvider: IEnvironmentVariablesProvider,
@inject(ILanguageServerOutputChannel) lsOutputChannel: ILanguageServerOutputChannel,
@inject(IConfigurationService) private readonly configurationService: IConfigurationService,
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
@inject(IWorkspaceService) workspace: IWorkspaceService,
) {
super(envVarsProvider, lsOutputChannel);
super(envVarsProvider, lsOutputChannel, workspace);
this.resource = undefined;
}

Expand Down
8 changes: 6 additions & 2 deletions src/client/activation/node/analysisOptions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { inject, injectable } from 'inversify';
import { IWorkspaceService } from '../../common/application/types';

import { LanguageServerAnalysisOptionsBase } from '../common/analysisOptions';
import { ILanguageServerOutputChannel } from '../types';

@injectable()
export class NodeLanguageServerAnalysisOptions extends LanguageServerAnalysisOptionsBase {
constructor(@inject(ILanguageServerOutputChannel) lsOutputChannel: ILanguageServerOutputChannel) {
super(lsOutputChannel);
constructor(
@inject(ILanguageServerOutputChannel) lsOutputChannel: ILanguageServerOutputChannel,
@inject(IWorkspaceService) workspace: IWorkspaceService,
) {
super(lsOutputChannel, workspace);
}

protected async getInitializationOptions() {
Expand Down
2 changes: 2 additions & 0 deletions src/client/activation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const IExtensionActivationService = Symbol('IExtensionActivationService')
* @interface IExtensionActivationService
*/
export interface IExtensionActivationService {
supportedWorkspaceTypes: { untrustedWorkspace: boolean; virtualWorkspace: boolean };
activate(resource: Resource): Promise<void>;
}

Expand Down Expand Up @@ -178,5 +179,6 @@ export const IExtensionSingleActivationService = Symbol('IExtensionSingleActivat
* @interface IExtensionSingleActivationService
*/
export interface IExtensionSingleActivationService {
supportedWorkspaceTypes: { untrustedWorkspace: boolean; virtualWorkspace: boolean };
activate(): Promise<void>;
}
1 change: 1 addition & 0 deletions src/client/common/application/commands/reloadCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { IApplicationShell, ICommandManager } from '../types';
*/
@injectable()
export class ReloadVSCodeCommandHandler implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true };
constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IApplicationShell) private readonly appShell: IApplicationShell,
Expand Down
2 changes: 2 additions & 0 deletions src/client/common/application/commands/reportIssueCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { EnvironmentType } from '../../../pythonEnvironments/info';
*/
@injectable()
export class ReportIssueCommandHandler implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true };

constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
Expand Down
1 change: 1 addition & 0 deletions src/client/common/application/debugSessionTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class TelemetryTracker implements DebugAdapterTracker {

@injectable()
export class DebugSessionTelemetry implements DebugAdapterTrackerFactory, IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true };
constructor(
@inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry,
@inject(IDebugService) debugService: IDebugService,
Expand Down
5 changes: 4 additions & 1 deletion src/client/common/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,10 @@ export interface IWorkspaceService {
* @memberof IWorkspaceService
*/
readonly hasWorkspaceFolders: boolean;

/**
* Returns if we're running in a virtual workspace.
*/
readonly isVirtualWorkspace: boolean;
/**
* Returns the [workspace folder](#WorkspaceFolder) that contains a given uri.
* * returns `undefined` when the given uri doesn't match any workspace folder
Expand Down
6 changes: 6 additions & 0 deletions src/client/common/application/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export class WorkspaceService implements IWorkspaceService {
: defaultValue;
}

public get isVirtualWorkspace(): boolean {
const isVirtualWorkspace =
workspace.workspaceFolders && workspace.workspaceFolders.every((f) => f.uri.scheme !== 'file');
return !!isVirtualWorkspace;
}

private get searchExcludes() {
const searchExcludes = this.getConfiguration('search.exclude');
const enabledSearchExcludes = Object.keys(searchExcludes).filter((key) => searchExcludes.get(key) === true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { traceDecoratorError } from '../../logging';

@injectable()
export class InsidersExtensionService implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true };
constructor(
@inject(IExtensionChannelService) private readonly extensionChannelService: IExtensionChannelService,
@inject(IInsiderExtensionPrompt) private readonly insidersPrompt: IInsiderExtensionPrompt,
Expand Down
1 change: 1 addition & 0 deletions src/client/common/persistentState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export type KeysStorage = { key: string; defaultValue: unknown };

@injectable()
export class PersistentStateFactory implements IPersistentStateFactory, IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true };
public readonly _globalKeysStorage = new PersistentState<KeysStorage[]>(
this.globalState,
GLOBAL_PERSISTENT_KEYS,
Expand Down
1 change: 1 addition & 0 deletions src/client/debugger/extension/adapter/activator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { IDebugAdapterDescriptorFactory, IDebugSessionLoggingFactory, IOutdatedD

@injectable()
export class DebugAdapterActivator implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
constructor(
@inject(IDebugService) private readonly debugService: IDebugService,
@inject(IDebugAdapterDescriptorFactory) private descriptorFactory: IDebugAdapterDescriptorFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum JsonLanguages {

@injectable()
export class LaunchJsonCompletionProvider implements CompletionItemProvider, IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
constructor(
@inject(ILanguageService) private readonly languageService: ILanguageService,
@inject(IDisposableRegistry) private readonly disposableRegistry: IDisposableRegistry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IConfigurationService, IDisposable, IDisposableRegistry } from '../../.

@injectable()
export class InterpreterPathCommand implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IConfigurationService) private readonly configurationService: IConfigurationService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export class LaunchJsonUpdaterServiceHelper {

@injectable()
export class LaunchJsonUpdaterService implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IDisposableRegistry) private readonly disposableRegistry: IDisposableRegistry,
Expand Down
2 changes: 2 additions & 0 deletions src/client/debugger/extension/debugCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { DebugPurpose, LaunchRequestArguments } from '../types';

@injectable()
export class DebugCommands implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };

constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IDebugService) private readonly debugService: IDebugService,
Expand Down
4 changes: 3 additions & 1 deletion src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export async function activate(context: IExtensionContext): Promise<IExtensionAp
}
// Send the "success" telemetry only if activation did not fail.
// Otherwise Telemetry is send via the error handler.

sendStartupTelemetry(ready, durations, stopWatch, serviceContainer)
// Run in the background.
.ignoreErrors();
Expand Down Expand Up @@ -131,8 +132,8 @@ async function activateUnsafe(

setTimeout(async () => {
if (activatedServiceContainer) {
const interpreterManager = activatedServiceContainer.get<IInterpreterService>(IInterpreterService);
const workspaceService = activatedServiceContainer.get<IWorkspaceService>(IWorkspaceService);
const interpreterManager = activatedServiceContainer.get<IInterpreterService>(IInterpreterService);
const workspaces = workspaceService.workspaceFolders ?? [];
await interpreterManager
.refresh(workspaces.length > 0 ? workspaces[0].uri : undefined)
Expand All @@ -159,6 +160,7 @@ async function handleError(ex: Error, startupDurations: IStartupDurations) {
"Extension activation failed, run the 'Developer: Toggle Developer Tools' command for more information.",
);
traceError('extension activation failed', ex);

await sendErrorTelemetry(ex, startupDurations, activatedServiceContainer);
}

Expand Down
Loading