Skip to content

Commit 293b117

Browse files
authored
Ensure interpreter is auto selected before updating status bar (microsoft#4139)
For microsoft#3501
1 parent 150cf62 commit 293b117

35 files changed

+547
-259
lines changed

src/client/activation/activationManager.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { inject, injectable, multiInject } from 'inversify';
77
import { TextDocument, workspace } from 'vscode';
88
import { IApplicationDiagnostics } from '../application/types';
99
import { IDocumentManager, IWorkspaceService } from '../common/application/types';
10-
import { isTestExecution } from '../common/constants';
10+
import { PYTHON_LANGUAGE } from '../common/constants';
1111
import { traceDecorators } from '../common/logger';
1212
import { IDisposable, Resource } from '../common/types';
1313
import { IInterpreterAutoSelectionService } from '../interpreter/autoSelection/types';
@@ -26,27 +26,40 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
2626
@inject(IInterpreterAutoSelectionService) private readonly autoSelection: IInterpreterAutoSelectionService,
2727
@inject(IApplicationDiagnostics) private readonly appDiagnostics: IApplicationDiagnostics,
2828
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService
29-
) { }
29+
) {}
3030

3131
public dispose() {
3232
while (this.disposables.length > 0) {
33-
const disposable = this.disposables.shift();
33+
const disposable = this.disposables.shift()!;
3434
disposable.dispose();
3535
}
36-
if (this. docOpenedHandler){
36+
if (this.docOpenedHandler) {
3737
this.docOpenedHandler.dispose();
3838
this.docOpenedHandler = undefined;
3939
}
4040
}
4141
public async activate(): Promise<void> {
4242
await this.initialize();
4343
await this.activateWorkspace(this.getActiveResource());
44+
await this.autoSelection.autoSelectInterpreter(undefined);
45+
}
46+
@traceDecorators.error('Failed to activate a workspace')
47+
public async activateWorkspace(resource: Resource) {
48+
const key = this.getWorkspaceKey(resource);
49+
if (this.activatedWorkspaces.has(key)) {
50+
return;
51+
}
52+
this.activatedWorkspaces.add(key);
53+
// Get latest interpreter list in the background.
54+
this.interpreterService.getInterpreters(resource).ignoreErrors();
55+
56+
await this.autoSelection.autoSelectInterpreter(resource);
57+
await Promise.all(this.activationServices.map(item => item.activate(resource)));
58+
await this.appDiagnostics.performPreStartupHealthCheck(resource);
4459
}
4560
protected async initialize() {
46-
// Get latest interpreter list.
47-
const mainWorkspaceUri = this.getActiveResource();
48-
this.interpreterService.getInterpreters(mainWorkspaceUri).ignoreErrors();
4961
this.addHandlers();
62+
this.addRemoveDocOpenedHandlers();
5063
}
5164
protected addHandlers() {
5265
this.disposables.push(this.workspaceService.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this));
@@ -67,45 +80,32 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
6780
this.addRemoveDocOpenedHandlers();
6881
}
6982
protected hasMultipleWorkspaces() {
70-
return this.workspaceService.hasWorkspaceFolders && this.workspaceService.workspaceFolders.length > 1;
83+
return this.workspaceService.hasWorkspaceFolders && this.workspaceService.workspaceFolders!.length > 1;
7184
}
7285
protected onDocOpened(doc: TextDocument) {
86+
if (doc.languageId !== PYTHON_LANGUAGE) {
87+
return;
88+
}
7389
const key = this.getWorkspaceKey(doc.uri);
90+
// If we have opened a doc that does not belong to workspace, then do nothing.
91+
if (key === '' && this.workspaceService.hasWorkspaceFolders) {
92+
return;
93+
}
7494
if (this.activatedWorkspaces.has(key)) {
7595
return;
7696
}
7797
const folder = this.workspaceService.getWorkspaceFolder(doc.uri);
7898
this.activateWorkspace(folder ? folder.uri : undefined).ignoreErrors();
7999
}
80-
@traceDecorators.error('Failed to activate a worksapce')
81-
protected async activateWorkspace(resource: Resource) {
82-
const key = this.getWorkspaceKey(resource);
83-
this.activatedWorkspaces.add(key);
84-
85-
await Promise.all(this.activationServices.map(item => item.activate(resource)));
86-
87-
// When testing, do not perform health checks, as modal dialogs can be displayed.
88-
if (!isTestExecution()) {
89-
await this.appDiagnostics.performPreStartupHealthCheck(resource);
90-
}
91-
await this.autoSelection.autoSelectInterpreter(resource);
92-
}
93100
protected getWorkspaceKey(resource: Resource) {
94-
if (!resource) {
95-
return '';
96-
}
97-
const workspaceFolder = this.workspaceService.getWorkspaceFolder(resource);
98-
if (!workspaceFolder) {
99-
return '';
100-
}
101-
return workspaceFolder.uri.fsPath;
101+
return this.workspaceService.getWorkspaceFolderIdentifier(resource, '');
102102
}
103103
private getActiveResource(): Resource {
104104
if (this.documentManager.activeTextEditor && !this.documentManager.activeTextEditor.document.isUntitled) {
105105
return this.documentManager.activeTextEditor.document.uri;
106106
}
107-
return Array.isArray(this.workspaceService.workspaceFolders) && workspace.workspaceFolders.length > 0
108-
? workspace.workspaceFolders[0].uri
107+
return Array.isArray(this.workspaceService.workspaceFolders) && workspace.workspaceFolders!.length > 0
108+
? workspace.workspaceFolders![0].uri
109109
: undefined;
110110
}
111111
}

