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 7 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.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"supported": false
},
"virtualWorkspaces": {
"supported": false,
"supported": "limited",
"description": "Limited support on the web."
}
},
Expand Down
38 changes: 26 additions & 12 deletions src/client/activation/activationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import { Deferred } from '../common/utils/async';
import { IInterpreterAutoSelectionService } from '../interpreter/autoSelection/types';
import { traceDecoratorError } from '../logging';
import { sendActivationTelemetry } from '../telemetry/envFileTelemetry';
import { IExtensionActivationManager, IExtensionActivationService, IExtensionSingleActivationService } from './types';
import {
IExtensionActivationManager,
IExtensionActivationService,
IExtensionSingleActivationService,
ILanguageServerActivation,
} from './types';

@injectable()
export class ExtensionActivationManager implements IExtensionActivationManager {
Expand All @@ -39,6 +44,7 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
@inject(IActiveResourceService) private readonly activeResourceService: IActiveResourceService,
@inject(IExperimentService) private readonly experiments: IExperimentService,
@inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService,
@inject(ILanguageServerActivation) private readonly languageServerActivation: ILanguageServerActivation,
) {}

public dispose(): void {
Expand All @@ -56,10 +62,15 @@ 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.activateWorkspace(this.activeResourceService.getActiveResource()),
]);

if (this.workspaceService.isVirtualWorkspace) {
await this.activateWorkspace(this.activeResourceService.getActiveResource());
} else {
await Promise.all([
Promise.all(this.singleActivationServices.map((item) => item.activate())),
this.activateWorkspace(this.activeResourceService.getActiveResource()),
]);
}
}

@traceDecoratorError('Failed to activate a workspace')
Expand All @@ -70,15 +81,18 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
}
this.activatedWorkspaces.add(key);

if (this.experiments.inExperimentSync(DeprecatePythonPath.experiment)) {
await this.interpreterPathService.copyOldInterpreterStorageValuesToNew(resource);
}

await sendActivationTelemetry(this.fileSystem, this.workspaceService, resource);

await this.autoSelection.autoSelectInterpreter(resource);
await Promise.all(this.activationServices.map((item) => item.activate(resource)));
await this.appDiagnostics.performPreStartupHealthCheck(resource);
if (this.workspaceService.isVirtualWorkspace) {
await this.languageServerActivation.activate(resource);
} else {
if (this.experiments.inExperimentSync(DeprecatePythonPath.experiment)) {
await this.interpreterPathService.copyOldInterpreterStorageValuesToNew(resource);
}
await this.autoSelection.autoSelectInterpreter(resource);
await Promise.all(this.activationServices.map((item) => item.activate(resource)));
await this.appDiagnostics.performPreStartupHealthCheck(resource);
}
}

public async initialize(): Promise<void> {
Expand Down
15 changes: 9 additions & 6 deletions src/client/activation/activationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class LanguageServerExtensionActivationService

private readonly output: OutputChannel;

private readonly interpreterService: IInterpreterService;
private readonly interpreterService?: IInterpreterService;

private readonly languageServerChangeHandler: LanguageServerChangeHandler;

Expand All @@ -67,14 +67,18 @@ export class LanguageServerExtensionActivationService
) {
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
this.configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
this.interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
if (!this.workspaceService.isVirtualWorkspace) {
this.interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
}
this.output = this.serviceContainer.get<OutputChannel>(IOutputChannel, STANDARD_OUTPUT_CHANNEL);

const disposables = serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
disposables.push(this);
disposables.push(this.workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this)));
disposables.push(this.workspaceService.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this));
disposables.push(this.interpreterService.onDidChangeInterpreter(this.onDidChangeInterpreter.bind(this)));
if (this.interpreterService) {
disposables.push(this.interpreterService.onDidChangeInterpreter(this.onDidChangeInterpreter.bind(this)));
}

this.languageServerChangeHandler = new LanguageServerChangeHandler(
this.getCurrentLanguageServerType(),
Expand All @@ -91,7 +95,7 @@ export class LanguageServerExtensionActivationService
const stopWatch = new StopWatch();
// Get a new server and dispose of the old one (might be the same one)
this.resource = resource;
const interpreter = await this.interpreterService.getActiveInterpreter(resource);
const interpreter = await this.interpreterService?.getActiveInterpreter(resource);
const key = await this.getKey(resource, interpreter);

// If we have an old server with a different key, then deactivate it as the
Expand Down Expand Up @@ -234,7 +238,6 @@ export class LanguageServerExtensionActivationService
}

this.sendTelemetryForChosenLanguageServer(serverType).ignoreErrors();

