|  | 
|  | 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. | 
|  | 2 | +// Licensed under the MIT License. | 
|  | 3 | + | 
|  | 4 | +import { inject, injectable, named } from 'inversify'; | 
|  | 5 | +import { ConfigurationTarget, Disposable, Uri } from 'vscode'; | 
|  | 6 | +import { IExtensionActivationService } from '../../activation/types'; | 
|  | 7 | +import { IApplicationShell, IWorkspaceService } from '../../common/application/types'; | 
|  | 8 | +import { traceDecorators } from '../../common/logger'; | 
|  | 9 | +import { IDisposableRegistry, IPersistentStateFactory } from '../../common/types'; | 
|  | 10 | +import { sleep } from '../../common/utils/async'; | 
|  | 11 | +import { InteractiveShiftEnterBanner, Interpreters } from '../../common/utils/localize'; | 
|  | 12 | +import { sendTelemetryEvent } from '../../telemetry'; | 
|  | 13 | +import { EventName } from '../../telemetry/constants'; | 
|  | 14 | +import { IPythonPathUpdaterServiceManager } from '../configuration/types'; | 
|  | 15 | +import { IInterpreterHelper, IInterpreterLocatorService, IInterpreterWatcherBuilder, PythonInterpreter, WORKSPACE_VIRTUAL_ENV_SERVICE } from '../contracts'; | 
|  | 16 | + | 
|  | 17 | +const doNotDisplayPromptStateKey = 'MESSAGE_KEY_FOR_VIRTUAL_ENV'; | 
|  | 18 | +@injectable() | 
|  | 19 | +export class VirtualEnvironmentPrompt implements IExtensionActivationService { | 
|  | 20 | + constructor( | 
|  | 21 | + @inject(IInterpreterWatcherBuilder) private readonly builder: IInterpreterWatcherBuilder, | 
|  | 22 | + @inject(IPersistentStateFactory) private readonly persistentStateFactory: IPersistentStateFactory, | 
|  | 23 | + @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, | 
|  | 24 | + @inject(IInterpreterHelper) private readonly helper: IInterpreterHelper, | 
|  | 25 | + @inject(IPythonPathUpdaterServiceManager) private readonly pythonPathUpdaterService: IPythonPathUpdaterServiceManager, | 
|  | 26 | + @inject(IInterpreterLocatorService) @named(WORKSPACE_VIRTUAL_ENV_SERVICE) private readonly locator: IInterpreterLocatorService, | 
|  | 27 | + @inject(IDisposableRegistry) private readonly disposableRegistry: Disposable[], | 
|  | 28 | + @inject(IApplicationShell) private readonly appShell: IApplicationShell) { } | 
|  | 29 | + | 
|  | 30 | + public async activate(resource: Uri): Promise<void> { | 
|  | 31 | + const watcher = await this.builder.getWorkspaceVirtualEnvInterpreterWatcher(resource); | 
|  | 32 | + watcher.onDidCreate(() => { | 
|  | 33 | + this.handleNewEnvironment(resource).ignoreErrors(); | 
|  | 34 | + }, this, this.disposableRegistry); | 
|  | 35 | + } | 
|  | 36 | + | 
|  | 37 | + @traceDecorators.error('Error in event handler for detection of new environment') | 
|  | 38 | + protected async handleNewEnvironment(resource: Uri): Promise<void> { | 
|  | 39 | + // Wait for a while, to ensure environment gets created and is accessible (as this is slow on Windows) | 
|  | 40 | + await sleep(1000); | 
|  | 41 | + const interpreters = await this.locator.getInterpreters(resource); | 
|  | 42 | + const interpreter = this.helper.getBestInterpreter(interpreters); | 
|  | 43 | + if (!interpreter || this.hasUserDefinedPythonPath(resource)) { | 
|  | 44 | + return; | 
|  | 45 | + } | 
|  | 46 | + await this.notifyUser(interpreter, resource); | 
|  | 47 | + } | 
|  | 48 | + protected async notifyUser(interpreter: PythonInterpreter, resource: Uri): Promise<void> { | 
|  | 49 | + const notificationPromptEnabled = this.persistentStateFactory.createWorkspacePersistentState(doNotDisplayPromptStateKey, true); | 
|  | 50 | + if (!notificationPromptEnabled.value) { | 
|  | 51 | + return; | 
|  | 52 | + } | 
|  | 53 | + const prompts = [InteractiveShiftEnterBanner.bannerLabelYes(), InteractiveShiftEnterBanner.bannerLabelNo(), Interpreters.doNotShowAgain()]; | 
|  | 54 | + const telemetrySelections: ['Yes', 'No', 'Ignore'] = ['Yes', 'No', 'Ignore']; | 
|  | 55 | + const selection = await this.appShell.showInformationMessage(Interpreters.environmentPromptMessage(), ...prompts); | 
|  | 56 | + sendTelemetryEvent(EventName.PYTHON_INTERPRETER_ACTIVATE_ENVIRONMENT_PROMPT, undefined, { selection: selection ? telemetrySelections[prompts.indexOf(selection)] : undefined }); | 
|  | 57 | + if (!selection) { | 
|  | 58 | + return; | 
|  | 59 | + } | 
|  | 60 | + if (selection === prompts[0]) { | 
|  | 61 | + await this.pythonPathUpdaterService.updatePythonPath(interpreter.path, ConfigurationTarget.WorkspaceFolder, 'ui', resource); | 
|  | 62 | + } else if (selection === prompts[2]) { | 
|  | 63 | + await notificationPromptEnabled.updateValue(false); | 
|  | 64 | + } | 
|  | 65 | + } | 
|  | 66 | + protected hasUserDefinedPythonPath(resource?: Uri) { | 
|  | 67 | + const settings = this.workspaceService.getConfiguration('python', resource)!.inspect<string>('pythonPath')!; | 
|  | 68 | + return ((settings.workspaceFolderValue && settings.workspaceFolderValue !== 'python') || | 
|  | 69 | + (settings.workspaceValue && settings.workspaceValue !== 'python')) ? true : false; | 
|  | 70 | + } | 
|  | 71 | +} | 
0 commit comments