src/client/activation/activationService.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ type ActivatorInfo = { jedi: boolean; activator: ILanguageServerActivator };
2222
@injectable()
2323
export class LanguageServerExtensionActivationService implements IExtensionActivationService, Disposable {
2424
private currentActivator?: ActivatorInfo;
25-
private activatedOnce: boolean;
25+
private activatedOnce: boolean = false;
2626
private readonly workspaceService: IWorkspaceService;
2727
private readonly output: OutputChannel;
2828
private readonly appShell: IApplicationShell;
2929
private readonly lsNotSupportedDiagnosticService: IDiagnosticsService;
30+
private resource!: Resource;
3031

3132
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
3233
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
@@ -41,10 +42,11 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
4142
disposables.push(this.workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this)));
4243
}
4344

44-
public async activate(_resource: Resource): Promise<void> {
45+
public async activate(resource: Resource): Promise<void> {
4546
if (this.currentActivator || this.activatedOnce) {
4647
return;
4748
}
49+
this.resource = resource;
4850
this.activatedOnce = true;
4951

5052
let jedi = this.useJedi();
@@ -114,10 +116,7 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
114116
}
115117
}
116118
private useJedi(): boolean {
117-
const workspacesUris: (Uri | undefined)[] = this.workspaceService.hasWorkspaceFolders
118-
? this.workspaceService.workspaceFolders!.map(item => item.uri)
119-
: [undefined];
120-
const configuraionService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
121-
return workspacesUris.filter(uri => configuraionService.getSettings(uri).jediEnabled).length > 0;
119+
const configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
120+
return configurationService.getSettings(this.resource).jediEnabled;
122121
}
123122
}

src/client/activation/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { IDisposable, LanguageServerDownloadChannels, Resource } from '../common
1313
export const IExtensionActivationManager = Symbol('IExtensionActivationManager');
1414
export interface IExtensionActivationManager extends IDisposable {
1515
activate(): Promise<void>;
16+
activateWorkspace(resource: Resource): Promise<void>;
1617
}
1718

1819
export const IExtensionActivationService = Symbol('IExtensionActivationService');