await this.logStartup(serverType);
let server = this.serviceContainer.get<ILanguageServerActivator>(ILanguageServerActivator, serverType);
try {
Expand Down Expand Up @@ -304,7 +307,7 @@ export class LanguageServerExtensionActivationService
resource,
workspacePathNameForGlobalWorkspaces,
);
interpreter = interpreter || (await this.interpreterService.getActiveInterpreter(resource));
interpreter = interpreter || (await this.interpreterService?.getActiveInterpreter(resource));
const interperterPortion = interpreter ? `${interpreter.path}-${interpreter.envName}` : '';
return `${resourcePortion}-${interperterPortion}`;
}
Expand Down
2 changes: 2 additions & 0 deletions src/client/activation/serviceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
IExtensionActivationService,
IExtensionSingleActivationService,
ILanguageClientFactory,
ILanguageServerActivation,
ILanguageServerActivator,
ILanguageServerAnalysisOptions,
ILanguageServerCache,
Expand All @@ -37,6 +38,7 @@ import { LoadLanguageServerExtension } from './common/loadLanguageServerExtensio
export function registerTypes(serviceManager: IServiceManager, languageServerType: LanguageServerType): void {
serviceManager.addSingleton<ILanguageServerCache>(ILanguageServerCache, LanguageServerExtensionActivationService);
serviceManager.addBinding(ILanguageServerCache, IExtensionActivationService);
serviceManager.addBinding(ILanguageServerCache, ILanguageServerActivation);
serviceManager.add<IExtensionActivationManager>(IExtensionActivationManager, ExtensionActivationManager);
serviceManager.add<ILanguageServerActivator>(
ILanguageServerActivator,
Expand Down
3 changes: 3 additions & 0 deletions src/client/activation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ export interface ILanguageServerCache {
get(resource: Resource, interpreter?: PythonEnvironment): Promise<ILanguageServer>;
}

export const ILanguageServerActivation = Symbol('ILanguageServerActivation');
export interface ILanguageServerActivation extends IExtensionActivationService {}

export type FolderVersionPair = { path: string; version: SemVer };
export const ILanguageServerFolderService = Symbol('ILanguageServerFolderService');

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
1 change: 1 addition & 0 deletions src/client/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const PYTHON = [
{ scheme: 'vscode-notebook', language: PYTHON_LANGUAGE },
{ scheme: NotebookCellScheme, language: PYTHON_LANGUAGE },
{ scheme: InteractiveInputScheme, language: PYTHON_LANGUAGE },
{ scheme: 'vscode-vfs', language: PYTHON_LANGUAGE },
];

export const PYTHON_NOTEBOOKS = [
Expand Down
29 changes: 20 additions & 9 deletions src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { sendErrorTelemetry, sendStartupTelemetry } from './startupTelemetry';
import { IStartupDurations } from './types';
import { runAfterActivation } from './common/utils/runAfterActivation';
import { IInterpreterService } from './interpreter/contracts';
import { WorkspaceService } from './common/application/workspace';

durations.codeLoadingTime = stopWatch.elapsedTime;

Expand All @@ -68,9 +69,13 @@ 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();

const workspace = new WorkspaceService();
if (!workspace.isVirtualWorkspace) {
sendStartupTelemetry(ready, durations, stopWatch, serviceContainer)
// Run in the background.
.ignoreErrors();
}
return api;
}

Expand Down Expand Up @@ -131,12 +136,14 @@ async function activateUnsafe(

setTimeout(async () => {
if (activatedServiceContainer) {
const interpreterManager = activatedServiceContainer.get<IInterpreterService>(IInterpreterService);
const workspaceService = activatedServiceContainer.get<IWorkspaceService>(IWorkspaceService);
const workspaces = workspaceService.workspaceFolders ?? [];
await interpreterManager
.refresh(workspaces.length > 0 ? workspaces[0].uri : undefined)
.catch((ex) => traceError('Python Extension: interpreterManager.refresh', ex));
if (!workspaceService.isVirtualWorkspace) {
const interpreterManager = activatedServiceContainer.get<IInterpreterService>(IInterpreterService);
const workspaces = workspaceService.workspaceFolders ?? [];
await interpreterManager
.refresh(workspaces.length > 0 ? workspaces[0].uri : undefined)
.catch((ex) => traceError('Python Extension: interpreterManager.refresh', ex));
}
}

runAfterActivation();
Expand All @@ -159,7 +166,11 @@ 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);

const workspace = new WorkspaceService();
if (!workspace.isVirtualWorkspace) {
await sendErrorTelemetry(ex, startupDurations, activatedServiceContainer);
}
}

interface IAppShell {
Expand Down
Loading