/standalone-packages/vscode-extensions/out/extensions/typescript-language-features/out/features/quickFix.js

https://github.com/codesandbox/codesandbox-client · JavaScript · 262 lines · 233 code · 0 blank · 29 comment · 32 complexity · c48e753841c6ff0ef8acdca90a2ad576 MD5 · raw file

  1. "use strict";
  2. /*---------------------------------------------------------------------------------------------
  3. * Copyright (c) Microsoft Corporation. All rights reserved.
  4. * Licensed under the MIT License. See License.txt in the project root for license information.
  5. *--------------------------------------------------------------------------------------------*/
  6. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  7. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  8. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  9. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  10. return c > 3 && r && Object.defineProperty(target, key, r), r;
  11. };
  12. Object.defineProperty(exports, "__esModule", { value: true });
  13. const vscode = require("vscode");
  14. const nls = require("vscode-nls");
  15. const api_1 = require("../utils/api");
  16. const cancellation_1 = require("../utils/cancellation");
  17. const codeAction_1 = require("../utils/codeAction");
  18. const dependentRegistration_1 = require("../utils/dependentRegistration");
  19. const memoize_1 = require("../utils/memoize");
  20. const typeConverters = require("../utils/typeConverters");
  21. const localize = nls.loadMessageBundle();
  22. class ApplyCodeActionCommand {
  23. constructor(client, telemetryReporter) {
  24. this.client = client;
  25. this.telemetryReporter = telemetryReporter;
  26. this.id = ApplyCodeActionCommand.ID;
  27. }
  28. async execute(action) {
  29. /* __GDPR__
  30. "quickFix.execute" : {
  31. "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
  32. "${include}": [
  33. "${TypeScriptCommonProperties}"
  34. ]
  35. }
  36. */
  37. this.telemetryReporter.logTelemetry('quickFix.execute', {
  38. fixName: action.fixName
  39. });
  40. return codeAction_1.applyCodeActionCommands(this.client, action.commands, cancellation_1.nulToken);
  41. }
  42. }
  43. ApplyCodeActionCommand.ID = '_typescript.applyCodeActionCommand';
  44. class ApplyFixAllCodeAction {
  45. constructor(client, telemetryReporter) {
  46. this.client = client;
  47. this.telemetryReporter = telemetryReporter;
  48. this.id = ApplyFixAllCodeAction.ID;
  49. }
  50. async execute(file, tsAction) {
  51. if (!tsAction.fixId) {
  52. return;
  53. }
  54. /* __GDPR__
  55. "quickFixAll.execute" : {
  56. "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
  57. "${include}": [
  58. "${TypeScriptCommonProperties}"
  59. ]
  60. }
  61. */
  62. this.telemetryReporter.logTelemetry('quickFixAll.execute', {
  63. fixName: tsAction.fixName
  64. });
  65. const args = {
  66. scope: {
  67. type: 'file',
  68. args: { file }
  69. },
  70. fixId: tsAction.fixId,
  71. };
  72. const response = await this.client.execute('getCombinedCodeFix', args, cancellation_1.nulToken);
  73. if (response.type !== 'response' || !response.body) {
  74. return undefined;
  75. }
  76. const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes);
  77. await vscode.workspace.applyEdit(edit);
  78. await codeAction_1.applyCodeActionCommands(this.client, response.body.commands, cancellation_1.nulToken);
  79. }
  80. }
  81. ApplyFixAllCodeAction.ID = '_typescript.applyFixAllCodeAction';
  82. /**
  83. * Unique set of diagnostics keyed on diagnostic range and error code.
  84. */
  85. class DiagnosticsSet {
  86. constructor(_values) {
  87. this._values = _values;
  88. }
  89. static from(diagnostics) {
  90. const values = new Map();
  91. for (const diagnostic of diagnostics) {
  92. values.set(DiagnosticsSet.key(diagnostic), diagnostic);
  93. }
  94. return new DiagnosticsSet(values);
  95. }
  96. static key(diagnostic) {
  97. const { start, end } = diagnostic.range;
  98. return `${diagnostic.code}-${start.line},${start.character}-${end.line},${end.character}`;
  99. }
  100. get values() {
  101. return this._values.values();
  102. }
  103. get size() {
  104. return this._values.size;
  105. }
  106. }
  107. class CodeActionSet {
  108. constructor() {
  109. this._actions = new Set();
  110. this._fixAllActions = new Map();
  111. }
  112. get values() {
  113. return this._actions;
  114. }
  115. addAction(action) {
  116. this._actions.add(action);
  117. }
  118. addFixAllAction(fixId, action) {
  119. const existing = this._fixAllActions.get(fixId);
  120. if (existing) {
  121. // reinsert action at back of actions list
  122. this._actions.delete(existing);
  123. }
  124. this.addAction(action);
  125. this._fixAllActions.set(fixId, action);
  126. }
  127. hasFixAllAction(fixId) {
  128. return this._fixAllActions.has(fixId);
  129. }
  130. }
  131. class SupportedCodeActionProvider {
  132. constructor(client) {
  133. this.client = client;
  134. }
  135. async getFixableDiagnosticsForContext(context) {
  136. const fixableCodes = await this.fixableDiagnosticCodes;
  137. return DiagnosticsSet.from(context.diagnostics.filter(diagnostic => typeof diagnostic.code !== 'undefined' && fixableCodes.has(diagnostic.code + '')));
  138. }
  139. get fixableDiagnosticCodes() {
  140. return this.client.execute('getSupportedCodeFixes', null, cancellation_1.nulToken)
  141. .then(response => response.type === 'response' ? response.body || [] : [])
  142. .then(codes => new Set(codes));
  143. }
  144. }
  145. __decorate([
  146. memoize_1.memoize
  147. ], SupportedCodeActionProvider.prototype, "fixableDiagnosticCodes", null);
  148. class TypeScriptQuickFixProvider {
  149. constructor(client, formattingConfigurationManager, commandManager, diagnosticsManager, telemetryReporter) {
  150. this.client = client;
  151. this.formattingConfigurationManager = formattingConfigurationManager;
  152. this.diagnosticsManager = diagnosticsManager;
  153. commandManager.register(new ApplyCodeActionCommand(client, telemetryReporter));
  154. commandManager.register(new ApplyFixAllCodeAction(client, telemetryReporter));
  155. this.supportedCodeActionProvider = new SupportedCodeActionProvider(client);
  156. }
  157. async provideCodeActions(document, _range, context, token) {
  158. const file = this.client.toOpenedFilePath(document);
  159. if (!file) {
  160. return [];
  161. }
  162. const fixableDiagnostics = await this.supportedCodeActionProvider.getFixableDiagnosticsForContext(context);
  163. if (!fixableDiagnostics.size) {
  164. return [];
  165. }
  166. if (this.client.bufferSyncSupport.hasPendingDiagnostics(document.uri)) {
  167. return [];
  168. }
  169. await this.formattingConfigurationManager.ensureConfigurationForDocument(document, token);
  170. const results = new CodeActionSet();
  171. for (const diagnostic of fixableDiagnostics.values) {
  172. await this.getFixesForDiagnostic(document, file, diagnostic, results, token);
  173. }
  174. return Array.from(results.values);
  175. }
  176. async getFixesForDiagnostic(document, file, diagnostic, results, token) {
  177. const args = {
  178. ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range),
  179. errorCodes: [+(diagnostic.code)]
  180. };
  181. const response = await this.client.execute('getCodeFixes', args, token);
  182. if (response.type !== 'response' || !response.body) {
  183. return results;
  184. }
  185. for (const tsCodeFix of response.body) {
  186. this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix);
  187. }
  188. return results;
  189. }
  190. addAllFixesForTsCodeAction(results, document, file, diagnostic, tsAction) {
  191. results.addAction(this.getSingleFixForTsCodeAction(diagnostic, tsAction));
  192. this.addFixAllForTsCodeAction(results, document, file, diagnostic, tsAction);
  193. return results;
  194. }
  195. getSingleFixForTsCodeAction(diagnostic, tsAction) {
  196. const codeAction = new vscode.CodeAction(tsAction.description, vscode.CodeActionKind.QuickFix);
  197. codeAction.edit = codeAction_1.getEditForCodeAction(this.client, tsAction);
  198. codeAction.diagnostics = [diagnostic];
  199. codeAction.command = {
  200. command: ApplyCodeActionCommand.ID,
  201. arguments: [tsAction],
  202. title: ''
  203. };
  204. codeAction.isPreferred = isPreferredFix(tsAction);
  205. return codeAction;
  206. }
  207. addFixAllForTsCodeAction(results, document, file, diagnostic, tsAction) {
  208. if (!tsAction.fixId || this.client.apiVersion.lt(api_1.default.v270) || results.hasFixAllAction(tsAction.fixId)) {
  209. return results;
  210. }
  211. // Make sure there are multiple diagnostics of the same type in the file
  212. if (!this.diagnosticsManager.getDiagnostics(document.uri).some(x => {
  213. if (x === diagnostic) {
  214. return false;
  215. }
  216. return x.code === diagnostic.code
  217. || (fixAllErrorCodes.has(x.code) && fixAllErrorCodes.get(x.code) === fixAllErrorCodes.get(diagnostic.code));
  218. })) {
  219. return results;
  220. }
  221. const action = new vscode.CodeAction(tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), vscode.CodeActionKind.QuickFix);
  222. action.diagnostics = [diagnostic];
  223. action.command = {
  224. command: ApplyFixAllCodeAction.ID,
  225. arguments: [file, tsAction],
  226. title: ''
  227. };
  228. results.addFixAllAction(tsAction.fixId, action);
  229. return results;
  230. }
  231. }
  232. TypeScriptQuickFixProvider.minVersion = api_1.default.v213;
  233. TypeScriptQuickFixProvider.metadata = {
  234. providedCodeActionKinds: [vscode.CodeActionKind.QuickFix]
  235. };
  236. // Some fix all actions can actually fix multiple differnt diagnostics. Make sure we still show the fix all action
  237. // in such cases
  238. const fixAllErrorCodes = new Map([
  239. // Missing async
  240. [2339, 2339],
  241. [2345, 2339],
  242. ]);
  243. const preferredFixes = new Set([
  244. 'annotateWithTypeFromJSDoc',
  245. 'constructorForDerivedNeedSuperCall',
  246. 'extendsInterfaceBecomesImplements',
  247. 'fixAwaitInSyncFunction',
  248. 'fixClassIncorrectlyImplementsInterface',
  249. 'fixUnreachableCode',
  250. 'forgottenThisPropertyAccess',
  251. 'spelling',
  252. 'unusedIdentifier',
  253. 'addMissingAwait',
  254. ]);
  255. function isPreferredFix(tsAction) {
  256. return preferredFixes.has(tsAction.fixName);
  257. }
  258. function register(selector, client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter) {
  259. return new dependentRegistration_1.VersionDependentRegistration(client, TypeScriptQuickFixProvider.minVersion, () => vscode.languages.registerCodeActionsProvider(selector, new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), TypeScriptQuickFixProvider.metadata));
  260. }
  261. exports.register = register;
  262. //# sourceMappingURL=quickFix.js.map