src/client/application/diagnostics/applicationDiagnostics.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { inject, injectable, named } from 'inversify';
77
import { DiagnosticSeverity } from 'vscode';
8-
import { STANDARD_OUTPUT_CHANNEL } from '../../common/constants';
8+
import { isTestExecution, STANDARD_OUTPUT_CHANNEL } from '../../common/constants';
99
import { ILogger, IOutputChannel, Resource } from '../../common/types';
1010
import { IServiceContainer } from '../../ioc/types';
1111
import { IApplicationDiagnostics } from '../types';
@@ -21,6 +21,10 @@ export class ApplicationDiagnostics implements IApplicationDiagnostics {
2121
this.serviceContainer.get<ISourceMapSupportService>(ISourceMapSupportService).register();
2222
}
2323
public async performPreStartupHealthCheck(resource: Resource): Promise<void> {
24+
// When testing, do not perform health checks, as modal dialogs can be displayed.
25+
if (!isTestExecution()) {
26+
return;
27+
}
2428
const services = this.serviceContainer.getAll<IDiagnosticsService>(IDiagnosticsService);
2529
// Perform these validation checks in the foreground.
2630
await this.runDiagnostics(services.filter(item => !item.runInBackground), resource);

src/client/common/application/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ export interface IWorkspaceService {
586586
* @returns {string}
587587
* @memberof IWorkspaceService
588588
*/
589-
getWorkspaceFolderIdentifier(resource: Uri | undefined): string;
589+
getWorkspaceFolderIdentifier(resource: Uri | undefined, defaultValue?: string): string;
590590
/**
591591
* Returns a path that is relative to the workspace folder or folders.
592592
*

src/client/common/application/workspace.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { injectable } from 'inversify';
55
import { CancellationToken, ConfigurationChangeEvent, Event, FileSystemWatcher, GlobPattern, Uri, workspace, WorkspaceConfiguration, WorkspaceFolder, WorkspaceFoldersChangeEvent } from 'vscode';
6+
import { Resource } from '../types';
67
import { IWorkspaceService } from './types';
78

89
@injectable()
@@ -37,8 +38,8 @@ export class WorkspaceService implements IWorkspaceService {
3738
public findFiles(include: GlobPattern, exclude?: GlobPattern, maxResults?: number, token?: CancellationToken): Thenable<Uri[]> {
3839
return workspace.findFiles(include, exclude, maxResults, token);
3940
}
40-
public getWorkspaceFolderIdentifier(resource: Uri): string {
41+
public getWorkspaceFolderIdentifier(resource: Resource, defaultValue: string = ''): string {
4142
const workspaceFolder = resource ? workspace.getWorkspaceFolder(resource) : undefined;
42-
return workspaceFolder ? workspaceFolder.uri.fsPath : '';
43+
return workspaceFolder ? workspaceFolder.uri.fsPath : defaultValue;
4344
}
4445
}

src/client/common/configSettings.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class PythonSettings implements IPythonSettings {
6565
return this.changed.event;
6666
}
6767

68-
constructor(workspaceFolder: Uri | undefined, private readonly InterpreterAutoSelectionService: IInterpreterAutoSeletionProxyService,
68+
constructor(workspaceFolder: Uri | undefined, private readonly interpreterAutoSelectionService: IInterpreterAutoSeletionProxyService,
6969
workspace?: IWorkspaceService) {
7070
this.workspace = workspace || new WorkspaceService();
7171
this.workspaceRoot = workspaceFolder ? workspaceFolder : Uri.file(__dirname);
@@ -129,9 +129,9 @@ export class PythonSettings implements IPythonSettings {
129129
this.pythonPath = systemVariables.resolveAny(pythonSettings.get<string>('pythonPath'))!;
130130
// If user has defined a custom value, use it else try to get the best interpreter ourselves.
131131
if (this.pythonPath.length === 0 || this.pythonPath === 'python') {
132-
const autoSelectedPythonInterpreter = this.InterpreterAutoSelectionService.getAutoSelectedInterpreter(this.workspaceRoot);
132+
const autoSelectedPythonInterpreter = this.interpreterAutoSelectionService.getAutoSelectedInterpreter(this.workspaceRoot);
133133
if (autoSelectedPythonInterpreter) {
134-
this.InterpreterAutoSelectionService.setWorkspaceInterpreter(this.workspaceRoot, autoSelectedPythonInterpreter).ignoreErrors();
134+
this.interpreterAutoSelectionService.setWorkspaceInterpreter(this.workspaceRoot, autoSelectedPythonInterpreter).ignoreErrors();
135135
}
136136
this.pythonPath = autoSelectedPythonInterpreter ? autoSelectedPythonInterpreter.path : this.pythonPath;
137137
}
@@ -382,7 +382,7 @@ export class PythonSettings implements IPythonSettings {
382382
// Let's defer the change notification.
383383
this.debounceChangeNotification();
384384
};
385-
this.disposables.push(this.InterpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(onDidChange.bind(this)));
385+
this.disposables.push(this.interpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(onDidChange.bind(this)));
386386
this.disposables.push(this.workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => {
387387
if (event.affectsConfiguration('python')) {
388388
onDidChange();

src/client/common/process/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export type InterpreterInfomation = {
7373
sysVersion: string;
7474
architecture: Architecture;
7575
sysPrefix: string;
76+
pipEnvWorkspaceFolder?: string;
7677
};
7778
export const IPythonExecutionService = Symbol('IPythonExecutionService');
7879

src/client/common/terminal/environmentActivationProviders/pipEnvActivationProvider.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
import { inject, injectable } from 'inversify';
77
import { Uri } from 'vscode';
88
import { IInterpreterService, InterpreterType, IPipEnvService } from '../../../interpreter/contracts';
9+
import { IWorkspaceService } from '../../application/types';
10+
import { IFileSystem } from '../../platform/types';
911
import { ITerminalActivationCommandProvider, TerminalShellType } from '../types';
1012

1113
@injectable()
1214
export class PipEnvActivationCommandProvider implements ITerminalActivationCommandProvider {
1315
constructor(
1416
@inject(IInterpreterService) private readonly interpreterService: IInterpreterService,
15-
@inject(IPipEnvService) private readonly pipenvService: IPipEnvService
17+
@inject(IPipEnvService) private readonly pipenvService: IPipEnvService,
18+
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
19+
@inject(IFileSystem) private readonly fs: IFileSystem
1620
) { }
1721

1822
public isShellSupported(_targetShell: TerminalShellType): boolean {
@@ -24,7 +28,12 @@ export class PipEnvActivationCommandProvider implements ITerminalActivationComma
2428
if (!interpreter || interpreter.type !== InterpreterType.Pipenv) {
2529
return;
2630
}
27-
31+
// Activate using `pipenv shell` only if the current folder relates pipenv environment.
32+
const workspaceFolder = resource ? this.workspaceService.getWorkspaceFolder(resource) : undefined;
33+
if (workspaceFolder && interpreter.pipEnvWorkspaceFolder &&
34+
!this.fs.arePathsSame(workspaceFolder.uri.fsPath, interpreter.pipEnvWorkspaceFolder)) {
35+
return;
36+
}
2837
const execName = this.pipenvService.executable;
2938
return [`${execName.fileToCommandArgument()} shell`];
3039
}

0 commit comments

Comments
 (0)