vscode-eslint 0.0.3 → 0.0.5

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.
Files changed (173) hide show
  1. package/bin/vscode-eslint +24 -1
  2. package/client/out/client.d.ts +13 -0
  3. package/client/out/extension.d.ts +3 -0
  4. package/client/out/extension.js +2 -0
  5. package/client/out/extension.js.map +1 -0
  6. package/client/out/node-utils.d.ts +51 -0
  7. package/client/out/settings.d.ts +41 -0
  8. package/client/out/shared/customMessages.d.ts +89 -0
  9. package/client/out/shared/settings.d.ts +106 -0
  10. package/client/out/tasks.d.ts +31 -0
  11. package/client/out/tests/glob.test.d.ts +1 -0
  12. package/client/out/vscode-utils.d.ts +5 -0
  13. package/package.json +9 -1
  14. package/server/out/diff.d.ts +134 -0
  15. package/server/out/eslint.d.ts +298 -0
  16. package/server/out/eslintServer.d.ts +1 -0
  17. package/server/out/eslintServer.js +2 -0
  18. package/server/out/eslintServer.js.map +1 -0
  19. package/server/out/is.d.ts +3 -0
  20. package/server/out/languageDefaults.d.ts +6 -0
  21. package/server/out/linkedMap.d.ts +53 -0
  22. package/server/out/paths.d.ts +22 -0
  23. package/server/out/shared/customMessages.d.ts +89 -0
  24. package/server/out/shared/settings.d.ts +106 -0
  25. package/$shared/customMessages.ts +0 -113
  26. package/$shared/settings.ts +0 -189
  27. package/.CodeQL.yml +0 -5
  28. package/.azure-pipelines.yml +0 -27
  29. package/.gemini/agents/vscode-eslint.md +0 -23
  30. package/.github/commands.yml +0 -127
  31. package/.github/locker.yml +0 -6
  32. package/.github/needs_more_info.yml +0 -6
  33. package/.github/workflows/npm-publish.yml +0 -53
  34. package/.github/workflows/release-please.yml +0 -22
  35. package/.lsifrc.json +0 -4
  36. package/.vscode/launch.json +0 -20
  37. package/.vscode/settings.json +0 -52
  38. package/.vscode/spellright.dict +0 -8
  39. package/.vscode/tasks.json +0 -39
  40. package/.vscodeignore +0 -23
  41. package/CHANGELOG.md +0 -524
  42. package/SECURITY.md +0 -41
  43. package/agents.md +0 -36
  44. package/client/.mocharc.json +0 -6
  45. package/client/agents.md +0 -5
  46. package/client/package-lock.json +0 -176
  47. package/client/package.json +0 -29
  48. package/client/src/client.ts +0 -992
  49. package/client/src/extension.ts +0 -180
  50. package/client/src/node-utils.ts +0 -393
  51. package/client/src/settings.ts +0 -379
  52. package/client/src/tasks.ts +0 -186
  53. package/client/src/tests/glob.test.ts +0 -31
  54. package/client/src/vscode-utils.ts +0 -28
  55. package/client/test/mocha.opts +0 -3
  56. package/client/tsconfig.json +0 -20
  57. package/client/webpack.config.js +0 -25
  58. package/contributing.md +0 -19
  59. package/esbuild.js +0 -62
  60. package/eslint.config.js +0 -129
  61. package/history/settings_1_9_x.md +0 -110
  62. package/images/2_1_10/eslint-dialog.png +0 -0
  63. package/images/2_1_10/eslint-status.png +0 -0
  64. package/package-json-schema.json +0 -9
  65. package/playgrounds/7.0/.eslintignore +0 -1
  66. package/playgrounds/7.0/.eslintrc.json +0 -71
  67. package/playgrounds/7.0/.vscode/settings.json +0 -85
  68. package/playgrounds/7.0/app.js +0 -12
  69. package/playgrounds/7.0/build/.eslintignore +0 -1
  70. package/playgrounds/7.0/build/.eslintrc.json +0 -30
  71. package/playgrounds/7.0/build/build.js +0 -11
  72. package/playgrounds/7.0/jsconfig.json +0 -5
  73. package/playgrounds/7.0/package-lock.json +0 -2133
  74. package/playgrounds/7.0/package.json +0 -10
  75. package/playgrounds/7.0/readme.md +0 -0
  76. package/playgrounds/7.0/subDir/sub.js +0 -11
  77. package/playgrounds/7.0/subDir/test.jsx +0 -10
  78. package/playgrounds/7.0/test.js +0 -11
  79. package/playgrounds/7.0/test.sh +0 -1
  80. package/playgrounds/7.0/test.vue +0 -33
  81. package/playgrounds/7.0/test2.html +0 -8
  82. package/playgrounds/8.0/.eslintignore +0 -1
  83. package/playgrounds/8.0/.eslintrc.json +0 -71
  84. package/playgrounds/8.0/.vscode/settings.json +0 -91
  85. package/playgrounds/8.0/app.js +0 -12
  86. package/playgrounds/8.0/build/.eslintignore +0 -1
  87. package/playgrounds/8.0/build/.eslintrc.json +0 -30
  88. package/playgrounds/8.0/build/build.js +0 -11
  89. package/playgrounds/8.0/jsconfig.json +0 -5
  90. package/playgrounds/8.0/package-lock.json +0 -2321
  91. package/playgrounds/8.0/package.json +0 -10
  92. package/playgrounds/8.0/readme.md +0 -17
  93. package/playgrounds/8.0/subDir/sub.js +0 -11
  94. package/playgrounds/8.0/subDir/test.jsx +0 -10
  95. package/playgrounds/8.0/test.ipynb +0 -49
  96. package/playgrounds/8.0/test.js +0 -3
  97. package/playgrounds/8.0/test.sh +0 -1
  98. package/playgrounds/8.0/test.vue +0 -33
  99. package/playgrounds/8.0/test2.html +0 -8
  100. package/playgrounds/9.0/flat/.vscode/settings.json +0 -3
  101. package/playgrounds/9.0/flat/app.js +0 -12
  102. package/playgrounds/9.0/flat/dist/ignore.js +0 -12
  103. package/playgrounds/9.0/flat/eslint.config.js +0 -61
  104. package/playgrounds/9.0/flat/package-lock.json +0 -1053
  105. package/playgrounds/9.0/flat/package.json +0 -9
  106. package/playgrounds/9.0/rc/.eslintrc.json +0 -57
  107. package/playgrounds/9.0/rc/.vscode/settings.json +0 -3
  108. package/playgrounds/9.0/rc/app.js +0 -12
  109. package/playgrounds/9.0/rc/package-lock.json +0 -1345
  110. package/playgrounds/9.0/rc/package.json +0 -9
  111. package/playgrounds/flat-config/.vscode/settings.json +0 -22
  112. package/playgrounds/flat-config/app.js +0 -12
  113. package/playgrounds/flat-config/eslint.config.js +0 -51
  114. package/playgrounds/flat-config/package-lock.json +0 -2733
  115. package/playgrounds/flat-config/package.json +0 -12
  116. package/playgrounds/flat-config/sub/sub.js +0 -2
  117. package/playgrounds/flat-config/test.ts +0 -7
  118. package/playgrounds/flat-config/tsconfig.json +0 -11
  119. package/playgrounds/flat-config-fail/f1/app.js +0 -12
  120. package/playgrounds/flat-config-fail/f1/eslint.config.js +0 -51
  121. package/playgrounds/flat-config-fail/package-lock.json +0 -1683
  122. package/playgrounds/flat-config-fail/package.json +0 -11
  123. package/playgrounds/flat-config-mjs/.vscode/settings.json +0 -21
  124. package/playgrounds/flat-config-mjs/app.js +0 -12
  125. package/playgrounds/flat-config-mjs/eslint.config.mjs +0 -53
  126. package/playgrounds/flat-config-mjs/package-lock.json +0 -2860
  127. package/playgrounds/flat-config-mjs/package.json +0 -11
  128. package/playgrounds/flat-config-mjs/sub/sub.js +0 -2
  129. package/playgrounds/flat-config-mjs/test.ts +0 -7
  130. package/playgrounds/flat-config-mjs/tsconfig.json +0 -11
  131. package/playgrounds/load-eslint/.vscode/settings.json +0 -21
  132. package/playgrounds/load-eslint/app.js +0 -12
  133. package/playgrounds/load-eslint/eslint.config.js +0 -51
  134. package/playgrounds/load-eslint/package-lock.json +0 -2860
  135. package/playgrounds/load-eslint/package.json +0 -11
  136. package/playgrounds/load-eslint/sub/sub.js +0 -2
  137. package/playgrounds/load-eslint/test.ts +0 -7
  138. package/playgrounds/load-eslint/tsconfig.json +0 -11
  139. package/playgrounds/noLib/test.js +0 -22
  140. package/playgrounds/noWD/.vscode/settings.json +0 -2
  141. package/playgrounds/noWD/src/.eslintrc.json +0 -18
  142. package/playgrounds/noWD/src/package-lock.json +0 -2812
  143. package/playgrounds/noWD/src/package.json +0 -12
  144. package/playgrounds/noWD/src/test.js +0 -3
  145. package/playgrounds/notebooks/notebook.ipynb +0 -7072
  146. package/playgrounds/notebooks/notebook2.ipynb +0 -20
  147. package/playgrounds/testing.code-workspace +0 -28
  148. package/playgrounds/ts/.eslintrc.base.json +0 -23
  149. package/playgrounds/ts/.eslintrc.json +0 -191
  150. package/playgrounds/ts/.vscode/settings.json +0 -12
  151. package/playgrounds/ts/package-lock.json +0 -2687
  152. package/playgrounds/ts/package.json +0 -11
  153. package/playgrounds/ts/test copy.ts +0 -4
  154. package/playgrounds/ts/test.ipynb +0 -49
  155. package/playgrounds/ts/test.ts +0 -4
  156. package/playgrounds/ts/test.tsx +0 -14
  157. package/playgrounds/ts/tsconfig.json +0 -100
  158. package/server/agents.md +0 -9
  159. package/server/package-lock.json +0 -93
  160. package/server/package.json +0 -32
  161. package/server/src/diff.ts +0 -1079
  162. package/server/src/eslint.ts +0 -1471
  163. package/server/src/eslintServer.ts +0 -865
  164. package/server/src/is.ts +0 -18
  165. package/server/src/languageDefaults.ts +0 -40
  166. package/server/src/linkedMap.ts +0 -448
  167. package/server/src/paths.ts +0 -128
  168. package/server/src/thenable.d.ts +0 -5
  169. package/server/tsconfig.json +0 -21
  170. package/server/webpack.config.js +0 -25
  171. package/shared.webpack.config.js +0 -59
  172. package/tsconfig.base.json +0 -9
  173. 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}`);