Skip to content

Commit 4a8319e

Browse files
committed
improve linter performance, kill older linters
1 parent 4fb8e3d commit 4a8319e

11 files changed

Lines changed: 48 additions & 39 deletions

File tree

src/client/common/utils.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export function execPythonFile(file: string, args: string[], cwd: string, includ
101101
if (stdOut) {
102102
return spawnFileInternal(file, args, { cwd }, includeErrorAsResponse, stdOut, token);
103103
}
104-
return execFileInternal(file, args, { cwd: cwd }, includeErrorAsResponse);
104+
return execFileInternal(file, args, { cwd: cwd }, includeErrorAsResponse, token);
105105
}
106106

107107
return getPythonInterpreterDirectory().then(pyPath => {
@@ -110,7 +110,7 @@ export function execPythonFile(file: string, args: string[], cwd: string, includ
110110
if (stdOut) {
111111
return spawnFileInternal(file, args, { cwd }, includeErrorAsResponse, stdOut, token);
112112
}
113-
return execFileInternal(file, args, { cwd: cwd }, includeErrorAsResponse);
113+
return execFileInternal(file, args, { cwd: cwd }, includeErrorAsResponse, token);
114114
}
115115

116116
if (customEnvVariables === null) {
@@ -134,7 +134,7 @@ export function execPythonFile(file: string, args: string[], cwd: string, includ
134134
if (stdOut) {
135135
return spawnFileInternal(file, args, { cwd, env: customEnvVariables }, includeErrorAsResponse, stdOut, token);
136136
}
137-
return execFileInternal(file, args, { cwd, env: customEnvVariables }, includeErrorAsResponse);
137+
return execFileInternal(file, args, { cwd, env: customEnvVariables }, includeErrorAsResponse, token);
138138
});
139139
}
140140

@@ -160,11 +160,19 @@ function handleResponse(file: string, includeErrorAsResponse: boolean, error: Er
160160
resolve(stdout + '');
161161
});
162162
}
163-
function execFileInternal(file: string, args: string[], options: child_process.ExecFileOptions, includeErrorAsResponse: boolean): Promise<string> {
163+
function execFileInternal(file: string, args: string[], options: child_process.ExecFileOptions, includeErrorAsResponse: boolean, token?: CancellationToken): Promise<string> {
164164
return new Promise<string>((resolve, reject) => {
165-
child_process.execFile(file, args, options, (error, stdout, stderr) => {
165+
let proc = child_process.execFile(file, args, options, (error, stdout, stderr) => {
166166
handleResponse(file, includeErrorAsResponse, error, stdout, stderr).then(resolve, reject);
167167
});
168+
if (token && token.onCancellationRequested) {
169+
token.onCancellationRequested(() => {
170+
if (proc) {
171+
proc.kill();
172+
proc = null;
173+
}
174+
});
175+
}
168176
});
169177
}
170178
function spawnFileInternal(file: string, args: string[], options: child_process.ExecFileOptions, includeErrorAsResponse: boolean, stdOut: (line: string) => void, token?: CancellationToken): Promise<string> {

src/client/linters/baseLinter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ export abstract class BaseLinter {
6363
this.pythonSettings = settings.PythonSettings.getInstance();
6464
}
6565
public abstract isEnabled(): Boolean;
66-
public abstract runLinter(document: vscode.TextDocument): Promise<ILintMessage[]>;
66+
public abstract runLinter(document: vscode.TextDocument, cancellation: vscode.CancellationToken): Promise<ILintMessage[]>;
6767

68-
protected run(command: string, args: string[], document: vscode.TextDocument, cwd: string, regEx: string = REGEX): Promise<ILintMessage[]> {
68+
protected run(command: string, args: string[], document: vscode.TextDocument, cwd: string, cancellation: vscode.CancellationToken, regEx: string = REGEX): Promise<ILintMessage[]> {
6969
let outputChannel = this.outputChannel;
7070

7171
return new Promise<ILintMessage[]>((resolve, reject) => {
72-
execPythonFile(command, args, cwd, true).then(data => {
72+
execPythonFile(command, args, cwd, true, null, cancellation).then(data => {
7373
outputChannel.append('#'.repeat(10) + 'Linting Output - ' + this.Id + '#'.repeat(10) + '\n');
7474
outputChannel.append(data);
7575
let outputLines = data.split(/\r?\n/g);

src/client/linters/flake8.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import * as baseLinter from './baseLinter';
44
import { OutputChannel } from 'vscode';
55
import { Product } from '../common/installer';
6-
import { TextDocument } from 'vscode';
6+
import { TextDocument, CancellationToken } from 'vscode';
77

88
export class Linter extends baseLinter.BaseLinter {
99
constructor(outputChannel: OutputChannel, workspaceRootPath?: string) {
@@ -27,15 +27,15 @@ export class Linter extends baseLinter.BaseLinter {
2727
public isEnabled(): Boolean {
2828
return this.pythonSettings.linting.flake8Enabled;
2929
}
30-
public runLinter(document: TextDocument): Promise<baseLinter.ILintMessage[]> {
30+
public runLinter(document: TextDocument, cancellation: CancellationToken): Promise<baseLinter.ILintMessage[]> {
3131
if (!this.pythonSettings.linting.flake8Enabled) {
3232
return Promise.resolve([]);
3333
}
3434

3535
let flake8Path = this.pythonSettings.linting.flake8Path;
3636
let flake8Args = Array.isArray(this.pythonSettings.linting.flake8Args) ? this.pythonSettings.linting.flake8Args : [];
3737
return new Promise<baseLinter.ILintMessage[]>((resolve, reject) => {
38-
this.run(flake8Path, flake8Args.concat(['--format=%(row)d,%(col)d,%(code)s,%(code)s:%(text)s', document.uri.fsPath]), document, this.workspaceRootPath).then(messages => {
38+
this.run(flake8Path, flake8Args.concat(['--format=%(row)d,%(col)d,%(code)s,%(code)s:%(text)s', document.uri.fsPath]), document, this.workspaceRootPath, cancellation).then(messages => {
3939
messages.forEach(msg => {
4040
msg.severity = this.parseMessagesCodeSeverity(msg.type);
4141
});

src/client/linters/mypy.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import * as baseLinter from './baseLinter';
44
import { OutputChannel } from 'vscode';
55
import { Product } from '../common/installer';
6-
import { TextDocument } from 'vscode';
6+
import { TextDocument, CancellationToken } from 'vscode';
77

88
const REGEX = '(?<file>.py):(?<line>\\d+): (?<type>\\w+): (?<message>.*)\\r?(\\n|$)';
99

@@ -28,15 +28,15 @@ export class Linter extends baseLinter.BaseLinter {
2828
public isEnabled(): Boolean {
2929
return this.pythonSettings.linting.mypyEnabled;
3030
}
31-
public runLinter(document: TextDocument): Promise<baseLinter.ILintMessage[]> {
31+
public runLinter(document: TextDocument, cancellation: CancellationToken): Promise<baseLinter.ILintMessage[]> {
3232
if (!this.pythonSettings.linting.mypyEnabled) {
3333
return Promise.resolve([]);
3434
}
3535

3636
let mypyPath = this.pythonSettings.linting.mypyPath;
3737
let mypyArgs = Array.isArray(this.pythonSettings.linting.mypyArgs) ? this.pythonSettings.linting.mypyArgs : [];
3838
return new Promise<baseLinter.ILintMessage[]>((resolve, reject) => {
39-
this.run(mypyPath, mypyArgs.concat([document.uri.fsPath]), document, this.workspaceRootPath, REGEX).then(messages => {
39+
this.run(mypyPath, mypyArgs.concat([document.uri.fsPath]), document, this.workspaceRootPath, cancellation, REGEX).then(messages => {
4040
messages.forEach(msg => {
4141
msg.severity = this.parseMessagesSeverity(msg.type);
4242
msg.code = msg.type;

src/client/linters/pep8Linter.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22

33
import * as baseLinter from './baseLinter';
4-
import {OutputChannel} from 'vscode';
4+
import { OutputChannel } from 'vscode';
55
import { Product } from '../common/installer';
6-
import { TextDocument } from 'vscode';
6+
import { TextDocument, CancellationToken } from 'vscode';
77

88
export class Linter extends baseLinter.BaseLinter {
99
constructor(outputChannel: OutputChannel, workspaceRootPath?: string) {
@@ -26,15 +26,15 @@ export class Linter extends baseLinter.BaseLinter {
2626
public isEnabled(): Boolean {
2727
return this.pythonSettings.linting.pep8Enabled;
2828
}
29-
public runLinter(document:TextDocument): Promise<baseLinter.ILintMessage[]> {
29+
public runLinter(document: TextDocument, cancellation: CancellationToken): Promise<baseLinter.ILintMessage[]> {
3030
if (!this.pythonSettings.linting.pep8Enabled) {
3131
return Promise.resolve([]);
3232
}
3333

3434
let pep8Path = this.pythonSettings.linting.pep8Path;
3535
let pep8Args = Array.isArray(this.pythonSettings.linting.pep8Args) ? this.pythonSettings.linting.pep8Args : [];
3636
return new Promise<baseLinter.ILintMessage[]>(resolve => {
37-
this.run(pep8Path, pep8Args.concat(['--format=%(row)d,%(col)d,%(code)s,%(code)s:%(text)s', document.uri.fsPath]), document, this.workspaceRootPath).then(messages => {
37+
this.run(pep8Path, pep8Args.concat(['--format=%(row)d,%(col)d,%(code)s,%(code)s:%(text)s', document.uri.fsPath]), document, this.workspaceRootPath, cancellation).then(messages => {
3838
messages.forEach(msg => {
3939
msg.severity = this.parseMessagesCodeSeverity(msg.type);
4040
});

src/client/linters/prospector.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
'use strict';
22

33
import * as baseLinter from './baseLinter';
4-
import {OutputChannel} from 'vscode';
5-
import {execPythonFile} from './../common/utils';
4+
import { OutputChannel } from 'vscode';
5+
import { execPythonFile } from './../common/utils';
66
import { Product } from '../common/installer';
7-
import { TextDocument } from 'vscode';
7+
import { TextDocument, CancellationToken } from 'vscode';
88

99
interface IProspectorResponse {
1010
messages: IProspectorMessage[];
@@ -31,7 +31,7 @@ export class Linter extends baseLinter.BaseLinter {
3131
public isEnabled(): Boolean {
3232
return this.pythonSettings.linting.prospectorEnabled;
3333
}
34-
public runLinter(document:TextDocument): Promise<baseLinter.ILintMessage[]> {
34+
public runLinter(document: TextDocument, cancellation: CancellationToken): Promise<baseLinter.ILintMessage[]> {
3535
if (!this.pythonSettings.linting.prospectorEnabled) {
3636
return Promise.resolve([]);
3737
}
@@ -40,7 +40,7 @@ export class Linter extends baseLinter.BaseLinter {
4040
let outputChannel = this.outputChannel;
4141
let prospectorArgs = Array.isArray(this.pythonSettings.linting.prospectorArgs) ? this.pythonSettings.linting.prospectorArgs : [];
4242
return new Promise<baseLinter.ILintMessage[]>((resolve, reject) => {
43-
execPythonFile(prospectorPath, prospectorArgs.concat(['--absolute-paths', '--output-format=json', document.uri.fsPath]), this.workspaceRootPath, false).then(data => {
43+
execPythonFile(prospectorPath, prospectorArgs.concat(['--absolute-paths', '--output-format=json', document.uri.fsPath]), this.workspaceRootPath, false, null, cancellation).then(data => {
4444
let parsedData: IProspectorResponse;
4545
try {
4646
parsedData = JSON.parse(data);
@@ -53,8 +53,8 @@ export class Linter extends baseLinter.BaseLinter {
5353
let diagnostics: baseLinter.ILintMessage[] = [];
5454
parsedData.messages.filter((value, index) => index <= this.pythonSettings.linting.maxNumberOfProblems).forEach(msg => {
5555

56-
let lineNumber = msg.location.line === null || isNaN(msg.location.line) ? 1 : msg.location.line;
57-
let sourceLine = document.lineAt(lineNumber - 1).text;
56+
let lineNumber = msg.location.line === null || isNaN(msg.location.line) ? 1 : msg.location.line;
57+
let sourceLine = document.lineAt(lineNumber - 1).text;
5858
let sourceStart = sourceLine.substring(msg.location.character);
5959

6060
// try to get the first word from the starting position

src/client/linters/pydocstyle.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ILintMessage } from './baseLinter';
66
import { OutputChannel } from 'vscode';
77
import { execPythonFile, IS_WINDOWS } from './../common/utils';
88
import { Product } from '../common/installer';
9-
import { TextDocument } from 'vscode';
9+
import { TextDocument, CancellationToken } from 'vscode';
1010

1111
export class Linter extends baseLinter.BaseLinter {
1212
constructor(outputChannel: OutputChannel, workspaceRootPath?: string) {
@@ -16,15 +16,15 @@ export class Linter extends baseLinter.BaseLinter {
1616
public isEnabled(): Boolean {
1717
return this.pythonSettings.linting.pydocstyleEnabled;
1818
}
19-
public runLinter(document:TextDocument): Promise<baseLinter.ILintMessage[]> {
19+
public runLinter(document: TextDocument, cancellation: CancellationToken): Promise<baseLinter.ILintMessage[]> {
2020
if (!this.pythonSettings.linting.pydocstyleEnabled) {
2121
return Promise.resolve([]);
2222
}
2323

2424
let pydocstylePath = this.pythonSettings.linting.pydocstylePath;
2525
let pydocstyleArgs = Array.isArray(this.pythonSettings.linting.pydocstyleArgs) ? this.pythonSettings.linting.pydocstyleArgs : [];
2626
return new Promise<baseLinter.ILintMessage[]>(resolve => {
27-
this.run(pydocstylePath, pydocstyleArgs.concat([document.uri.fsPath]), document).then(messages => {
27+
this.run(pydocstylePath, pydocstyleArgs.concat([document.uri.fsPath]), document, null, cancellation).then(messages => {
2828
// All messages in pep8 are treated as warnings for now
2929
messages.forEach(msg => {
3030
msg.severity = baseLinter.LintMessageSeverity.Information;
@@ -35,11 +35,11 @@ export class Linter extends baseLinter.BaseLinter {
3535
});
3636
}
3737

38-
protected run(commandLine: string, args: string[], document:TextDocument): Promise<ILintMessage[]> {
38+
protected run(commandLine: string, args: string[], document: TextDocument, cwd: any, cancellation: CancellationToken): Promise<ILintMessage[]> {
3939
let outputChannel = this.outputChannel;
4040

4141
return new Promise<ILintMessage[]>((resolve, reject) => {
42-
execPythonFile(commandLine, args, this.workspaceRootPath, true).then(data => {
42+
execPythonFile(commandLine, args, this.workspaceRootPath, true, null, cancellation).then(data => {
4343
outputChannel.append('#'.repeat(10) + 'Linting Output - ' + this.Id + '#'.repeat(10) + '\n');
4444
outputChannel.append(data);
4545
let outputLines = data.split(/\r?\n/g);

src/client/linters/pylama.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22

33
import * as baseLinter from './baseLinter';
4-
import {OutputChannel} from 'vscode';
4+
import { OutputChannel } from 'vscode';
55
import { Product } from '../common/installer';
6-
import { TextDocument } from 'vscode';
6+
import { TextDocument, CancellationToken } from 'vscode';
77

88
const REGEX = '(?<file>.py):(?<line>\\d+):(?<column>\\d+): \\[(?<type>\\w+)\\] (?<code>\\w\\d+):? (?<message>.*)\\r?(\\n|$)';
99

@@ -15,15 +15,15 @@ export class Linter extends baseLinter.BaseLinter {
1515
public isEnabled(): Boolean {
1616
return this.pythonSettings.linting.pylamaEnabled;
1717
}
18-
public runLinter(document:TextDocument): Promise<baseLinter.ILintMessage[]> {
18+
public runLinter(document: TextDocument, cancellation: CancellationToken): Promise<baseLinter.ILintMessage[]> {
1919
if (!this.pythonSettings.linting.pylamaEnabled) {
2020
return Promise.resolve([]);
2121
}
2222

2323
let pylamaPath = this.pythonSettings.linting.pylamaPath;
2424
let pylamaArgs = Array.isArray(this.pythonSettings.linting.pylamaArgs) ? this.pythonSettings.linting.pylamaArgs : [];
2525
return new Promise<baseLinter.ILintMessage[]>(resolve => {
26-
this.run(pylamaPath, pylamaArgs.concat(['--format=parsable', document.uri.fsPath]), document, this.workspaceRootPath, REGEX).then(messages => {
26+
this.run(pylamaPath, pylamaArgs.concat(['--format=parsable', document.uri.fsPath]), document, this.workspaceRootPath, cancellation, REGEX).then(messages => {
2727
// All messages in pylama are treated as warnings for now
2828
messages.forEach(msg => {
2929
msg.severity = baseLinter.LintMessageSeverity.Information;

src/client/linters/pylint.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import * as baseLinter from './baseLinter';
44
import { OutputChannel } from 'vscode';
55
import { Product } from '../common/installer';
6-
import { TextDocument } from 'vscode';
6+
import { TextDocument, CancellationToken } from 'vscode';
77

88
export class Linter extends baseLinter.BaseLinter {
99
constructor(outputChannel: OutputChannel, workspaceRootPath?: string) {
@@ -36,15 +36,15 @@ export class Linter extends baseLinter.BaseLinter {
3636
public isEnabled(): Boolean {
3737
return this.pythonSettings.linting.pylintEnabled;
3838
}
39-
public runLinter(document: TextDocument): Promise<baseLinter.ILintMessage[]> {
39+
public runLinter(document: TextDocument, cancellation: CancellationToken): Promise<baseLinter.ILintMessage[]> {
4040
if (!this.pythonSettings.linting.pylintEnabled) {
4141
return Promise.resolve([]);
4242
}
4343

4444
let pylintPath = this.pythonSettings.linting.pylintPath;
4545
let pylintArgs = Array.isArray(this.pythonSettings.linting.pylintArgs) ? this.pythonSettings.linting.pylintArgs : [];
4646
return new Promise<baseLinter.ILintMessage[]>((resolve, reject) => {
47-
this.run(pylintPath, pylintArgs.concat(['--msg-template=\'{line},{column},{category},{msg_id}:{msg}\'', '--reports=n', '--output-format=text', document.uri.fsPath]), document, this.workspaceRootPath).then(messages => {
47+
this.run(pylintPath, pylintArgs.concat(['--msg-template=\'{line},{column},{category},{msg_id}:{msg}\'', '--reports=n', '--output-format=text', document.uri.fsPath]), document, this.workspaceRootPath, cancellation).then(messages => {
4848
messages.forEach(msg => {
4949
msg.severity = this.parseMessagesSeverity(msg.type);
5050
});

src/client/providers/lintProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export class LintProvider extends vscode.Disposable {
162162
}
163163
// turn off telemetry for linters (at least for now)
164164
//let delays = new telemetryHelper.Delays();
165-
return linter.runLinter(document).then(results => {
165+
return linter.runLinter(document, cancelToken.token).then(results => {
166166
//delays.stop();
167167
//telemetryHelper.sendTelemetryEvent(telemetryContracts.IDE.Lint, { Lint_Provider: linter.Id }, delays.toMeasures());
168168
return results;

0 commit comments

Comments
 (0)