Skip to content

Commit cda34d4

Browse files
author
Mikhail Arkhipov
authored
LS settings for typeshed and diagnostics (#2065)
* LS symbol providers * Typeshed paths * Typeshed submodule * Add submodule * New settings * Add diagnostics control settings * Add typeshed paths change check * Exclude some typeshed files from packages
1 parent d17f16f commit cda34d4

File tree

5 files changed

+138
-39
lines changed

5 files changed

+138
-39
lines changed

.vscodeignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,13 @@ scripts/**
4646
src/**
4747
test/**
4848
tmp/**
49+
typeshed/tests/**
50+
typeshed/.flake8
51+
typeshed/.git
52+
typeshed/.gitignore
53+
typeshed/.travis.yml
54+
typeshed/CONTRIBUTING.md
55+
typeshed/README.md
56+
typeshed/*.txt
4957
typings/**
5058
vsc-extension-quickstart.md

package.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,57 @@
12751275
"description": "Path to directory containing the Jedi library (this path will contain the 'Jedi' sub directory).",
12761276
"scope": "resource"
12771277
},
1278+
"python.analysis.openFilesOnly": {
1279+
"type": "boolean",
1280+
"default": false,
1281+
"description": "Only show errors and warnings for open files rather than for the entire workspace.",
1282+
"scope": "resource"
1283+
},
1284+
"python.analysis.typeshedPaths": {
1285+
"type": "array",
1286+
"default": [],
1287+
"items": {
1288+
"type": "string"
1289+
},
1290+
"description": "Paths to look for typeshed modules.",
1291+
"scope": "resource"
1292+
},
1293+
"python.analysis.errors": {
1294+
"type": "array",
1295+
"default": [],
1296+
"items": {
1297+
"type": "string"
1298+
},
1299+
"description": "List of diagnostics messages to be shown as errors.",
1300+
"scope": "resource"
1301+
},
1302+
"python.analysis.warnings": {
1303+
"type": "array",
1304+
"default": [],
1305+
"items": {
1306+
"type": "string"
1307+
},
1308+
"description": "List of diagnostics messages to be shown as warnings.",
1309+
"scope": "resource"
1310+
},
1311+
"python.analysis.information": {
1312+
"type": "array",
1313+
"default": [],
1314+
"items": {
1315+
"type": "string"
1316+
},
1317+
"description": "List of diagnostics messages to be shown as information.",
1318+
"scope": "resource"
1319+
},
1320+
"python.analysis.disabled": {
1321+
"type": "array",
1322+
"default": [],
1323+
"items": {
1324+
"type": "string"
1325+
},
1326+
"description": "List of suppressed diagnostic messages.",
1327+
"scope": "resource"
1328+
},
12781329
"python.linting.enabled": {
12791330
"type": "boolean",
12801331
"default": true,

src/client/activation/analysis.ts

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import * as path from 'path';
66
import { OutputChannel, Uri } from 'vscode';
77
import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient';
88
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../common/application/types';
9+
import { PythonSettings } from '../common/configSettings';
910
import { isTestExecution, STANDARD_OUTPUT_CHANNEL } from '../common/constants';
1011
import { createDeferred, Deferred } from '../common/helpers';
1112
import { IFileSystem, IPlatformService } from '../common/platform/types';
1213
import { StopWatch } from '../common/stopWatch';
13-
import { IConfigurationService, IExtensionContext, IOutputChannel } from '../common/types';
14-
import { IInterpreterService } from '../interpreter/contracts';
14+
import { IConfigurationService, IExtensionContext, IOutputChannel, IPythonSettings } from '../common/types';
1515
import { IServiceContainer } from '../ioc/types';
1616
import {
1717
PYTHON_ANALYSIS_ENGINE_DOWNLOADED,
@@ -38,7 +38,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
3838
private readonly fs: IFileSystem;
3939
private readonly sw = new StopWatch();
4040
private readonly platformData: PlatformData;
41-
private readonly interpreterService: IInterpreterService;
4241
private readonly startupCompleted: Deferred<void>;
4342
private readonly disposables: Disposable[] = [];
4443
private readonly context: IExtensionContext;
@@ -47,6 +46,8 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
4746

4847
private languageClient: LanguageClient | undefined;
4948
private interpreterHash: string = '';
49+
private excludedFiles: string[] = [];
50+
private typeshedPaths: string[] = [];
5051
private loadExtensionArgs: {} | undefined;
5152

5253
constructor(@inject(IServiceContainer) private readonly services: IServiceContainer) {
@@ -56,7 +57,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
5657
this.output = this.services.get<OutputChannel>(IOutputChannel, STANDARD_OUTPUT_CHANNEL);
5758
this.fs = this.services.get<IFileSystem>(IFileSystem);
5859
this.platformData = new PlatformData(services.get<IPlatformService>(IPlatformService), this.fs);
59-
this.interpreterService = this.services.get<IInterpreterService>(IInterpreterService);
6060
this.workspace = this.services.get<IWorkspaceService>(IWorkspaceService);
6161

6262
// Currently only a single root. Multi-root support is future.
@@ -76,6 +76,8 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
7676
}
7777
}
7878
));
79+
80+
(this.configuration.getSettings() as PythonSettings).addListener('change', this.onSettingsChanged);
7981
}
8082

8183
public async activate(): Promise<boolean> {
@@ -84,7 +86,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
8486
if (!clientOptions) {
8587
return false;
8688
}
87-
this.disposables.push(this.interpreterService.onDidChangeInterpreter(() => this.restartLanguageServer()));
8889
return this.startLanguageServer(clientOptions);
8990
}
9091

@@ -96,19 +97,7 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
9697
for (const d of this.disposables) {
9798
d.dispose();
9899
}
99-
}
100-
101-
private async restartLanguageServer(): Promise<void> {
102-
if (!this.context) {
103-
return;
104-
}
105-
const ids = new InterpreterDataService(this.context, this.services);
106-
const idata = await ids.getInterpreterData();
107-
if (!idata || idata.hash !== this.interpreterHash) {
108-
this.interpreterHash = idata ? idata.hash : '';
109-
await this.deactivate();
110-
await this.activate();
111-
}
100+
(this.configuration.getSettings() as PythonSettings).removeListener('change', this.onSettingsChanged);
112101
}
113102

114103
private async startLanguageServer(clientOptions: LanguageClientOptions): Promise<boolean> {
@@ -204,34 +193,27 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
204193
properties['PrefixPath'] = interpreterData.prefix;
205194
}
206195

207-
let searchPathsString = interpreterData ? interpreterData.searchPaths : '';
208-
let typeshedPaths: string[] = [];
196+
// tslint:disable-next-line:no-string-literal
197+
properties['DatabasePath'] = path.join(this.context.extensionPath, analysisEngineFolder);
209198

199+
let searchPaths = interpreterData ? interpreterData.searchPaths.split(path.delimiter) : [];
210200
const settings = this.configuration.getSettings();
211201
if (settings.autoComplete) {
212202
const extraPaths = settings.autoComplete.extraPaths;
213203
if (extraPaths && extraPaths.length > 0) {
214-
searchPathsString = `${searchPathsString};${extraPaths.join(';')}`;
204+
searchPaths.push(...extraPaths);
215205
}
216-
typeshedPaths = settings.autoComplete.typeshedPaths;
217206
}
218207

219-
// tslint:disable-next-line:no-string-literal
220-
properties['DatabasePath'] = path.join(this.context.extensionPath, analysisEngineFolder);
221-
222208
// Make sure paths do not contain multiple slashes so file URIs
223209
// in VS Code (Node.js) and in the language server (.NET) match.
224210
// Note: for the language server paths separator is always ;
225-
const searchPaths = searchPathsString.split(path.delimiter).map(p => path.normalize(p));
226-
// tslint:disable-next-line:no-string-literal
227-
properties['SearchPaths'] = `${searchPaths.join(';')};${pythonPath}`;
228-
229-
if (!typeshedPaths || typeshedPaths.length === 0) {
230-
typeshedPaths = [path.join(this.context.extensionPath, 'typeshed')];
231-
}
211+
searchPaths.push(pythonPath);
212+
searchPaths = searchPaths.map(p => path.normalize(p));
232213

233214
const selector = [{ language: PYTHON, scheme: 'file' }];
234-
const excludeFiles = this.getExcludedFiles();
215+
this.excludedFiles = this.getExcludedFiles();
216+
this.typeshedPaths = this.getTypeshedPaths(settings);
235217

236218
// Options to control the language client
237219
return {
@@ -253,9 +235,8 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
253235
maxDocumentationTextLength: 0
254236
},
255237
searchPaths,
256-
typeStubSearchPaths: typeshedPaths,
257-
asyncStartup: true,
258-
excludeFiles: excludeFiles,
238+
typeStubSearchPaths: this.typeshedPaths,
239+
excludeFiles: this.excludedFiles,
259240
testEnvironment: isTestExecution()
260241
}
261242
};
@@ -289,4 +270,49 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
289270
.forEach(p => list.push(p));
290271
}
291272
}
273+
274+
private getTypeshedPaths(settings: IPythonSettings): string[] {
275+
return settings.analysis.typeshedPaths && settings.analysis.typeshedPaths.length > 0
276+
? settings.analysis.typeshedPaths
277+
: [path.join(this.context.extensionPath, 'typeshed')];
278+
}
279+
280+
private async onSettingsChanged(): Promise<void> {
281+
const ids = new InterpreterDataService(this.context, this.services);
282+
const idata = await ids.getInterpreterData();
283+
if (!idata || idata.hash !== this.interpreterHash) {
284+
this.interpreterHash = idata ? idata.hash : '';
285+
await this.restartLanguageServer();
286+
return;
287+
}
288+
289+
const excludedFiles = this.getExcludedFiles();
290+
await this.restartLanguageServerIfArrayChanged(this.excludedFiles, excludedFiles);
291+
292+
const settings = this.configuration.getSettings();
293+
const typeshedPaths = this.getTypeshedPaths(settings);
294+
await this.restartLanguageServerIfArrayChanged(this.typeshedPaths, typeshedPaths);
295+
}
296+
297+
private async restartLanguageServerIfArrayChanged(oldArray: string[], newArray: string[]): Promise<void> {
298+
if (newArray.length !== oldArray.length) {
299+
await this.restartLanguageServer();
300+
return;
301+
}
302+
303+
for (let i = 0; i < oldArray.length; i += 1) {
304+
if (oldArray[i] !== newArray[i]) {
305+
await this.restartLanguageServer();
306+
return;
307+
}
308+
}
309+
}
310+
311+
private async restartLanguageServer(): Promise<void> {
312+
if (!this.context) {
313+
return;
314+
}
315+
await this.deactivate();
316+
await this.activate();
317+
}
292318
}

src/client/common/configSettings.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { sendTelemetryEvent } from '../telemetry';
88
import { COMPLETION_ADD_BRACKETS, FORMAT_ON_TYPE } from '../telemetry/constants';
99
import { isTestExecution } from './constants';
1010
import {
11+
IAnalysisSettings,
1112
IAutoCompleteSettings,
1213
IFormattingSettings,
1314
ILintingSettings,
@@ -44,6 +45,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
4445
public workspaceSymbols!: IWorkspaceSymbolSettings;
4546
public disableInstallationChecks = false;
4647
public globalModuleInstallation = false;
48+
public analysis!: IAnalysisSettings;
4749

4850
private workspaceRoot: Uri;
4951
private disposables: Disposable[] = [];
@@ -147,6 +149,14 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
147149
this.linting = lintingSettings;
148150
}
149151

152+
// tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion
153+
const analysisSettings = systemVariables.resolveAny(pythonSettings.get<IAnalysisSettings>('analysis'))!;
154+
if (this.analysis) {
155+
Object.assign<IAnalysisSettings, IAnalysisSettings>(this.analysis, analysisSettings);
156+
} else {
157+
this.analysis = analysisSettings;
158+
}
159+
150160
this.disableInstallationChecks = pythonSettings.get<boolean>('disableInstallationCheck') === true;
151161
this.globalModuleInstallation = pythonSettings.get<boolean>('globalModuleInstallation') === true;
152162

src/client/common/types.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export interface IPythonSettings {
133133
readonly envFile: string;
134134
readonly disableInstallationChecks: boolean;
135135
readonly globalModuleInstallation: boolean;
136+
readonly analysis: IAnalysisSettings;
136137
}
137138
export interface ISortImportSettings {
138139
readonly path: string;
@@ -236,13 +237,16 @@ export interface ITerminalSettings {
236237
readonly launchArgs: string[];
237238
readonly activateEnvironment: boolean;
238239
}
239-
export interface IPythonAnalysisEngineSettings {
240-
readonly showAdvancedMembers: boolean;
240+
export interface IAnalysisSettings {
241+
readonly openFilesOnly: boolean;
241242
readonly typeshedPaths: string[];
243+
readonly errors: string[];
244+
readonly warnings: string[];
245+
readonly information: string[];
246+
readonly disabled: string[];
242247
}
243248

244249
export const IConfigurationService = Symbol('IConfigurationService');
245-
246250
export interface IConfigurationService {
247251
getSettings(resource?: Uri): IPythonSettings;
248252
isTestExecution(): boolean;

0 commit comments

Comments
 (0)