Skip to content

LS settings for typeshed and diagnostics #2065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,13 @@ scripts/**
src/**
test/**
tmp/**
typeshed/tests/**
typeshed/.flake8
typeshed/.git
typeshed/.gitignore
typeshed/.travis.yml
typeshed/CONTRIBUTING.md
typeshed/README.md
typeshed/*.txt
typings/**
vsc-extension-quickstart.md
51 changes: 51 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,57 @@
"description": "Path to directory containing the Jedi library (this path will contain the 'Jedi' sub directory).",
"scope": "resource"
},
"python.analysis.openFilesOnly": {
"type": "boolean",
"default": false,
"description": "Only show errors and warnings for open files rather than for the entire workspace.",
"scope": "resource"
},
"python.analysis.typeshedPaths": {
"type": "array",
"default": [],
"items": {
"type": "string"
},
"description": "Paths to look for typeshed modules.",
"scope": "resource"
},
"python.analysis.errors": {
"type": "array",
"default": [],
"items": {
"type": "string"
},
"description": "List of diagnostics messages to be shown as errors.",
"scope": "resource"
},
"python.analysis.warnings": {
"type": "array",
"default": [],
"items": {
"type": "string"
},
"description": "List of diagnostics messages to be shown as warnings.",
"scope": "resource"
},
"python.analysis.information": {
"type": "array",
"default": [],
"items": {
"type": "string"
},
"description": "List of diagnostics messages to be shown as information.",
"scope": "resource"
},
"python.analysis.disabled": {
"type": "array",
"default": [],
"items": {
"type": "string"
},
"description": "List of suppressed diagnostic messages.",
"scope": "resource"
},
"python.linting.enabled": {
"type": "boolean",
"default": true,
Expand Down
98 changes: 62 additions & 36 deletions src/client/activation/analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import * as path from 'path';
import { OutputChannel, Uri } from 'vscode';
import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient';
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../common/application/types';
import { PythonSettings } from '../common/configSettings';
import { isTestExecution, STANDARD_OUTPUT_CHANNEL } from '../common/constants';
import { createDeferred, Deferred } from '../common/helpers';
import { IFileSystem, IPlatformService } from '../common/platform/types';
import { StopWatch } from '../common/stopWatch';
import { IConfigurationService, IExtensionContext, IOutputChannel } from '../common/types';
import { IInterpreterService } from '../interpreter/contracts';
import { IConfigurationService, IExtensionContext, IOutputChannel, IPythonSettings } from '../common/types';
import { IServiceContainer } from '../ioc/types';
import {
PYTHON_ANALYSIS_ENGINE_DOWNLOADED,
Expand All @@ -38,7 +38,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
private readonly fs: IFileSystem;
private readonly sw = new StopWatch();
private readonly platformData: PlatformData;
private readonly interpreterService: IInterpreterService;
private readonly startupCompleted: Deferred<void>;
private readonly disposables: Disposable[] = [];
private readonly context: IExtensionContext;
Expand All @@ -47,6 +46,8 @@ export class AnalysisExtensionActivator implements IExtensionActivator {

private languageClient: LanguageClient | undefined;
private interpreterHash: string = '';
private excludedFiles: string[] = [];
private typeshedPaths: string[] = [];
private loadExtensionArgs: {} | undefined;

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

// Currently only a single root. Multi-root support is future.
Expand All @@ -76,6 +76,8 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
}
}
));

(this.configuration.getSettings() as PythonSettings).addListener('change', this.onSettingsChanged);
}

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

Expand All @@ -96,19 +97,7 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
for (const d of this.disposables) {
d.dispose();
}
}

private async restartLanguageServer(): Promise<void> {
if (!this.context) {
return;
}
const ids = new InterpreterDataService(this.context, this.services);
const idata = await ids.getInterpreterData();
if (!idata || idata.hash !== this.interpreterHash) {
this.interpreterHash = idata ? idata.hash : '';
await this.deactivate();
await this.activate();
}
(this.configuration.getSettings() as PythonSettings).removeListener('change', this.onSettingsChanged);
}

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

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

let searchPaths = interpreterData ? interpreterData.searchPaths.split(path.delimiter) : [];
const settings = this.configuration.getSettings();
if (settings.autoComplete) {
const extraPaths = settings.autoComplete.extraPaths;
if (extraPaths && extraPaths.length > 0) {
searchPathsString = `${searchPathsString};${extraPaths.join(';')}`;
searchPaths.push(...extraPaths);
}
typeshedPaths = settings.autoComplete.typeshedPaths;
}

// tslint:disable-next-line:no-string-literal
properties['DatabasePath'] = path.join(this.context.extensionPath, analysisEngineFolder);

// Make sure paths do not contain multiple slashes so file URIs
// in VS Code (Node.js) and in the language server (.NET) match.
// Note: for the language server paths separator is always ;
const searchPaths = searchPathsString.split(path.delimiter).map(p => path.normalize(p));
// tslint:disable-next-line:no-string-literal
properties['SearchPaths'] = `${searchPaths.join(';')};${pythonPath}`;

if (!typeshedPaths || typeshedPaths.length === 0) {
typeshedPaths = [path.join(this.context.extensionPath, 'typeshed')];
}
searchPaths.push(pythonPath);
searchPaths = searchPaths.map(p => path.normalize(p));

const selector = [{ language: PYTHON, scheme: 'file' }];
const excludeFiles = this.getExcludedFiles();
this.excludedFiles = this.getExcludedFiles();
this.typeshedPaths = this.getTypeshedPaths(settings);

// Options to control the language client
return {
Expand All @@ -253,9 +235,8 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
maxDocumentationTextLength: 0
},
searchPaths,
typeStubSearchPaths: typeshedPaths,
asyncStartup: true,
excludeFiles: excludeFiles,
typeStubSearchPaths: this.typeshedPaths,
excludeFiles: this.excludedFiles,
testEnvironment: isTestExecution()
}
};
Expand Down Expand Up @@ -289,4 +270,49 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
.forEach(p => list.push(p));
}
}

private getTypeshedPaths(settings: IPythonSettings): string[] {
return settings.analysis.typeshedPaths && settings.analysis.typeshedPaths.length > 0
? settings.analysis.typeshedPaths
: [path.join(this.context.extensionPath, 'typeshed')];
}

private async onSettingsChanged(): Promise<void> {
const ids = new InterpreterDataService(this.context, this.services);
const idata = await ids.getInterpreterData();
if (!idata || idata.hash !== this.interpreterHash) {
this.interpreterHash = idata ? idata.hash : '';
await this.restartLanguageServer();
return;
}

const excludedFiles = this.getExcludedFiles();
await this.restartLanguageServerIfArrayChanged(this.excludedFiles, excludedFiles);

const settings = this.configuration.getSettings();
const typeshedPaths = this.getTypeshedPaths(settings);
await this.restartLanguageServerIfArrayChanged(this.typeshedPaths, typeshedPaths);
}

private async restartLanguageServerIfArrayChanged(oldArray: string[], newArray: string[]): Promise<void> {
if (newArray.length !== oldArray.length) {
await this.restartLanguageServer();
return;
}

for (let i = 0; i < oldArray.length; i += 1) {
if (oldArray[i] !== newArray[i]) {
await this.restartLanguageServer();
return;
}
}
}

private async restartLanguageServer(): Promise<void> {
if (!this.context) {
return;
}
await this.deactivate();
await this.activate();
}
}
10 changes: 10 additions & 0 deletions src/client/common/configSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { sendTelemetryEvent } from '../telemetry';
import { COMPLETION_ADD_BRACKETS, FORMAT_ON_TYPE } from '../telemetry/constants';
import { isTestExecution } from './constants';
import {
IAnalysisSettings,
IAutoCompleteSettings,
IFormattingSettings,
ILintingSettings,
Expand Down Expand Up @@ -44,6 +45,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
public workspaceSymbols!: IWorkspaceSymbolSettings;
public disableInstallationChecks = false;
public globalModuleInstallation = false;
public analysis!: IAnalysisSettings;

private workspaceRoot: Uri;
private disposables: Disposable[] = [];
Expand Down Expand Up @@ -147,6 +149,14 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
this.linting = lintingSettings;
}

// tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion
const analysisSettings = systemVariables.resolveAny(pythonSettings.get<IAnalysisSettings>('analysis'))!;
if (this.analysis) {
Object.assign<IAnalysisSettings, IAnalysisSettings>(this.analysis, analysisSettings);
} else {
this.analysis = analysisSettings;
}

this.disableInstallationChecks = pythonSettings.get<boolean>('disableInstallationCheck') === true;
this.globalModuleInstallation = pythonSettings.get<boolean>('globalModuleInstallation') === true;

Expand Down
10 changes: 7 additions & 3 deletions src/client/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export interface IPythonSettings {
readonly envFile: string;
readonly disableInstallationChecks: boolean;
readonly globalModuleInstallation: boolean;
readonly analysis: IAnalysisSettings;
}
export interface ISortImportSettings {
readonly path: string;
Expand Down Expand Up @@ -236,13 +237,16 @@ export interface ITerminalSettings {
readonly launchArgs: string[];
readonly activateEnvironment: boolean;
}
export interface IPythonAnalysisEngineSettings {
readonly showAdvancedMembers: boolean;
export interface IAnalysisSettings {
readonly openFilesOnly: boolean;
readonly typeshedPaths: string[];
readonly errors: string[];
readonly warnings: string[];
readonly information: string[];
readonly disabled: string[];
}

export const IConfigurationService = Symbol('IConfigurationService');

export interface IConfigurationService {
getSettings(resource?: Uri): IPythonSettings;
isTestExecution(): boolean;
Expand Down