Skip to content

Commit e441ed6

Browse files
authored
Merge release into master (microsoft#5167)
* New release * Bumnp ptvsd and ls * Updated ptvsd * Track telemetry when switching to and from LS (microsoft#5164) * Capture telemetry when switching LS * Update changelog * Update src/client/activation/activationService.ts Co-Authored-By: DonJayamanne <don.jayamanne@yahoo.com> * Bump language server * Bump extension version
1 parent ccf9072 commit e441ed6

File tree

7 files changed

+91
-20
lines changed

7 files changed

+91
-20
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
# Changelog
22

33

4+
## 2019.3.3 (8 April 2019)
5+
6+
### Fixes
7+
8+
1. Update ptvsd to [4.2.7](https://github.com/Microsoft/ptvsd/releases/tag/v4.2.7).
9+
* Fix issues related to debugging Django templagtes.
10+
1. Update the Python language server to 0.2.47.
11+
12+
### Code Health
13+
14+
1. Capture telemetry to track switching to and from the Language Server.
15+
([#5162](https://github.com/Microsoft/vscode-python/issues/5162))
16+
17+
418
## 2019.3.2 (2 April 2019)
519

620
### Fixes

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "Python",
44
"description": "Linting, Debugging (multi-threaded, remote), Intellisense, code formatting, refactoring, unit tests, snippets, and more.",
55
"version": "2019.4.0-alpha",
6-
"languageServerVersion": "0.2.31",
6+
"languageServerVersion": "0.2.47",
77
"publisher": "ms-python",
88
"author": {
99
"name": "Microsoft Corporation"

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
jedi==0.12.0
22
parso==0.2.1
33
isort==4.3.4
4-
ptvsd==4.2.6
4+
ptvsd==4.2.7

src/client/activation/activationService.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import { IDiagnosticsService } from '../application/diagnostics/types';
1010
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../common/application/types';
1111
import { STANDARD_OUTPUT_CHANNEL } from '../common/constants';
1212
import '../common/extensions';
13-
import { IConfigurationService, IDisposableRegistry, IOutputChannel, IPythonSettings, Resource } from '../common/types';
13+
import { IConfigurationService, IDisposableRegistry, IOutputChannel, IPersistentStateFactory, IPythonSettings, Resource } from '../common/types';
14+
import { swallowExceptions } from '../common/utils/decorators';
1415
import { IServiceContainer } from '../ioc/types';
1516
import { sendTelemetryEvent } from '../telemetry';
1617
import { EventName } from '../telemetry/constants';
@@ -31,7 +32,8 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
3132
private readonly lsNotSupportedDiagnosticService: IDiagnosticsService;
3233
private resource!: Resource;
3334

34-
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
35+
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer,
36+
@inject(IPersistentStateFactory) private stateFactory: IPersistentStateFactory) {
3537
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
3638
this.output = this.serviceContainer.get<OutputChannel>(IOutputChannel, STANDARD_OUTPUT_CHANNEL);
3739
this.appShell = this.serviceContainer.get<IApplicationShell>(IApplicationShell);
@@ -98,7 +100,19 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
98100
this.currentActivator.activator.dispose();
99101
}
100102
}
101-
103+
@swallowExceptions('Switch Language Server')
104+
public async trackLangaugeServerSwitch(jediEnabled: boolean): Promise<void> {
105+
const state = this.stateFactory.createGlobalPersistentState<boolean | undefined>('SWITCH_LS', undefined);
106+
if (typeof state.value !== 'boolean') {
107+
await state.updateValue(jediEnabled);
108+
return;
109+
}
110+
if (state.value !== jediEnabled) {
111+
await state.updateValue(jediEnabled);
112+
const message = jediEnabled ? 'Switch to Jedi from LS' : 'Switch to LS from Jedi';
113+
sendTelemetryEvent(EventName.PYTHON_LANGUAGE_SERVER_SWITCHED, undefined, { change: message });
114+
}
115+
}
102116
protected onWorkspaceFoldersChanged() {
103117
//If an activated workspace folder was removed, dispose its activator
104118
const workspaceKeys = this.workspaceService.workspaceFolders!.map(workspaceFolder => this.getWorkspacePathKey(workspaceFolder.uri));
@@ -141,7 +155,9 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
141155
}
142156
private useJedi(): boolean {
143157
const configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
144-
return configurationService.getSettings(this.resource).jediEnabled;
158+
const enabled = configurationService.getSettings(this.resource).jediEnabled;
159+
this.trackLangaugeServerSwitch(enabled).ignoreErrors();
160+
return enabled;
145161
}
146162
private getWorkspacePathKey(resource: Resource): string {
147163
return this.workspaceService.getWorkspaceFolderIdentifier(resource, workspacePathNameForGlobalWorkspaces);

src/client/telemetry/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export enum EventName {
4949
UNITTEST_NAVIGATE_TEST_FUNCTION = 'UNITTEST.NAVIGATE.TEST_FUNCTION',
5050
UNITTEST_NAVIGATE_TEST_SUITE = 'UNITTEST.NAVIGATE.TEST_SUITE',
5151
UNITTEST_EXPLORER_WORK_SPACE_COUNT = 'UNITTEST.TEST_EXPLORER.WORK_SPACE_COUNT',
52+
PYTHON_LANGUAGE_SERVER_SWITCHED = 'PYTHON_LANGUAGE_SERVER.SWITCHED',
5253
PYTHON_LANGUAGE_SERVER_ANALYSISTIME = 'PYTHON_LANGUAGE_SERVER.ANALYSIS_TIME',
5354
PYTHON_LANGUAGE_SERVER_ENABLED = 'PYTHON_LANGUAGE_SERVER.ENABLED',
5455
PYTHON_LANGUAGE_SERVER_EXTRACTED = 'PYTHON_LANGUAGE_SERVER.EXTRACTED',

src/client/telemetry/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ export interface IEventNamePropertyMapping {
285285
[EventName.PYTHON_INTERPRETER_ACTIVATION_FOR_TERMINAL]: InterpreterActivation;
286286
[EventName.PYTHON_INTERPRETER_AUTO_SELECTION]: InterpreterAutoSelection;
287287
[EventName.PYTHON_INTERPRETER_DISCOVERY]: InterpreterDiscovery;
288+
[EventName.PYTHON_LANGUAGE_SERVER_SWITCHED]: { change: 'Switch to Jedi from LS' | 'Switch to LS from Jedi' };
288289
[EventName.PYTHON_LANGUAGE_SERVER_ANALYSISTIME]: { success: boolean };
289290
[EventName.PYTHON_LANGUAGE_SERVER_DOWNLOADED]: LanguageServerVersionTelemetry;
290291
[EventName.PYTHON_LANGUAGE_SERVER_ENABLED]: never | undefined;

src/test/activation/activationService.unit.test.ts

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import { LSNotSupportedDiagnosticServiceId } from '../../client/application/diag
2121
import { IDiagnostic, IDiagnosticsService } from '../../client/application/diagnostics/types';
2222
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../client/common/application/types';
2323
import { IPlatformService } from '../../client/common/platform/types';
24-
import { IConfigurationService, IDisposable, IDisposableRegistry, IOutputChannel, IPythonSettings, Resource } from '../../client/common/types';
24+
import { IConfigurationService, IDisposable, IDisposableRegistry, IOutputChannel, IPersistentState, IPersistentStateFactory, IPythonSettings, Resource } from '../../client/common/types';
2525
import { IServiceContainer } from '../../client/ioc/types';
2626

27+
// tslint:disable:no-any
28+
2729
suite('Activation - ActivationService', () => {
2830
[true, false].forEach(jediIsEnabled => {
2931
suite(`Jedi is ${jediIsEnabled ? 'enabled' : 'disabled'}`, () => {
@@ -34,12 +36,16 @@ suite('Activation - ActivationService', () => {
3436
let workspaceService: TypeMoq.IMock<IWorkspaceService>;
3537
let platformService: TypeMoq.IMock<IPlatformService>;
3638
let lsNotSupportedDiagnosticService: TypeMoq.IMock<IDiagnosticsService>;
39+
let stateFactory: TypeMoq.IMock<IPersistentStateFactory>;
40+
let state: TypeMoq.IMock<IPersistentState<boolean | undefined>>;
3741
setup(() => {
3842
serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
3943
appShell = TypeMoq.Mock.ofType<IApplicationShell>();
4044
workspaceService = TypeMoq.Mock.ofType<IWorkspaceService>();
4145
cmdManager = TypeMoq.Mock.ofType<ICommandManager>();
4246
platformService = TypeMoq.Mock.ofType<IPlatformService>();
47+
stateFactory = TypeMoq.Mock.ofType<IPersistentStateFactory>();
48+
state = TypeMoq.Mock.ofType<IPersistentState<boolean | undefined>>();
4349
const configService = TypeMoq.Mock.ofType<IConfigurationService>();
4450
pythonSettings = TypeMoq.Mock.ofType<IPythonSettings>();
4551
const langFolderServiceMock = TypeMoq.Mock.ofType<ILanguageServerFolderService>();
@@ -54,7 +60,10 @@ suite('Activation - ActivationService', () => {
5460
langFolderServiceMock
5561
.setup(l => l.getCurrentLanguageServerDirectory())
5662
.returns(() => Promise.resolve(folderVer));
57-
63+
stateFactory.setup(f => f.createGlobalPersistentState(TypeMoq.It.isValue('SWITCH_LS'), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
64+
.returns(() => state.object);
65+
state.setup(s => s.value).returns(() => undefined);
66+
state.setup(s => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve());
5867
const output = TypeMoq.Mock.ofType<IOutputChannel>();
5968
serviceContainer
6069
.setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny()))
@@ -101,7 +110,7 @@ suite('Activation - ActivationService', () => {
101110
if (lsSupported && !jediIsEnabled) {
102111
activatorName = LanguageServerActivator.DotNet;
103112
}
104-
let diagnostics : IDiagnostic[];
113+
let diagnostics: IDiagnostic[];
105114
if (!lsSupported && !jediIsEnabled) {
106115
diagnostics = [TypeMoq.It.isAny()];
107116
} else {
@@ -127,29 +136,29 @@ suite('Activation - ActivationService', () => {
127136
test('LS is supported', async () => {
128137
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
129138
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
130-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
139+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
131140

132141
await testActivation(activationService, activator, true);
133142
});
134143
test('LS is not supported', async () => {
135144
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
136145
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
137-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
146+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
138147

139148
await testActivation(activationService, activator, false);
140149
});
141150

142151
test('Activatory must be activated', async () => {
143152
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
144153
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
145-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
154+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
146155

147156
await testActivation(activationService, activator);
148157
});
149158
test('Activatory must be deactivated', async () => {
150159
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
151160
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
152-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
161+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
153162

154163
await testActivation(activationService, activator);
155164

@@ -171,7 +180,7 @@ suite('Activation - ActivationService', () => {
171180

172181
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabledValueInSetting);
173182
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
174-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
183+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
175184

176185
workspaceService.verifyAll();
177186
await testActivation(activationService, activator);
@@ -208,7 +217,7 @@ suite('Activation - ActivationService', () => {
208217

209218
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabledValueInSetting);
210219
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
211-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
220+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
212221

213222
workspaceService.verifyAll();
214223
await testActivation(activationService, activator);
@@ -244,7 +253,7 @@ suite('Activation - ActivationService', () => {
244253

245254
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
246255
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
247-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
256+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
248257

249258
workspaceService.verifyAll();
250259
await testActivation(activationService, activator);
@@ -279,7 +288,7 @@ suite('Activation - ActivationService', () => {
279288

280289
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
281290
const activator = TypeMoq.Mock.ofType<ILanguageServerActivator>();
282-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
291+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
283292

284293
workspaceService.verifyAll();
285294
await testActivation(activationService, activator);
@@ -304,12 +313,42 @@ suite('Activation - ActivationService', () => {
304313
appShell.verifyAll();
305314
cmdManager.verifyAll();
306315
});
316+
test('Track current LS usage for first usage', async () => {
317+
state.reset();
318+
state.setup(s => s.value).returns(() => undefined).verifiable(TypeMoq.Times.once());
319+
state.setup(s => s.updateValue(TypeMoq.It.isValue(true))).returns(() => Promise.resolve()).verifiable(TypeMoq.Times.once());
320+
321+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
322+
await activationService.trackLangaugeServerSwitch(true);
323+
324+
state.verifyAll();
325+
});
326+
test('Track switch to LS', async () => {
327+
state.reset();
328+
state.setup(s => s.value).returns(() => true).verifiable(TypeMoq.Times.once());
329+
state.setup(s => s.updateValue(TypeMoq.It.isValue(false))).returns(() => Promise.resolve()).verifiable(TypeMoq.Times.once());
330+
331+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
332+
await activationService.trackLangaugeServerSwitch(false);
333+
334+
state.verify(s => s.updateValue(TypeMoq.It.isValue(false)), TypeMoq.Times.once());
335+
});
336+
test('Track switch to Jedi', async () => {
337+
state.reset();
338+
state.setup(s => s.value).returns(() => false).verifiable(TypeMoq.Times.once());
339+
state.setup(s => s.updateValue(TypeMoq.It.isValue(true))).returns(() => Promise.resolve()).verifiable(TypeMoq.Times.once());
340+
341+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
342+
await activationService.trackLangaugeServerSwitch(true);
343+
344+
state.verify(s => s.updateValue(TypeMoq.It.isValue(true)), TypeMoq.Times.once());
345+
});
307346
if (!jediIsEnabled) {
308347
test('Revert to jedi when LS activation fails', async () => {
309348
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
310349
const activatorDotNet = TypeMoq.Mock.ofType<ILanguageServerActivator>();
311350
const activatorJedi = TypeMoq.Mock.ofType<ILanguageServerActivator>();
312-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
351+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
313352
const diagnostics: IDiagnostic[] = [];
314353
lsNotSupportedDiagnosticService
315354
.setup(l => l.diagnose(undefined))
@@ -388,7 +427,7 @@ suite('Activation - ActivationService', () => {
388427
.callback(cb => (workspaceFoldersChangedHandler = cb))
389428
.returns(() => TypeMoq.Mock.ofType<IDisposable>().object)
390429
.verifiable(TypeMoq.Times.once());
391-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
430+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
392431
workspaceService.verifyAll();
393432
expect(workspaceFoldersChangedHandler).not.to.be.equal(undefined, 'Handler not set');
394433
const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 };
@@ -430,7 +469,7 @@ suite('Activation - ActivationService', () => {
430469
test('Jedi is only activated once', async () => {
431470
pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled);
432471
const activator1 = TypeMoq.Mock.ofType<ILanguageServerActivator>();
433-
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object);
472+
const activationService = new LanguageServerExtensionActivationService(serviceContainer.object, stateFactory.object);
434473
const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 };
435474
const folder2 = { name: 'two', uri: Uri.parse('two'), index: 2 };
436475
serviceContainer

0 commit comments

Comments
 (0)