Skip to content

Commit 5e884b1

Browse files
Mikhail Arkhipovjakebailey
andauthored
Activate banner prompt for Pylance (#12817)
* Fix path * Actually fix settings * Add news * Add test * Format * Suppress 'jediEnabled' removal * Drop survey first launch threshold * Remove LS experiments * Frequency + tests * Fix test * Update message to match spec * Open workspace for extension rather than changing setting * Fix localization string * Show banners asynchronously * Add experiments * Formatting * Typo * Put back verifyAll * Remove obsolete experiments, add Pylance * Suppress experiment if Pylance is installed * PR feedback Co-authored-by: Jake Bailey <[email protected]>
1 parent acfc132 commit 5e884b1

22 files changed

+339
-500
lines changed

experiments.json

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,6 @@
6565
"min": 0,
6666
"max": 0
6767
},
68-
{
69-
"name": "LS - enabled",
70-
"salt": "LS",
71-
"min": 0,
72-
"max": 4
73-
},
74-
{
75-
"name": "LS - control",
76-
"salt": "LS",
77-
"min": 20,
78-
"max": 24
79-
},
8068
{
8169
"name": "UseTerminalToGetActivatedEnvVars - experiment",
8270
"salt": "UseTerminalToGetActivatedEnvVars",

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,7 +1827,6 @@
18271827
"default": [],
18281828
"items": {
18291829
"enum": [
1830-
"LS - enabled",
18311830
"AlwaysDisplayTestExplorer - experiment",
18321831
"ShowExtensionSurveyPrompt - enabled",
18331832
"Reload - experiment",
@@ -1841,6 +1840,7 @@
18411840
"DeprecatePythonPath - experiment",
18421841
"RunByLine - experiment",
18431842
"EnableTrustedNotebooks",
1843+
"tryPylance",
18441844
"All"
18451845
]
18461846
},
@@ -1852,7 +1852,6 @@
18521852
"default": [],
18531853
"items": {
18541854
"enum": [
1855-
"LS - enabled",
18561855
"AlwaysDisplayTestExplorer - experiment",
18571856
"ShowExtensionSurveyPrompt - enabled",
18581857
"Reload - experiment",
@@ -1866,6 +1865,7 @@
18661865
"DeprecatePythonPath - experiment",
18671866
"RunByLine - experiment",
18681867
"EnableTrustedNotebooks",
1868+
"tryPylance",
18691869
"All"
18701870
]
18711871
},

package.nls.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@
109109
"python.snippet.launch.django.label": "Python: Django",
110110
"python.snippet.launch.flask.label": "Python: Flask",
111111
"python.snippet.launch.pyramid.label": "Python: Pyramid Application",
112+
"LanguageService.proposePylanceMessage": "Try out a new faster, feature-rich language server for Python by Microsoft, Pylance! Install the extension now.",
113+
"LanguageService.tryItNow": "Try it now",
114+
"LanguageService.remindMeLater": "Remind me later",
112115
"LanguageService.bannerLabelYes": "Yes, take survey now",
113116
"LanguageService.bannerLabelNo": "No, thanks",
114117
"LanguageService.lsFailedToStart": "We encountered an issue starting the Language Server. Reverting to the alternative, Jedi. Check the Python output panel for details.",

src/client/activation/activationService.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ import { LSNotSupportedDiagnosticServiceId } from '../application/diagnostics/ch
99
import { IDiagnosticsService } from '../application/diagnostics/types';
1010
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../common/application/types';
1111
import { STANDARD_OUTPUT_CHANNEL } from '../common/constants';
12-
import { LSControl, LSEnabled } from '../common/experiments/groups';
1312
import { traceError } from '../common/logger';
1413
import {
1514
IConfigurationService,
1615
IDisposableRegistry,
17-
IExperimentsManager,
1816
IOutputChannel,
1917
IPersistentStateFactory,
2018
IPythonSettings,
@@ -58,8 +56,7 @@ export class LanguageServerExtensionActivationService
5856

5957
constructor(
6058
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
61-
@inject(IPersistentStateFactory) private stateFactory: IPersistentStateFactory,
62-
@inject(IExperimentsManager) private readonly abExperiments: IExperimentsManager
59+
@inject(IPersistentStateFactory) private stateFactory: IPersistentStateFactory
6360
) {
6461
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
6562
this.interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
@@ -175,17 +172,6 @@ export class LanguageServerExtensionActivationService
175172
* @returns `true` if user is using jedi, `false` if user is using language server
176173
*/
177174
public useJedi(): boolean {
178-
// Check if `languageServer` setting is missing (default configuration).
179-
if (this.isJediUsingDefaultConfiguration(this.resource)) {
180-
// If user is assigned to an experiment (i.e. use LS), return false.
181-
if (this.abExperiments.inExperiment(LSEnabled)) {
182-
return false;
183-
}
184-
// Send telemetry if user is in control group
185-
this.abExperiments.sendTelemetryIfInExperiment(LSControl);
186-
return true; // Do use Jedi as it is default.
187-
}
188-
// Configuration is non-default, so `languageServer` should be present.
189175
const configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
190176
const lstType = configurationService.getSettings(this.resource).languageServer;
191177
return lstType === LanguageServerType.Jedi;

src/client/activation/languageServer/activator.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
import { inject, injectable } from 'inversify';
4+
import { inject, injectable, named } from 'inversify';
55
import * as path from 'path';
66

77
import { IWorkspaceService } from '../../common/application/types';
8+
import { isTestExecution } from '../../common/constants';
89
import { traceDecorators } from '../../common/logger';
910
import { IFileSystem } from '../../common/platform/types';
10-
import { IConfigurationService, Resource } from '../../common/types';
11+
import { BANNER_NAME_PROPOSE_LS, IConfigurationService, IPythonExtensionBanner, Resource } from '../../common/types';
12+
import { PythonInterpreter } from '../../pythonEnvironments/info';
1113
import { LanguageServerActivatorBase } from '../common/activatorBase';
1214
import { ILanguageServerDownloader, ILanguageServerFolderService, ILanguageServerManager } from '../types';
1315

@@ -27,11 +29,21 @@ export class DotNetLanguageServerActivator extends LanguageServerActivatorBase {
2729
@inject(IFileSystem) fs: IFileSystem,
2830
@inject(ILanguageServerDownloader) lsDownloader: ILanguageServerDownloader,
2931
@inject(ILanguageServerFolderService) languageServerFolderService: ILanguageServerFolderService,
30-
@inject(IConfigurationService) configurationService: IConfigurationService
32+
@inject(IConfigurationService) configurationService: IConfigurationService,
33+
@inject(IPythonExtensionBanner)
34+
@named(BANNER_NAME_PROPOSE_LS)
35+
private proposePylancePopup: IPythonExtensionBanner
3136
) {
3237
super(manager, workspace, fs, lsDownloader, languageServerFolderService, configurationService);
3338
}
3439

40+
public async start(resource: Resource, interpreter?: PythonInterpreter): Promise<void> {
41+
if (!isTestExecution()) {
42+
this.proposePylancePopup.showBanner().ignoreErrors();
43+
}
44+
return super.start(resource, interpreter);
45+
}
46+
3547
@traceDecorators.error('Failed to ensure language server is available')
3648
public async ensureLanguageServerIsAvailable(resource: Resource): Promise<void> {
3749
const languageServerFolderPath = await this.ensureLanguageServerFileIsAvailable(resource, 'mscorlib.dll');

src/client/activation/node/languageServerFolderService.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@ import { inject, injectable } from 'inversify';
88
import * as path from 'path';
99
import { SemVer } from 'semver';
1010
import { IWorkspaceService } from '../../common/application/types';
11+
import { PYLANCE_EXTENSION_ID } from '../../common/constants';
1112
import { NugetPackage } from '../../common/nuget/types';
1213
import { IConfigurationService, IExtensions, Resource } from '../../common/types';
1314
import { IServiceContainer } from '../../ioc/types';
1415
import { LanguageServerFolderService } from '../common/languageServerFolderService';
1516
import { FolderVersionPair, ILanguageServerFolderService, NodeLanguageServerFolder } from '../types';
1617

17-
export const PylanceExtensionName = 'ms-python.vscode-pylance';
18-
1918
class FallbackNodeLanguageServerFolderService extends LanguageServerFolderService {
2019
constructor(serviceContainer: IServiceContainer) {
2120
super(serviceContainer, NodeLanguageServerFolder);
@@ -101,7 +100,7 @@ export class NodeLanguageServerFolderService implements ILanguageServerFolderSer
101100
return undefined;
102101
}
103102

104-
const extension = this.extensions.getExtension<ILSExtensionApi>(PylanceExtensionName);
103+
const extension = this.extensions.getExtension<ILSExtensionApi>(PYLANCE_EXTENSION_ID);
105104
if (!extension) {
106105
return undefined;
107106
}

src/client/activation/none/activator.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
3-
import { injectable } from 'inversify';
3+
import { inject, injectable, named } from 'inversify';
44
import {
55
CancellationToken,
66
CodeLens,
@@ -20,7 +20,8 @@ import {
2020
TextDocument,
2121
WorkspaceEdit
2222
} from 'vscode';
23-
import { Resource } from '../../common/types';
23+
import { isTestExecution } from '../../common/constants';
24+
import { BANNER_NAME_PROPOSE_LS, IPythonExtensionBanner, Resource } from '../../common/types';
2425
import { PythonInterpreter } from '../../pythonEnvironments/info';
2526
import { ILanguageServerActivator } from '../types';
2627

@@ -33,8 +34,16 @@ import { ILanguageServerActivator } from '../types';
3334
*/
3435
@injectable()
3536
export class NoLanguageServerExtensionActivator implements ILanguageServerActivator {
36-
// tslint:disable-next-line: no-empty
37-
public async start(_resource: Resource, _interpreter?: PythonInterpreter): Promise<void> {}
37+
constructor(
38+
@inject(IPythonExtensionBanner)
39+
@named(BANNER_NAME_PROPOSE_LS)
40+
private proposePylancePopup: IPythonExtensionBanner
41+
) {}
42+
public async start(_resource: Resource, _interpreter?: PythonInterpreter): Promise<void> {
43+
if (!isTestExecution()) {
44+
this.proposePylancePopup.showBanner().ignoreErrors();
45+
}
46+
}
3847
// tslint:disable-next-line: no-empty
3948
public dispose(): void {}
4049
// tslint:disable-next-line: no-empty

src/client/activation/serviceRegistry.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { DataScienceSurveyBanner } from '../datascience/dataScienceSurveyBanner';
1313
import { InteractiveShiftEnterBanner } from '../datascience/shiftEnterBanner';
1414
import { IServiceManager } from '../ioc/types';
15-
import { ProposeLanguageServerBanner } from '../languageServices/proposeLanguageServerBanner';
15+
import { ProposePylanceBanner } from '../languageServices/proposeLanguageServerBanner';
1616
import { AATesting } from './aaTesting';
1717
import { ExtensionActivationManager } from './activationManager';
1818
import { LanguageServerExtensionActivationService } from './activationService';
@@ -86,7 +86,7 @@ export function registerTypes(serviceManager: IServiceManager, languageServerTyp
8686

8787
serviceManager.addSingleton<IPythonExtensionBanner>(
8888
IPythonExtensionBanner,
89-
ProposeLanguageServerBanner,
89+
ProposePylanceBanner,
9090
BANNER_NAME_PROPOSE_LS
9191
);
9292
serviceManager.addSingleton<IPythonExtensionBanner>(

src/client/common/application/applicationEnvironment.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,7 @@ export class ApplicationEnvironment implements IApplicationEnvironment {
8888
const version = parse(this.packageJson.version);
8989
return !version || version.prerelease.length > 0 ? 'insiders' : 'stable';
9090
}
91+
public get uriScheme(): string {
92+
return vscode.env.uriScheme;
93+
}
9194
}

src/client/common/application/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,10 @@ export interface IApplicationEnvironment {
10241024
* The version of the editor.
10251025
*/
10261026
readonly vscodeVersion: string;
1027+
/**
1028+
* The custom uri scheme the editor registers to in the operating system.
1029+
*/
1030+
readonly uriScheme: string;
10271031
}
10281032

10291033
export const IWebPanelMessageListener = Symbol('IWebPanelMessageListener');

src/client/common/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const PYTHON_ALLFILES = [{ language: PYTHON_LANGUAGE }];
1515

1616
export const PVSC_EXTENSION_ID = 'ms-python.python';
1717
export const CODE_RUNNER_EXTENSION_ID = 'formulahendry.code-runner';
18+
export const PYLANCE_EXTENSION_ID = 'ms-python.vscode-pylance';
1819
export const AppinsightsKey = 'AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217';
1920

2021
export namespace Commands {

src/client/common/experiments/groups.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
export const LSControl = 'LS - control';
2-
export const LSEnabled = 'LS - enabled';
3-
41
// Experiment to check whether to always display the test explorer.
52
export enum AlwaysDisplayTestExplorerGroups {
63
control = 'AlwaysDisplayTestExplorer - control',
@@ -99,3 +96,8 @@ export enum RemoveKernelToolbarInInteractiveWindow {
9996
export enum EnableTrustedNotebooks {
10097
experiment = 'EnableTrustedNotebooks'
10198
}
99+
100+
// Experiment to offer switch to Pylance language server
101+
export enum TryPylance {
102+
experiment = 'tryPylance'
103+
}

src/client/common/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ export interface IPythonExtensionBanner {
528528
readonly enabled: boolean;
529529
showBanner(): Promise<void>;
530530
}
531-
export const BANNER_NAME_PROPOSE_LS: string = 'ProposeLS';
531+
export const BANNER_NAME_PROPOSE_LS: string = 'ProposePylance';
532532
export const BANNER_NAME_DS_SURVEY: string = 'DSSurveyBanner';
533533
export const BANNER_NAME_INTERACTIVE_SHIFTENTER: string = 'InteractiveShiftEnterBanner';
534534

src/client/common/utils/localize.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ export namespace AttachProcess {
101101
}
102102

103103
export namespace LanguageService {
104+
export const proposePylanceMessage = localize(
105+
'LanguageService.proposePylanceMessage',
106+
'Try out a new faster, feature-rich language server for Python by Microsoft, Pylance! Install the extension now.'
107+
);
108+
export const tryItNow = localize('LanguageService.tryItNow', 'Try it now');
109+
export const remindMeLater = localize('LanguageService.remindMeLater', 'Remind me later');
110+
104111
export const bannerLabelYes = localize('LanguageService.bannerLabelYes', 'Yes, take survey now');
105112
export const bannerLabelNo = localize('LanguageService.bannerLabelNo', 'No, thanks');
106113
export const lsFailedToStart = localize(

0 commit comments

Comments
 (0)