vscode-eslint 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vscode-eslint +82 -0
- package/client/out/client.d.ts +13 -0
- package/client/out/extension.d.ts +3 -0
- package/client/out/extension.js +2 -0
- package/client/out/extension.js.map +1 -0
- package/client/out/node-utils.d.ts +51 -0
- package/client/out/settings.d.ts +41 -0
- package/client/out/shared/customMessages.d.ts +89 -0
- package/client/out/shared/settings.d.ts +106 -0
- package/client/out/tasks.d.ts +31 -0
- package/client/out/tests/glob.test.d.ts +1 -0
- package/client/out/vscode-utils.d.ts +5 -0
- package/package.json +9 -2
- package/server/out/diff.d.ts +134 -0
- package/server/out/eslint.d.ts +298 -0
- package/server/out/eslintServer.d.ts +1 -0
- package/server/out/eslintServer.js +2 -0
- package/server/out/eslintServer.js.map +1 -0
- package/server/out/is.d.ts +3 -0
- package/server/out/languageDefaults.d.ts +6 -0
- package/server/out/linkedMap.d.ts +53 -0
- package/server/out/paths.d.ts +22 -0
- package/server/out/shared/customMessages.d.ts +89 -0
- package/server/out/shared/settings.d.ts +106 -0
- package/$shared/customMessages.ts +0 -113
- package/$shared/settings.ts +0 -189
- package/.CodeQL.yml +0 -5
- package/.azure-pipelines.yml +0 -27
- package/.gemini/agents/vscode-eslint.md +0 -23
- package/.github/commands.yml +0 -127
- package/.github/locker.yml +0 -6
- package/.github/needs_more_info.yml +0 -6
- package/.github/workflows/npm-publish.yml +0 -53
- package/.github/workflows/release-please.yml +0 -22
- package/.lsifrc.json +0 -4
- package/.vscode/launch.json +0 -20
- package/.vscode/settings.json +0 -52
- package/.vscode/spellright.dict +0 -8
- package/.vscode/tasks.json +0 -39
- package/.vscodeignore +0 -23
- package/CHANGELOG.md +0 -524
- package/SECURITY.md +0 -41
- package/agents.md +0 -36
- package/bin/vscode-eslint.js +0 -56
- package/build/azure-pipelines/linux/build.yml +0 -14
- package/build/azure-pipelines/pre-release.yml +0 -42
- package/build/azure-pipelines/release.yml +0 -45
- package/build/azure-pipelines/win32/build.yml +0 -14
- package/build/bin/all.js +0 -29
- package/build/bin/linking.js +0 -102
- package/build/bin/symlink.js +0 -35
- package/client/.mocharc.json +0 -6
- package/client/agents.md +0 -5
- package/client/package-lock.json +0 -176
- package/client/package.json +0 -29
- package/client/src/client.ts +0 -992
- package/client/src/extension.ts +0 -180
- package/client/src/node-utils.ts +0 -393
- package/client/src/settings.ts +0 -379
- package/client/src/tasks.ts +0 -186
- package/client/src/tests/glob.test.ts +0 -31
- package/client/src/vscode-utils.ts +0 -28
- package/client/test/mocha.opts +0 -3
- package/client/tsconfig.json +0 -20
- package/client/webpack.config.js +0 -25
- package/contributing.md +0 -19
- package/esbuild.js +0 -62
- package/eslint.config.js +0 -129
- package/history/settings_1_9_x.md +0 -110
- package/images/2_1_10/eslint-dialog.png +0 -0
- package/images/2_1_10/eslint-status.png +0 -0
- package/package-json-schema.json +0 -9
- package/playgrounds/7.0/.eslintignore +0 -1
- package/playgrounds/7.0/.eslintrc.json +0 -71
- package/playgrounds/7.0/.vscode/settings.json +0 -85
- package/playgrounds/7.0/app.js +0 -12
- package/playgrounds/7.0/build/.eslintignore +0 -1
- package/playgrounds/7.0/build/.eslintrc.json +0 -30
- package/playgrounds/7.0/build/build.js +0 -11
- package/playgrounds/7.0/jsconfig.json +0 -5
- package/playgrounds/7.0/package-lock.json +0 -2133
- package/playgrounds/7.0/package.json +0 -10
- package/playgrounds/7.0/readme.md +0 -0
- package/playgrounds/7.0/subDir/sub.js +0 -11
- package/playgrounds/7.0/subDir/test.jsx +0 -10
- package/playgrounds/7.0/test.js +0 -11
- package/playgrounds/7.0/test.sh +0 -1
- package/playgrounds/7.0/test.vue +0 -33
- package/playgrounds/7.0/test2.html +0 -8
- package/playgrounds/8.0/.eslintignore +0 -1
- package/playgrounds/8.0/.eslintrc.json +0 -71
- package/playgrounds/8.0/.vscode/settings.json +0 -91
- package/playgrounds/8.0/app.js +0 -12
- package/playgrounds/8.0/build/.eslintignore +0 -1
- package/playgrounds/8.0/build/.eslintrc.json +0 -30
- package/playgrounds/8.0/build/build.js +0 -11
- package/playgrounds/8.0/jsconfig.json +0 -5
- package/playgrounds/8.0/package-lock.json +0 -2321
- package/playgrounds/8.0/package.json +0 -10
- package/playgrounds/8.0/readme.md +0 -17
- package/playgrounds/8.0/subDir/sub.js +0 -11
- package/playgrounds/8.0/subDir/test.jsx +0 -10
- package/playgrounds/8.0/test.ipynb +0 -49
- package/playgrounds/8.0/test.js +0 -3
- package/playgrounds/8.0/test.sh +0 -1
- package/playgrounds/8.0/test.vue +0 -33
- package/playgrounds/8.0/test2.html +0 -8
- package/playgrounds/9.0/flat/.vscode/settings.json +0 -3
- package/playgrounds/9.0/flat/app.js +0 -12
- package/playgrounds/9.0/flat/dist/ignore.js +0 -12
- package/playgrounds/9.0/flat/eslint.config.js +0 -61
- package/playgrounds/9.0/flat/package-lock.json +0 -1053
- package/playgrounds/9.0/flat/package.json +0 -9
- package/playgrounds/9.0/rc/.eslintrc.json +0 -57
- package/playgrounds/9.0/rc/.vscode/settings.json +0 -3
- package/playgrounds/9.0/rc/app.js +0 -12
- package/playgrounds/9.0/rc/package-lock.json +0 -1345
- package/playgrounds/9.0/rc/package.json +0 -9
- package/playgrounds/flat-config/.vscode/settings.json +0 -22
- package/playgrounds/flat-config/app.js +0 -12
- package/playgrounds/flat-config/eslint.config.js +0 -51
- package/playgrounds/flat-config/package-lock.json +0 -2733
- package/playgrounds/flat-config/package.json +0 -12
- package/playgrounds/flat-config/sub/sub.js +0 -2
- package/playgrounds/flat-config/test.ts +0 -7
- package/playgrounds/flat-config/tsconfig.json +0 -11
- package/playgrounds/flat-config-fail/f1/app.js +0 -12
- package/playgrounds/flat-config-fail/f1/eslint.config.js +0 -51
- package/playgrounds/flat-config-fail/package-lock.json +0 -1683
- package/playgrounds/flat-config-fail/package.json +0 -11
- package/playgrounds/flat-config-mjs/.vscode/settings.json +0 -21
- package/playgrounds/flat-config-mjs/app.js +0 -12
- package/playgrounds/flat-config-mjs/eslint.config.mjs +0 -53
- package/playgrounds/flat-config-mjs/package-lock.json +0 -2860
- package/playgrounds/flat-config-mjs/package.json +0 -11
- package/playgrounds/flat-config-mjs/sub/sub.js +0 -2
- package/playgrounds/flat-config-mjs/test.ts +0 -7
- package/playgrounds/flat-config-mjs/tsconfig.json +0 -11
- package/playgrounds/load-eslint/.vscode/settings.json +0 -21
- package/playgrounds/load-eslint/app.js +0 -12
- package/playgrounds/load-eslint/eslint.config.js +0 -51
- package/playgrounds/load-eslint/package-lock.json +0 -2860
- package/playgrounds/load-eslint/package.json +0 -11
- package/playgrounds/load-eslint/sub/sub.js +0 -2
- package/playgrounds/load-eslint/test.ts +0 -7
- package/playgrounds/load-eslint/tsconfig.json +0 -11
- package/playgrounds/noLib/test.js +0 -22
- package/playgrounds/noWD/.vscode/settings.json +0 -2
- package/playgrounds/noWD/src/.eslintrc.json +0 -18
- package/playgrounds/noWD/src/package-lock.json +0 -2812
- package/playgrounds/noWD/src/package.json +0 -12
- package/playgrounds/noWD/src/test.js +0 -3
- package/playgrounds/notebooks/notebook.ipynb +0 -7072
- package/playgrounds/notebooks/notebook2.ipynb +0 -20
- package/playgrounds/testing.code-workspace +0 -28
- package/playgrounds/ts/.eslintrc.base.json +0 -23
- package/playgrounds/ts/.eslintrc.json +0 -191
- package/playgrounds/ts/.vscode/settings.json +0 -12
- package/playgrounds/ts/package-lock.json +0 -2687
- package/playgrounds/ts/package.json +0 -11
- package/playgrounds/ts/test copy.ts +0 -4
- package/playgrounds/ts/test.ipynb +0 -49
- package/playgrounds/ts/test.ts +0 -4
- package/playgrounds/ts/test.tsx +0 -14
- package/playgrounds/ts/tsconfig.json +0 -100
- package/server/agents.md +0 -9
- package/server/package-lock.json +0 -93
- package/server/package.json +0 -32
- package/server/src/diff.ts +0 -1079
- package/server/src/eslint.ts +0 -1471
- package/server/src/eslintServer.ts +0 -865
- package/server/src/is.ts +0 -18
- package/server/src/languageDefaults.ts +0 -40
- package/server/src/linkedMap.ts +0 -448
- package/server/src/paths.ts +0 -128
- package/server/src/thenable.d.ts +0 -5
- package/server/tsconfig.json +0 -21
- package/server/webpack.config.js +0 -25
- package/shared.webpack.config.js +0 -59
- package/tsconfig.base.json +0 -9
- package/tsconfig.json +0 -21
|
@@ -1,865 +0,0 @@
|
|
|
1
|
-
/* --------------------------------------------------------------------------------------------
|
|
2
|
-
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
-
* ------------------------------------------------------------------------------------------ */
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
import { EOL } from 'os';
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
createConnection, Diagnostic, Range, TextDocuments, TextDocumentSyncKind, TextEdit, Command, WorkspaceChange, VersionedTextDocumentIdentifier,
|
|
10
|
-
DidChangeConfigurationNotification, CodeAction, CodeActionKind, Position, TextDocumentEdit, Message as LMessage, ResponseMessage as LResponseMessage,
|
|
11
|
-
uinteger, ServerCapabilities, NotebookDocuments, ProposedFeatures, ClientCapabilities, type FullDocumentDiagnosticReport, DocumentDiagnosticReportKind
|
|
12
|
-
} from 'vscode-languageserver/node';
|
|
13
|
-
|
|
14
|
-
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
15
|
-
import { URI } from 'vscode-uri';
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
ExitCalled, OpenESLintDocRequest, Status, StatusNotification
|
|
19
|
-
} from './shared/customMessages';
|
|
20
|
-
|
|
21
|
-
import { Validate, CodeActionsOnSaveMode } from './shared/settings';
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
CodeActions, ESLint, ESLintClassOptions, FixableProblem, Fixes, Problem, RuleMetaData, RuleSeverities,
|
|
25
|
-
SaveRuleConfigs, SuggestionsProblem, TextDocumentSettings,
|
|
26
|
-
} from './eslint';
|
|
27
|
-
|
|
28
|
-
import { getFileSystemPath, getUri, isUNC } from './paths';
|
|
29
|
-
import { stringDiff } from './diff';
|
|
30
|
-
import LanguageDefaults from './languageDefaults';
|
|
31
|
-
|
|
32
|
-
// The connection to use. Code action requests get removed from the queue if
|
|
33
|
-
// canceled.
|
|
34
|
-
const connection: ProposedFeatures.Connection = createConnection(ProposedFeatures.all, {
|
|
35
|
-
connectionStrategy: {
|
|
36
|
-
cancelUndispatched: (message: LMessage) => {
|
|
37
|
-
// Code actions can safely be cancel on request.
|
|
38
|
-
if (LMessage.isRequest(message) && message.method === 'textDocument/codeAction') {
|
|
39
|
-
const response: LResponseMessage = {
|
|
40
|
-
jsonrpc: message.jsonrpc,
|
|
41
|
-
id: message.id,
|
|
42
|
-
result: null
|
|
43
|
-
};
|
|
44
|
-
return response;
|
|
45
|
-
}
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
maxParallelism: 1
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Set when handling the initialize request.
|
|
53
|
-
let clientCapabilities: ClientCapabilities;
|
|
54
|
-
|
|
55
|
-
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
|
56
|
-
// The notebooks manager is using the normal document manager for the cell documents.
|
|
57
|
-
// So all validating will work out of the box since normal document events will fire.
|
|
58
|
-
const notebooks = new NotebookDocuments(documents);
|
|
59
|
-
|
|
60
|
-
// This makes loading work in a plain NodeJS and a WebPacked environment
|
|
61
|
-
declare const __webpack_require__: typeof require;
|
|
62
|
-
declare const __non_webpack_require__: typeof require;
|
|
63
|
-
function loadNodeModule<T>(moduleName: string): T | undefined {
|
|
64
|
-
const r = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;
|
|
65
|
-
try {
|
|
66
|
-
return r(moduleName);
|
|
67
|
-
} catch (err: any) {
|
|
68
|
-
if (err.stack) {
|
|
69
|
-
connection.console.error(err.stack.toString());
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Some plugins call exit which will terminate the server.
|
|
76
|
-
// To not loose the information we sent such a behavior
|
|
77
|
-
// to the client.
|
|
78
|
-
const nodeExit = process.exit;
|
|
79
|
-
process.exit = ((code?: number): void => {
|
|
80
|
-
const stack = new Error('stack');
|
|
81
|
-
void connection.sendNotification(ExitCalled.type, [code ? code : 0, stack.stack]);
|
|
82
|
-
setTimeout(() => {
|
|
83
|
-
nodeExit(code);
|
|
84
|
-
}, 1000);
|
|
85
|
-
}) as any;
|
|
86
|
-
|
|
87
|
-
// Handling of uncaught exceptions hitting the event loop.
|
|
88
|
-
process.on('uncaughtException', (error: any) => {
|
|
89
|
-
let message: string | undefined;
|
|
90
|
-
if (error) {
|
|
91
|
-
if (typeof error.stack === 'string') {
|
|
92
|
-
message = error.stack;
|
|
93
|
-
} else if (typeof error.message === 'string') {
|
|
94
|
-
message = error.message;
|
|
95
|
-
} else if (typeof error === 'string') {
|
|
96
|
-
message = error;
|
|
97
|
-
}
|
|
98
|
-
if (message === undefined || message.length === 0) {
|
|
99
|
-
try {
|
|
100
|
-
message = JSON.stringify(error, undefined, 4);
|
|
101
|
-
} catch (e) {
|
|
102
|
-
// Should not happen.
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// eslint-disable-next-line no-console
|
|
107
|
-
console.error('Uncaught exception received.');
|
|
108
|
-
if (message) {
|
|
109
|
-
// eslint-disable-next-line no-console
|
|
110
|
-
console.error(message);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Infers a file path for a given URI / TextDocument. If the document is a notebook
|
|
116
|
-
* cell document it uses the file path from the notebook with a corresponding
|
|
117
|
-
* extension (e.g. TypeScript -> ts)
|
|
118
|
-
*/
|
|
119
|
-
function inferFilePath(documentOrUri: string | TextDocument | URI | undefined, useRealpaths: boolean): string | undefined {
|
|
120
|
-
if (!documentOrUri) {
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
const uri = getUri(documentOrUri);
|
|
124
|
-
if (uri.scheme === 'file') {
|
|
125
|
-
return getFileSystemPath(uri, useRealpaths);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const notebookDocument = notebooks.findNotebookDocumentForCell(uri.toString());
|
|
129
|
-
if (notebookDocument !== undefined ) {
|
|
130
|
-
const notebookUri = URI.parse(notebookDocument.uri);
|
|
131
|
-
if (notebookUri.scheme === 'file') {
|
|
132
|
-
const filePath = getFileSystemPath(uri, useRealpaths);
|
|
133
|
-
if (filePath !== undefined) {
|
|
134
|
-
const textDocument = documents.get(uri.toString());
|
|
135
|
-
if (textDocument !== undefined) {
|
|
136
|
-
const extension = LanguageDefaults.getExtension(textDocument.languageId);
|
|
137
|
-
if (extension !== undefined) {
|
|
138
|
-
const extname = path.extname(filePath);
|
|
139
|
-
if (extname.length === 0 && filePath[0] === '.') {
|
|
140
|
-
return `${filePath}.${extension}`;
|
|
141
|
-
} else if (extname.length > 0 && extname !== extension) {
|
|
142
|
-
return `${filePath.substring(0, filePath.length - extname.length)}.${extension}`;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
ESLint.initialize(connection, documents, inferFilePath, loadNodeModule);
|
|
153
|
-
SaveRuleConfigs.inferFilePath = inferFilePath;
|
|
154
|
-
|
|
155
|
-
documents.onDidClose(async (event) => {
|
|
156
|
-
const document = event.document;
|
|
157
|
-
const uri = document.uri;
|
|
158
|
-
ESLint.removeSettings(uri);
|
|
159
|
-
SaveRuleConfigs.remove(uri);
|
|
160
|
-
CodeActions.remove(uri);
|
|
161
|
-
ESLint.unregisterAsFormatter(document);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
function environmentChanged() {
|
|
165
|
-
ESLint.clearSettings();
|
|
166
|
-
RuleSeverities.clear();
|
|
167
|
-
SaveRuleConfigs.clear();
|
|
168
|
-
ESLint.clearFormatters();
|
|
169
|
-
connection.languages.diagnostics.refresh().catch(() => {
|
|
170
|
-
connection.console.error('Failed to refresh diagnostics');
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
namespace CommandIds {
|
|
175
|
-
export const applySingleFix: string = 'eslint.applySingleFix';
|
|
176
|
-
export const applySuggestion: string = 'eslint.applySuggestion';
|
|
177
|
-
export const applySameFixes: string = 'eslint.applySameFixes';
|
|
178
|
-
export const applyAllFixes: string = 'eslint.applyAllFixes';
|
|
179
|
-
export const applyDisableLine: string = 'eslint.applyDisableLine';
|
|
180
|
-
export const applyDisableFile: string = 'eslint.applyDisableFile';
|
|
181
|
-
export const openRuleDoc: string = 'eslint.openRuleDoc';
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
connection.onInitialize((params, _cancel, progress) => {
|
|
185
|
-
progress.begin('Initializing ESLint Server');
|
|
186
|
-
const syncKind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental;
|
|
187
|
-
clientCapabilities = params.capabilities;
|
|
188
|
-
progress.done();
|
|
189
|
-
const capabilities: ServerCapabilities = {
|
|
190
|
-
textDocumentSync: {
|
|
191
|
-
openClose: true,
|
|
192
|
-
change: syncKind,
|
|
193
|
-
willSaveWaitUntil: false,
|
|
194
|
-
save: {
|
|
195
|
-
includeText: false
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
workspace: {
|
|
199
|
-
workspaceFolders: {
|
|
200
|
-
supported: true
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
executeCommandProvider: {
|
|
204
|
-
commands: [
|
|
205
|
-
CommandIds.applySingleFix,
|
|
206
|
-
CommandIds.applySuggestion,
|
|
207
|
-
CommandIds.applySameFixes,
|
|
208
|
-
CommandIds.applyAllFixes,
|
|
209
|
-
CommandIds.applyDisableLine,
|
|
210
|
-
CommandIds.applyDisableFile,
|
|
211
|
-
CommandIds.openRuleDoc,
|
|
212
|
-
]
|
|
213
|
-
},
|
|
214
|
-
diagnosticProvider: {
|
|
215
|
-
identifier: 'eslint',
|
|
216
|
-
interFileDependencies: false,
|
|
217
|
-
workspaceDiagnostics: false
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
if (clientCapabilities.textDocument?.codeAction?.codeActionLiteralSupport?.codeActionKind.valueSet !== undefined) {
|
|
222
|
-
capabilities.codeActionProvider = {
|
|
223
|
-
codeActionKinds: [CodeActionKind.QuickFix, `${CodeActionKind.SourceFixAll}.eslint`]
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return { capabilities };
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
connection.onInitialized(() => {
|
|
231
|
-
if (clientCapabilities.workspace?.didChangeConfiguration?.dynamicRegistration === true) {
|
|
232
|
-
connection.onDidChangeConfiguration((_params) => {
|
|
233
|
-
environmentChanged();
|
|
234
|
-
});
|
|
235
|
-
void connection.client.register(DidChangeConfigurationNotification.type, undefined);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (clientCapabilities.workspace?.workspaceFolders === true) {
|
|
239
|
-
connection.workspace.onDidChangeWorkspaceFolders((_params) => {
|
|
240
|
-
environmentChanged();
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const emptyDiagnosticResult: FullDocumentDiagnosticReport = {
|
|
247
|
-
kind: DocumentDiagnosticReportKind.Full,
|
|
248
|
-
items: []
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
connection.languages.diagnostics.on(async (params) => {
|
|
252
|
-
const document = documents.get(params.textDocument.uri);
|
|
253
|
-
if (document === undefined) {
|
|
254
|
-
return emptyDiagnosticResult;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const settings = await ESLint.resolveSettings(document);
|
|
258
|
-
if (settings.validate !== Validate.on || !TextDocumentSettings.hasLibrary(settings)) {
|
|
259
|
-
return emptyDiagnosticResult;
|
|
260
|
-
}
|
|
261
|
-
try {
|
|
262
|
-
const start = Date.now();
|
|
263
|
-
const diagnostics = await ESLint.validate(document, settings);
|
|
264
|
-
const timeTaken = Date.now() - start;
|
|
265
|
-
void connection.sendNotification(StatusNotification.type, { uri: document.uri, state: Status.ok, validationTime: timeTaken });
|
|
266
|
-
return {
|
|
267
|
-
kind: DocumentDiagnosticReportKind.Full,
|
|
268
|
-
items: diagnostics
|
|
269
|
-
};
|
|
270
|
-
} catch (err) {
|
|
271
|
-
// if an exception has occurred while validating clear all errors to ensure
|
|
272
|
-
// we are not showing any stale once
|
|
273
|
-
if (!settings.silent) {
|
|
274
|
-
let status: Status | undefined = undefined;
|
|
275
|
-
for (const handler of ESLint.ErrorHandlers.single) {
|
|
276
|
-
status = handler(err, document, settings.library, settings);
|
|
277
|
-
if (status) {
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
status = status || Status.error;
|
|
282
|
-
void connection.sendNotification(StatusNotification.type, { uri: document.uri, state: status });
|
|
283
|
-
} else {
|
|
284
|
-
connection.console.info(ESLint.ErrorHandlers.getMessage(err, document));
|
|
285
|
-
void connection.sendNotification(StatusNotification.type, { uri: document.uri, state: Status.ok });
|
|
286
|
-
}
|
|
287
|
-
return emptyDiagnosticResult;
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
connection.onDidChangeWatchedFiles(async (params) => {
|
|
292
|
-
// A .eslintrc has change. No smartness here.
|
|
293
|
-
// Simply revalidate all file.
|
|
294
|
-
RuleMetaData.clear();
|
|
295
|
-
ESLint.ErrorHandlers.clearNoConfigReported();
|
|
296
|
-
ESLint.ErrorHandlers.clearMissingModuleReported();
|
|
297
|
-
ESLint.clearSettings(); // config files can change plugins and parser.
|
|
298
|
-
RuleSeverities.clear();
|
|
299
|
-
SaveRuleConfigs.clear();
|
|
300
|
-
|
|
301
|
-
await Promise.all(params.changes.map(async (change) => {
|
|
302
|
-
const fsPath = inferFilePath(change.uri, false);
|
|
303
|
-
if (fsPath === undefined || fsPath.length === 0 || isUNC(fsPath)) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
const dirname = path.dirname(fsPath);
|
|
307
|
-
if (dirname) {
|
|
308
|
-
const data = ESLint.ErrorHandlers.getConfigErrorReported(fsPath);
|
|
309
|
-
if (data !== undefined) {
|
|
310
|
-
const eslintClass = await ESLint.newClass(data.library, {}, data.settings);
|
|
311
|
-
try {
|
|
312
|
-
await eslintClass.lintText('', { filePath: path.join(dirname, '___test___.js') });
|
|
313
|
-
ESLint.ErrorHandlers.removeConfigErrorReported(fsPath);
|
|
314
|
-
} catch (error) {
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}));
|
|
319
|
-
connection.languages.diagnostics.refresh().catch(() => {
|
|
320
|
-
connection.console.error('Failed to refresh diagnostics');
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
type RuleCodeActions = {
|
|
325
|
-
fixes: CodeAction[];
|
|
326
|
-
suggestions: CodeAction[];
|
|
327
|
-
disable?: CodeAction;
|
|
328
|
-
fixAll?: CodeAction;
|
|
329
|
-
disableFile?: CodeAction;
|
|
330
|
-
showDocumentation?: CodeAction;
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
class CodeActionResult {
|
|
334
|
-
private _actions: Map<string, RuleCodeActions>;
|
|
335
|
-
private _fixAll: CodeAction[] | undefined;
|
|
336
|
-
|
|
337
|
-
public constructor() {
|
|
338
|
-
this._actions = new Map();
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
public get(ruleId: string): RuleCodeActions {
|
|
342
|
-
let result: RuleCodeActions | undefined = this._actions.get(ruleId);
|
|
343
|
-
if (result === undefined) {
|
|
344
|
-
result = { fixes: [], suggestions: [] };
|
|
345
|
-
this._actions.set(ruleId, result);
|
|
346
|
-
}
|
|
347
|
-
return result;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
public get fixAll() {
|
|
351
|
-
if (this._fixAll === undefined) {
|
|
352
|
-
this._fixAll = [];
|
|
353
|
-
}
|
|
354
|
-
return this._fixAll;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
public all(): CodeAction[] {
|
|
358
|
-
const result: CodeAction[] = [];
|
|
359
|
-
for (const actions of this._actions.values()) {
|
|
360
|
-
result.push(...actions.fixes);
|
|
361
|
-
result.push(...actions.suggestions);
|
|
362
|
-
if (actions.disable) {
|
|
363
|
-
result.push(actions.disable);
|
|
364
|
-
}
|
|
365
|
-
if (actions.fixAll) {
|
|
366
|
-
result.push(actions.fixAll);
|
|
367
|
-
}
|
|
368
|
-
if (actions.disableFile) {
|
|
369
|
-
result.push(actions.disableFile);
|
|
370
|
-
}
|
|
371
|
-
if (actions.showDocumentation) {
|
|
372
|
-
result.push(actions.showDocumentation);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
if (this._fixAll !== undefined) {
|
|
376
|
-
result.push(...this._fixAll);
|
|
377
|
-
}
|
|
378
|
-
return result;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
public get length(): number {
|
|
382
|
-
let result: number = 0;
|
|
383
|
-
for (const actions of this._actions.values()) {
|
|
384
|
-
result += actions.fixes.length;
|
|
385
|
-
}
|
|
386
|
-
return result;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
class Changes {
|
|
391
|
-
|
|
392
|
-
private readonly values: Map<string, WorkspaceChange>;
|
|
393
|
-
private uri: string | undefined;
|
|
394
|
-
private version: number | undefined;
|
|
395
|
-
|
|
396
|
-
constructor() {
|
|
397
|
-
this.values = new Map();
|
|
398
|
-
this.uri = undefined;
|
|
399
|
-
this.version = undefined;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
public clear(textDocument?: TextDocument): void {
|
|
403
|
-
if (textDocument === undefined) {
|
|
404
|
-
this.uri = undefined;
|
|
405
|
-
this.version = undefined;
|
|
406
|
-
} else {
|
|
407
|
-
this.uri = textDocument.uri;
|
|
408
|
-
this.version = textDocument.version;
|
|
409
|
-
}
|
|
410
|
-
this.values.clear();
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
public isUsable(uri: string, version: number): boolean {
|
|
414
|
-
return this.uri === uri && this.version === version;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
public set(key: string, change: WorkspaceChange): void {
|
|
418
|
-
this.values.set(key, change);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
public get(key: string): WorkspaceChange | undefined {
|
|
422
|
-
return this.values.get(key);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
interface CommandParams extends VersionedTextDocumentIdentifier {
|
|
427
|
-
version: number;
|
|
428
|
-
ruleId?: string;
|
|
429
|
-
sequence?: number;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
namespace CommandParams {
|
|
433
|
-
export function create(textDocument: TextDocument, ruleId?: string, sequence?: number): CommandParams {
|
|
434
|
-
return { uri: textDocument.uri, version: textDocument.version, ruleId, sequence };
|
|
435
|
-
}
|
|
436
|
-
export function hasRuleId(value: CommandParams): value is CommandParams & { ruleId: string } {
|
|
437
|
-
return value.ruleId !== undefined;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const changes = new Changes();
|
|
442
|
-
const ESLintSourceFixAll: string = `${CodeActionKind.SourceFixAll}.eslint`;
|
|
443
|
-
|
|
444
|
-
connection.onCodeAction(async (params) => {
|
|
445
|
-
const result: CodeActionResult = new CodeActionResult();
|
|
446
|
-
const uri = params.textDocument.uri;
|
|
447
|
-
const textDocument = documents.get(uri);
|
|
448
|
-
if (textDocument === undefined) {
|
|
449
|
-
changes.clear(textDocument);
|
|
450
|
-
return result.all();
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function createCodeAction(title: string, kind: string, commandId: string, arg: CommandParams, diagnostic?: Diagnostic): CodeAction {
|
|
454
|
-
const command = Command.create(title, commandId, arg);
|
|
455
|
-
const action = CodeAction.create(
|
|
456
|
-
title,
|
|
457
|
-
command,
|
|
458
|
-
kind
|
|
459
|
-
);
|
|
460
|
-
if (diagnostic !== undefined) {
|
|
461
|
-
action.diagnostics = [diagnostic];
|
|
462
|
-
}
|
|
463
|
-
return action;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function getDisableRuleEditInsertionIndex(line: string, commentTags: string | [string, string]): number {
|
|
467
|
-
let charIndex = line.indexOf('--');
|
|
468
|
-
|
|
469
|
-
if (charIndex < 0) {
|
|
470
|
-
if (typeof commentTags === 'string') {
|
|
471
|
-
return line.length;
|
|
472
|
-
} else { // commentTags is an array containing the block comment closing and opening tags
|
|
473
|
-
charIndex = line.indexOf(commentTags[1]);
|
|
474
|
-
while (charIndex > 0 && line[charIndex - 1] === ' ') {
|
|
475
|
-
charIndex--;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
} else {
|
|
479
|
-
while (charIndex > 1 && line[charIndex - 1] === ' ') {
|
|
480
|
-
charIndex--;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return charIndex;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Prefix characters with special meaning in comment markers with a backslash
|
|
489
|
-
* See also: https://github.com/microsoft/vscode-eslint/issues/1610
|
|
490
|
-
*/
|
|
491
|
-
function escapeStringRegexp(value: string) {
|
|
492
|
-
return value.replace(/[|{}\\()[\]^$+*?.]/g, '\\$&');
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function createDisableLineTextEdit(textDocument: TextDocument, editInfo: Problem, indentationText: string): TextEdit {
|
|
496
|
-
const lineComment = LanguageDefaults.getLineComment(textDocument.languageId);
|
|
497
|
-
const blockComment = LanguageDefaults.getBlockComment(textDocument.languageId);
|
|
498
|
-
|
|
499
|
-
// If the concerned line is not the first line of the file
|
|
500
|
-
if (editInfo.line - 1 > 0) {
|
|
501
|
-
// Check previous line if there is a eslint-disable-next-line comment already present.
|
|
502
|
-
const prevLine = textDocument.getText(Range.create(Position.create(editInfo.line - 2, 0), Position.create(editInfo.line - 2, uinteger.MAX_VALUE)));
|
|
503
|
-
|
|
504
|
-
// For consistency, we ignore the settings here and use the comment style from that
|
|
505
|
-
// specific line.
|
|
506
|
-
const matchedLineDisable = new RegExp(`${escapeStringRegexp(lineComment)} eslint-disable-next-line`).test(prevLine);
|
|
507
|
-
if (matchedLineDisable) {
|
|
508
|
-
const insertionIndex = getDisableRuleEditInsertionIndex(prevLine, lineComment);
|
|
509
|
-
return TextEdit.insert(Position.create(editInfo.line - 2, insertionIndex), `, ${editInfo.ruleId}`);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const matchedBlockDisable = new RegExp(`${escapeStringRegexp(blockComment[0])} eslint-disable-next-line`).test(prevLine);
|
|
513
|
-
if (matchedBlockDisable) {
|
|
514
|
-
const insertionIndex = getDisableRuleEditInsertionIndex(prevLine, blockComment);
|
|
515
|
-
return TextEdit.insert(Position.create(editInfo.line - 2, insertionIndex), `, ${editInfo.ruleId}`);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// We're creating a new disabling comment. Use the comment style given in settings.
|
|
520
|
-
const commentStyle = settings.codeAction.disableRuleComment.commentStyle;
|
|
521
|
-
let disableRuleContent: string;
|
|
522
|
-
if (commentStyle === 'block') {
|
|
523
|
-
disableRuleContent = `${indentationText}${blockComment[0]} eslint-disable-next-line ${editInfo.ruleId} ${blockComment[1]}${EOL}`;
|
|
524
|
-
} else { // commentStyle === 'line'
|
|
525
|
-
disableRuleContent = `${indentationText}${lineComment} eslint-disable-next-line ${editInfo.ruleId}${EOL}`;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
return TextEdit.insert(Position.create(editInfo.line - 1, 0), disableRuleContent);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function createDisableSameLineTextEdit(textDocument: TextDocument, editInfo: Problem): TextEdit {
|
|
532
|
-
const lineComment = LanguageDefaults.getLineComment(textDocument.languageId);
|
|
533
|
-
const blockComment = LanguageDefaults.getBlockComment(textDocument.languageId);
|
|
534
|
-
const currentLine = textDocument.getText(Range.create(Position.create(editInfo.line - 1, 0), Position.create(editInfo.line - 1, uinteger.MAX_VALUE)));
|
|
535
|
-
let disableRuleContent: string;
|
|
536
|
-
let insertionIndex: number;
|
|
537
|
-
|
|
538
|
-
// Check if there's already a disabling comment. If so, we ignore the settings here
|
|
539
|
-
// and use the comment style from that specific line.
|
|
540
|
-
const matchedLineDisable = new RegExp(`${lineComment} eslint-disable-line`).test(currentLine);
|
|
541
|
-
const matchedBlockDisable = new RegExp(`${blockComment[0]} eslint-disable-line`).test(currentLine);
|
|
542
|
-
if (matchedLineDisable) {
|
|
543
|
-
disableRuleContent = `, ${editInfo.ruleId}`;
|
|
544
|
-
insertionIndex = getDisableRuleEditInsertionIndex(currentLine, lineComment);
|
|
545
|
-
} else if (matchedBlockDisable) {
|
|
546
|
-
disableRuleContent = `, ${editInfo.ruleId}`;
|
|
547
|
-
insertionIndex = getDisableRuleEditInsertionIndex(currentLine, blockComment);
|
|
548
|
-
} else {
|
|
549
|
-
// We're creating a new disabling comment.
|
|
550
|
-
const commentStyle = settings.codeAction.disableRuleComment.commentStyle;
|
|
551
|
-
disableRuleContent = commentStyle === 'line' ? ` ${lineComment} eslint-disable-line ${editInfo.ruleId}` : ` ${blockComment[0]} eslint-disable-line ${editInfo.ruleId} ${blockComment[1]}`;
|
|
552
|
-
insertionIndex = uinteger.MAX_VALUE;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
return TextEdit.insert(Position.create(editInfo.line - 1, insertionIndex), disableRuleContent);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
function createDisableFileTextEdit(textDocument: TextDocument, editInfo: Problem): TextEdit {
|
|
559
|
-
// If first line contains a shebang, insert on the next line instead.
|
|
560
|
-
const shebang = textDocument.getText(Range.create(Position.create(0, 0), Position.create(0, 2)));
|
|
561
|
-
const line = shebang === '#!' ? 1 : 0;
|
|
562
|
-
const block = LanguageDefaults.getBlockComment(textDocument.languageId);
|
|
563
|
-
return TextEdit.insert(Position.create(line, 0), `${block[0]} eslint-disable ${editInfo.ruleId} ${block[1]}${EOL}`);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
function getLastEdit(array: FixableProblem[]): FixableProblem | undefined {
|
|
567
|
-
const length = array.length;
|
|
568
|
-
if (length === 0) {
|
|
569
|
-
return undefined;
|
|
570
|
-
}
|
|
571
|
-
return array[length - 1];
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
const settings = await ESLint.resolveSettings(textDocument);
|
|
575
|
-
|
|
576
|
-
// The file is not validated at all or we couldn't load an eslint library for it.
|
|
577
|
-
if (settings.validate !== Validate.on || !TextDocumentSettings.hasLibrary(settings)) {
|
|
578
|
-
return result.all();
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
const problems = CodeActions.get(uri);
|
|
582
|
-
// We validate on type and have no problems ==> nothing to fix.
|
|
583
|
-
if (problems === undefined && settings.run === 'onType') {
|
|
584
|
-
return result.all();
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
const only: string | undefined = params.context.only !== undefined && params.context.only.length > 0 ? params.context.only[0] : undefined;
|
|
588
|
-
const isSource = only === CodeActionKind.Source;
|
|
589
|
-
const isSourceFixAll = (only === ESLintSourceFixAll || only === CodeActionKind.SourceFixAll);
|
|
590
|
-
if (isSourceFixAll || isSource) {
|
|
591
|
-
if (isSourceFixAll) {
|
|
592
|
-
const textDocumentIdentifier: VersionedTextDocumentIdentifier = { uri: textDocument.uri, version: textDocument.version };
|
|
593
|
-
const edits = await computeAllFixes(textDocumentIdentifier, AllFixesMode.onSave);
|
|
594
|
-
if (edits !== undefined) {
|
|
595
|
-
result.fixAll.push(CodeAction.create(
|
|
596
|
-
`Fix all fixable ESLint issues`,
|
|
597
|
-
{ documentChanges: [ TextDocumentEdit.create(textDocumentIdentifier, edits )]},
|
|
598
|
-
ESLintSourceFixAll
|
|
599
|
-
));
|
|
600
|
-
}
|
|
601
|
-
} else if (isSource) {
|
|
602
|
-
result.fixAll.push(createCodeAction(
|
|
603
|
-
`Fix all fixable ESLint issues`,
|
|
604
|
-
CodeActionKind.Source,
|
|
605
|
-
CommandIds.applyAllFixes,
|
|
606
|
-
CommandParams.create(textDocument)
|
|
607
|
-
));
|
|
608
|
-
}
|
|
609
|
-
return result.all();
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
if (problems === undefined) {
|
|
613
|
-
return result.all();
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const fixes = new Fixes(problems);
|
|
617
|
-
if (fixes.isEmpty()) {
|
|
618
|
-
return result.all();
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
let documentVersion: number = -1;
|
|
622
|
-
const allFixableRuleIds: string[] = [];
|
|
623
|
-
const kind: CodeActionKind = only ?? CodeActionKind.QuickFix;
|
|
624
|
-
|
|
625
|
-
for (const editInfo of fixes.getScoped(params.context.diagnostics)) {
|
|
626
|
-
documentVersion = editInfo.documentVersion;
|
|
627
|
-
const ruleId = editInfo.ruleId;
|
|
628
|
-
allFixableRuleIds.push(ruleId);
|
|
629
|
-
|
|
630
|
-
if (Problem.isFixable(editInfo)) {
|
|
631
|
-
const workspaceChange = new WorkspaceChange();
|
|
632
|
-
workspaceChange.getTextEditChange({ uri, version: documentVersion }).add(FixableProblem.createTextEdit(textDocument, editInfo));
|
|
633
|
-
changes.set(`${CommandIds.applySingleFix}:${ruleId}`, workspaceChange);
|
|
634
|
-
const action = createCodeAction(
|
|
635
|
-
editInfo.label,
|
|
636
|
-
kind,
|
|
637
|
-
CommandIds.applySingleFix,
|
|
638
|
-
CommandParams.create(textDocument, ruleId),
|
|
639
|
-
editInfo.diagnostic
|
|
640
|
-
);
|
|
641
|
-
action.isPreferred = true;
|
|
642
|
-
result.get(ruleId).fixes.push(action);
|
|
643
|
-
}
|
|
644
|
-
if (Problem.hasSuggestions(editInfo)) {
|
|
645
|
-
editInfo.suggestions.forEach((suggestion, suggestionSequence) => {
|
|
646
|
-
const workspaceChange = new WorkspaceChange();
|
|
647
|
-
workspaceChange.getTextEditChange({ uri, version: documentVersion }).add(SuggestionsProblem.createTextEdit(textDocument, suggestion));
|
|
648
|
-
changes.set(`${CommandIds.applySuggestion}:${ruleId}:${suggestionSequence}`, workspaceChange);
|
|
649
|
-
const action = createCodeAction(
|
|
650
|
-
`${suggestion.desc} (${editInfo.ruleId})`,
|
|
651
|
-
CodeActionKind.QuickFix,
|
|
652
|
-
CommandIds.applySuggestion,
|
|
653
|
-
CommandParams.create(textDocument, ruleId, suggestionSequence),
|
|
654
|
-
editInfo.diagnostic
|
|
655
|
-
);
|
|
656
|
-
result.get(ruleId).suggestions.push(action);
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
if (settings.codeAction.disableRuleComment.enable && ruleId !== RuleMetaData.unusedDisableDirectiveId) {
|
|
661
|
-
let workspaceChange = new WorkspaceChange();
|
|
662
|
-
if (settings.codeAction.disableRuleComment.location === 'sameLine') {
|
|
663
|
-
workspaceChange.getTextEditChange({ uri, version: documentVersion }).add(createDisableSameLineTextEdit(textDocument, editInfo));
|
|
664
|
-
} else {
|
|
665
|
-
const lineText = textDocument.getText(Range.create(Position.create(editInfo.line - 1, 0), Position.create(editInfo.line - 1, uinteger.MAX_VALUE)));
|
|
666
|
-
const matches = /^([ \t]*)/.exec(lineText);
|
|
667
|
-
const indentationText = matches !== null && matches.length > 0 ? matches[1] : '';
|
|
668
|
-
workspaceChange.getTextEditChange({ uri, version: documentVersion }).add(createDisableLineTextEdit(textDocument, editInfo, indentationText));
|
|
669
|
-
}
|
|
670
|
-
changes.set(`${CommandIds.applyDisableLine}:${ruleId}`, workspaceChange);
|
|
671
|
-
result.get(ruleId).disable = createCodeAction(
|
|
672
|
-
`Disable ${ruleId} for this line`,
|
|
673
|
-
kind,
|
|
674
|
-
CommandIds.applyDisableLine,
|
|
675
|
-
CommandParams.create(textDocument, ruleId)
|
|
676
|
-
);
|
|
677
|
-
|
|
678
|
-
if (result.get(ruleId).disableFile === undefined) {
|
|
679
|
-
workspaceChange = new WorkspaceChange();
|
|
680
|
-
workspaceChange.getTextEditChange({ uri, version: documentVersion }).add(createDisableFileTextEdit(textDocument, editInfo));
|
|
681
|
-
changes.set(`${CommandIds.applyDisableFile}:${ruleId}`, workspaceChange);
|
|
682
|
-
result.get(ruleId).disableFile = createCodeAction(
|
|
683
|
-
`Disable ${ruleId} for the entire file`,
|
|
684
|
-
kind,
|
|
685
|
-
CommandIds.applyDisableFile,
|
|
686
|
-
CommandParams.create(textDocument, ruleId)
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
if (settings.codeAction.showDocumentation.enable && result.get(ruleId).showDocumentation === undefined) {
|
|
692
|
-
if (RuleMetaData.hasRuleId(ruleId)) {
|
|
693
|
-
result.get(ruleId).showDocumentation = createCodeAction(
|
|
694
|
-
`Show documentation for ${ruleId}`,
|
|
695
|
-
kind,
|
|
696
|
-
CommandIds.openRuleDoc,
|
|
697
|
-
CommandParams.create(textDocument, ruleId)
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
if (result.length > 0) {
|
|
704
|
-
const sameProblems: Map<string, FixableProblem[]> = new Map<string, FixableProblem[]>(allFixableRuleIds.map<[string, FixableProblem[]]>(s => [s, []]));
|
|
705
|
-
|
|
706
|
-
for (const editInfo of fixes.getAllSorted()) {
|
|
707
|
-
if (documentVersion === -1) {
|
|
708
|
-
documentVersion = editInfo.documentVersion;
|
|
709
|
-
}
|
|
710
|
-
if (sameProblems.has(editInfo.ruleId)) {
|
|
711
|
-
const same = sameProblems.get(editInfo.ruleId)!;
|
|
712
|
-
if (!Fixes.overlaps(getLastEdit(same), editInfo)) {
|
|
713
|
-
same.push(editInfo);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
sameProblems.forEach((same, ruleId) => {
|
|
718
|
-
if (same.length > 1) {
|
|
719
|
-
const sameFixes: WorkspaceChange = new WorkspaceChange();
|
|
720
|
-
const sameTextChange = sameFixes.getTextEditChange({ uri, version: documentVersion });
|
|
721
|
-
same.map(fix => FixableProblem.createTextEdit(textDocument, fix)).forEach(edit => sameTextChange.add(edit));
|
|
722
|
-
changes.set(CommandIds.applySameFixes, sameFixes);
|
|
723
|
-
result.get(ruleId).fixAll = createCodeAction(
|
|
724
|
-
`Fix all ${ruleId} problems`,
|
|
725
|
-
kind,
|
|
726
|
-
CommandIds.applySameFixes,
|
|
727
|
-
CommandParams.create(textDocument)
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
result.fixAll.push(createCodeAction(
|
|
732
|
-
`Fix all auto-fixable problems`,
|
|
733
|
-
kind,
|
|
734
|
-
CommandIds.applyAllFixes,
|
|
735
|
-
CommandParams.create(textDocument)
|
|
736
|
-
));
|
|
737
|
-
}
|
|
738
|
-
return result.all();
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
enum AllFixesMode {
|
|
742
|
-
onSave = 'onsave',
|
|
743
|
-
format = 'format',
|
|
744
|
-
command = 'command'
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
async function computeAllFixes(identifier: VersionedTextDocumentIdentifier, mode: AllFixesMode): Promise<TextEdit[] | undefined> {
|
|
748
|
-
const uri = identifier.uri;
|
|
749
|
-
const textDocument = documents.get(uri)!;
|
|
750
|
-
if (textDocument === undefined || identifier.version !== textDocument.version) {
|
|
751
|
-
return undefined;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
const settings = await ESLint.resolveSettings(textDocument);
|
|
755
|
-
|
|
756
|
-
if (settings.validate !== Validate.on || !TextDocumentSettings.hasLibrary(settings) || (mode === AllFixesMode.format && !settings.format)) {
|
|
757
|
-
return [];
|
|
758
|
-
}
|
|
759
|
-
const filePath = inferFilePath(textDocument, settings.useRealpaths);
|
|
760
|
-
const problems = CodeActions.get(uri);
|
|
761
|
-
const originalContent = textDocument.getText();
|
|
762
|
-
let start = Date.now();
|
|
763
|
-
// Only use known fixes when running in onSave mode. See https://github.com/microsoft/vscode-eslint/issues/871
|
|
764
|
-
// for details
|
|
765
|
-
if (mode === AllFixesMode.onSave && settings.codeActionOnSave.mode === CodeActionsOnSaveMode.problems) {
|
|
766
|
-
const result = problems !== undefined && problems.size > 0
|
|
767
|
-
? new Fixes(problems).getApplicable().map(fix => FixableProblem.createTextEdit(textDocument, fix))
|
|
768
|
-
: [];
|
|
769
|
-
connection.tracer.log(`Computing all fixes took: ${Date.now() - start} ms.`);
|
|
770
|
-
return result;
|
|
771
|
-
} else {
|
|
772
|
-
const saveConfig = filePath !== undefined && mode === AllFixesMode.onSave ? await SaveRuleConfigs.get(uri, settings) : undefined;
|
|
773
|
-
const offRules = saveConfig?.offRules;
|
|
774
|
-
const overrideOptions = saveConfig?.options;
|
|
775
|
-
let eslintOptions: ESLintClassOptions = { fix: true };
|
|
776
|
-
if (offRules !== undefined || overrideOptions !== undefined) {
|
|
777
|
-
if (overrideOptions !== undefined) {
|
|
778
|
-
eslintOptions = { ...eslintOptions, ...overrideOptions };
|
|
779
|
-
}
|
|
780
|
-
if (offRules !== undefined && offRules.size > 0) {
|
|
781
|
-
const overrideConfig = { rules: Object.create(null) };
|
|
782
|
-
for (const ruleId of offRules) {
|
|
783
|
-
overrideConfig.rules[ruleId] = 'off';
|
|
784
|
-
}
|
|
785
|
-
eslintOptions.overrideConfig = overrideConfig;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
return ESLint.withClass(async (eslintClass) => {
|
|
789
|
-
// Don't use any precomputed fixes since neighbour fixes can produce incorrect results.
|
|
790
|
-
// See https://github.com/microsoft/vscode-eslint/issues/1745
|
|
791
|
-
const result: TextEdit[] = [];
|
|
792
|
-
const reportResults = await eslintClass.lintText(originalContent, { filePath });
|
|
793
|
-
connection.tracer.log(`Computing all fixes took: ${Date.now() - start} ms.`);
|
|
794
|
-
if (Array.isArray(reportResults) && reportResults.length === 1 && reportResults[0].output !== undefined) {
|
|
795
|
-
const fixedContent = reportResults[0].output;
|
|
796
|
-
start = Date.now();
|
|
797
|
-
const diffs = stringDiff(originalContent, fixedContent, false);
|
|
798
|
-
connection.tracer.log(`Computing minimal edits took: ${Date.now() - start} ms.`);
|
|
799
|
-
for (const diff of diffs) {
|
|
800
|
-
result.push({
|
|
801
|
-
range: {
|
|
802
|
-
start: textDocument.positionAt(diff.originalStart),
|
|
803
|
-
end: textDocument.positionAt(diff.originalStart + diff.originalLength)
|
|
804
|
-
},
|
|
805
|
-
newText: fixedContent.substr(diff.modifiedStart, diff.modifiedLength)
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
return result;
|
|
810
|
-
}, settings, eslintOptions);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
connection.onExecuteCommand(async (params) => {
|
|
815
|
-
let workspaceChange: WorkspaceChange | undefined;
|
|
816
|
-
const commandParams: CommandParams = params.arguments![0] as CommandParams;
|
|
817
|
-
if (params.command === CommandIds.applyAllFixes) {
|
|
818
|
-
const edits = await computeAllFixes(commandParams, AllFixesMode.command);
|
|
819
|
-
if (edits !== undefined && edits.length > 0) {
|
|
820
|
-
workspaceChange = new WorkspaceChange();
|
|
821
|
-
const textChange = workspaceChange.getTextEditChange(commandParams);
|
|
822
|
-
edits.forEach(edit => textChange.add(edit));
|
|
823
|
-
}
|
|
824
|
-
} else {
|
|
825
|
-
if ([CommandIds.applySingleFix, CommandIds.applyDisableLine, CommandIds.applyDisableFile].indexOf(params.command) !== -1) {
|
|
826
|
-
workspaceChange = changes.get(`${params.command}:${commandParams.ruleId}`);
|
|
827
|
-
} else if ([CommandIds.applySuggestion].indexOf(params.command) !== -1) {
|
|
828
|
-
workspaceChange = changes.get(`${params.command}:${commandParams.ruleId}:${commandParams.sequence}`);
|
|
829
|
-
} else if (params.command === CommandIds.openRuleDoc && CommandParams.hasRuleId(commandParams)) {
|
|
830
|
-
const url = RuleMetaData.getUrl(commandParams.ruleId);
|
|
831
|
-
if (url) {
|
|
832
|
-
void connection.sendRequest(OpenESLintDocRequest.type, { url });
|
|
833
|
-
}
|
|
834
|
-
} else {
|
|
835
|
-
workspaceChange = changes.get(params.command);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
if (workspaceChange === undefined) {
|
|
840
|
-
return null;
|
|
841
|
-
}
|
|
842
|
-
return connection.workspace.applyEdit(workspaceChange.edit).then((response) => {
|
|
843
|
-
if (!response.applied) {
|
|
844
|
-
connection.console.error(`Failed to apply command: ${params.command}`);
|
|
845
|
-
}
|
|
846
|
-
return null;
|
|
847
|
-
}, () => {
|
|
848
|
-
connection.console.error(`Failed to apply command: ${params.command}`);
|
|
849
|
-
return null;
|
|
850
|
-
});
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
connection.onDocumentFormatting((params) => {
|
|
854
|
-
const textDocument = documents.get(params.textDocument.uri);
|
|
855
|
-
if (textDocument === undefined) {
|
|
856
|
-
return [];
|
|
857
|
-
}
|
|
858
|
-
return computeAllFixes({ uri: textDocument.uri, version: textDocument.version }, AllFixesMode.format);
|
|
859
|
-
});
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
documents.listen(connection);
|
|
863
|
-
notebooks.listen(connection);
|
|
864
|
-
connection.listen();
|
|
865
|
-
connection.console.info(`ESLint server running in node ${process.version}`);
|