codeActionCommands.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. 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;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  15. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  16. return new (P || (P = Promise))(function (resolve, reject) {
  17. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  18. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  19. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  20. step((generator = generator.apply(thisArg, _arguments || [])).next());
  21. });
  22. };
  23. import { CancellationToken } from '../../../base/common/cancellation.js';
  24. import { Lazy } from '../../../base/common/lazy.js';
  25. import { Disposable } from '../../../base/common/lifecycle.js';
  26. import { escapeRegExpCharacters } from '../../../base/common/strings.js';
  27. import { EditorAction, EditorCommand } from '../../browser/editorExtensions.js';
  28. import { IBulkEditService, ResourceEdit } from '../../browser/services/bulkEditService.js';
  29. import { EditorContextKeys } from '../../common/editorContextKeys.js';
  30. import { codeActionCommandId, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from './codeAction.js';
  31. import { CodeActionUi } from './codeActionUi.js';
  32. import { MessageController } from '../message/messageController.js';
  33. import * as nls from '../../../nls.js';
  34. import { ICommandService } from '../../../platform/commands/common/commands.js';
  35. import { ContextKeyExpr, IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';
  36. import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';
  37. import { IMarkerService } from '../../../platform/markers/common/markers.js';
  38. import { INotificationService } from '../../../platform/notification/common/notification.js';
  39. import { IEditorProgressService } from '../../../platform/progress/common/progress.js';
  40. import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js';
  41. import { CodeActionModel, SUPPORTED_CODE_ACTIONS } from './codeActionModel.js';
  42. import { CodeActionCommandArgs, CodeActionKind } from './types.js';
  43. function contextKeyForSupportedActions(kind) {
  44. return ContextKeyExpr.regex(SUPPORTED_CODE_ACTIONS.keys()[0], new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b'));
  45. }
  46. const argsSchema = {
  47. type: 'object',
  48. defaultSnippets: [{ body: { kind: '' } }],
  49. properties: {
  50. 'kind': {
  51. type: 'string',
  52. description: nls.localize('args.schema.kind', "Kind of the code action to run."),
  53. },
  54. 'apply': {
  55. type: 'string',
  56. description: nls.localize('args.schema.apply', "Controls when the returned actions are applied."),
  57. default: "ifSingle" /* IfSingle */,
  58. enum: ["first" /* First */, "ifSingle" /* IfSingle */, "never" /* Never */],
  59. enumDescriptions: [
  60. nls.localize('args.schema.apply.first', "Always apply the first returned code action."),
  61. nls.localize('args.schema.apply.ifSingle', "Apply the first returned code action if it is the only one."),
  62. nls.localize('args.schema.apply.never', "Do not apply the returned code actions."),
  63. ]
  64. },
  65. 'preferred': {
  66. type: 'boolean',
  67. default: false,
  68. description: nls.localize('args.schema.preferred', "Controls if only preferred code actions should be returned."),
  69. }
  70. }
  71. };
  72. let QuickFixController = class QuickFixController extends Disposable {
  73. constructor(editor, markerService, contextKeyService, progressService, _instantiationService) {
  74. super();
  75. this._instantiationService = _instantiationService;
  76. this._editor = editor;
  77. this._model = this._register(new CodeActionModel(this._editor, markerService, contextKeyService, progressService));
  78. this._register(this._model.onDidChangeState(newState => this.update(newState)));
  79. this._ui = new Lazy(() => this._register(new CodeActionUi(editor, QuickFixAction.Id, AutoFixAction.Id, {
  80. applyCodeAction: (action, retrigger) => __awaiter(this, void 0, void 0, function* () {
  81. try {
  82. yield this._applyCodeAction(action);
  83. }
  84. finally {
  85. if (retrigger) {
  86. this._trigger({ type: 2 /* Auto */, filter: {} });
  87. }
  88. }
  89. })
  90. }, this._instantiationService)));
  91. }
  92. static get(editor) {
  93. return editor.getContribution(QuickFixController.ID);
  94. }
  95. update(newState) {
  96. this._ui.getValue().update(newState);
  97. }
  98. showCodeActions(trigger, actions, at) {
  99. return this._ui.getValue().showCodeActionList(trigger, actions, at, { includeDisabledActions: false });
  100. }
  101. manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply) {
  102. if (!this._editor.hasModel()) {
  103. return;
  104. }
  105. MessageController.get(this._editor).closeMessage();
  106. const triggerPosition = this._editor.getPosition();
  107. this._trigger({ type: 1 /* Invoke */, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
  108. }
  109. _trigger(trigger) {
  110. return this._model.trigger(trigger);
  111. }
  112. _applyCodeAction(action) {
  113. return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor);
  114. }
  115. };
  116. QuickFixController.ID = 'editor.contrib.quickFixController';
  117. QuickFixController = __decorate([
  118. __param(1, IMarkerService),
  119. __param(2, IContextKeyService),
  120. __param(3, IEditorProgressService),
  121. __param(4, IInstantiationService)
  122. ], QuickFixController);
  123. export { QuickFixController };
  124. export function applyCodeAction(accessor, item, editor) {
  125. return __awaiter(this, void 0, void 0, function* () {
  126. const bulkEditService = accessor.get(IBulkEditService);
  127. const commandService = accessor.get(ICommandService);
  128. const telemetryService = accessor.get(ITelemetryService);
  129. const notificationService = accessor.get(INotificationService);
  130. telemetryService.publicLog2('codeAction.applyCodeAction', {
  131. codeActionTitle: item.action.title,
  132. codeActionKind: item.action.kind,
  133. codeActionIsPreferred: !!item.action.isPreferred,
  134. });
  135. yield item.resolve(CancellationToken.None);
  136. if (item.action.edit) {
  137. yield bulkEditService.apply(ResourceEdit.convert(item.action.edit), { editor, label: item.action.title });
  138. }
  139. if (item.action.command) {
  140. try {
  141. yield commandService.executeCommand(item.action.command.id, ...(item.action.command.arguments || []));
  142. }
  143. catch (err) {
  144. const message = asMessage(err);
  145. notificationService.error(typeof message === 'string'
  146. ? message
  147. : nls.localize('applyCodeActionFailed', "An unknown error occurred while applying the code action"));
  148. }
  149. }
  150. });
  151. }
  152. function asMessage(err) {
  153. if (typeof err === 'string') {
  154. return err;
  155. }
  156. else if (err instanceof Error && typeof err.message === 'string') {
  157. return err.message;
  158. }
  159. else {
  160. return undefined;
  161. }
  162. }
  163. function triggerCodeActionsForEditorSelection(editor, notAvailableMessage, filter, autoApply) {
  164. if (editor.hasModel()) {
  165. const controller = QuickFixController.get(editor);
  166. if (controller) {
  167. controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply);
  168. }
  169. }
  170. }
  171. export class QuickFixAction extends EditorAction {
  172. constructor() {
  173. super({
  174. id: QuickFixAction.Id,
  175. label: nls.localize('quickfix.trigger.label', "Quick Fix..."),
  176. alias: 'Quick Fix...',
  177. precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
  178. kbOpts: {
  179. kbExpr: EditorContextKeys.editorTextFocus,
  180. primary: 2048 /* CtrlCmd */ | 84 /* Period */,
  181. weight: 100 /* EditorContrib */
  182. }
  183. });
  184. }
  185. run(_accessor, editor) {
  186. return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), undefined, undefined);
  187. }
  188. }
  189. QuickFixAction.Id = 'editor.action.quickFix';
  190. export class CodeActionCommand extends EditorCommand {
  191. constructor() {
  192. super({
  193. id: codeActionCommandId,
  194. precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
  195. description: {
  196. description: 'Trigger a code action',
  197. args: [{ name: 'args', schema: argsSchema, }]
  198. }
  199. });
  200. }
  201. runEditorCommand(_accessor, editor, userArgs) {
  202. const args = CodeActionCommandArgs.fromUser(userArgs, {
  203. kind: CodeActionKind.Empty,
  204. apply: "ifSingle" /* IfSingle */,
  205. });
  206. return triggerCodeActionsForEditorSelection(editor, typeof (userArgs === null || userArgs === void 0 ? void 0 : userArgs.kind) === 'string'
  207. ? args.preferred
  208. ? nls.localize('editor.action.codeAction.noneMessage.preferred.kind', "No preferred code actions for '{0}' available", userArgs.kind)
  209. : nls.localize('editor.action.codeAction.noneMessage.kind', "No code actions for '{0}' available", userArgs.kind)
  210. : args.preferred
  211. ? nls.localize('editor.action.codeAction.noneMessage.preferred', "No preferred code actions available")
  212. : nls.localize('editor.action.codeAction.noneMessage', "No code actions available"), {
  213. include: args.kind,
  214. includeSourceActions: true,
  215. onlyIncludePreferredActions: args.preferred,
  216. }, args.apply);
  217. }
  218. }
  219. export class RefactorAction extends EditorAction {
  220. constructor() {
  221. super({
  222. id: refactorCommandId,
  223. label: nls.localize('refactor.label', "Refactor..."),
  224. alias: 'Refactor...',
  225. precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
  226. kbOpts: {
  227. kbExpr: EditorContextKeys.editorTextFocus,
  228. primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 48 /* KeyR */,
  229. mac: {
  230. primary: 256 /* WinCtrl */ | 1024 /* Shift */ | 48 /* KeyR */
  231. },
  232. weight: 100 /* EditorContrib */
  233. },
  234. contextMenuOpts: {
  235. group: '1_modification',
  236. order: 2,
  237. when: ContextKeyExpr.and(EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.Refactor)),
  238. },
  239. description: {
  240. description: 'Refactor...',
  241. args: [{ name: 'args', schema: argsSchema }]
  242. }
  243. });
  244. }
  245. run(_accessor, editor, userArgs) {
  246. const args = CodeActionCommandArgs.fromUser(userArgs, {
  247. kind: CodeActionKind.Refactor,
  248. apply: "never" /* Never */
  249. });
  250. return triggerCodeActionsForEditorSelection(editor, typeof (userArgs === null || userArgs === void 0 ? void 0 : userArgs.kind) === 'string'
  251. ? args.preferred
  252. ? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind)
  253. : nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind)
  254. : args.preferred
  255. ? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available")
  256. : nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), {
  257. include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None,
  258. onlyIncludePreferredActions: args.preferred,
  259. }, args.apply);
  260. }
  261. }
  262. export class SourceAction extends EditorAction {
  263. constructor() {
  264. super({
  265. id: sourceActionCommandId,
  266. label: nls.localize('source.label', "Source Action..."),
  267. alias: 'Source Action...',
  268. precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
  269. contextMenuOpts: {
  270. group: '1_modification',
  271. order: 2.1,
  272. when: ContextKeyExpr.and(EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.Source)),
  273. },
  274. description: {
  275. description: 'Source Action...',
  276. args: [{ name: 'args', schema: argsSchema }]
  277. }
  278. });
  279. }
  280. run(_accessor, editor, userArgs) {
  281. const args = CodeActionCommandArgs.fromUser(userArgs, {
  282. kind: CodeActionKind.Source,
  283. apply: "never" /* Never */
  284. });
  285. return triggerCodeActionsForEditorSelection(editor, typeof (userArgs === null || userArgs === void 0 ? void 0 : userArgs.kind) === 'string'
  286. ? args.preferred
  287. ? nls.localize('editor.action.source.noneMessage.preferred.kind', "No preferred source actions for '{0}' available", userArgs.kind)
  288. : nls.localize('editor.action.source.noneMessage.kind', "No source actions for '{0}' available", userArgs.kind)
  289. : args.preferred
  290. ? nls.localize('editor.action.source.noneMessage.preferred', "No preferred source actions available")
  291. : nls.localize('editor.action.source.noneMessage', "No source actions available"), {
  292. include: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.None,
  293. includeSourceActions: true,
  294. onlyIncludePreferredActions: args.preferred,
  295. }, args.apply);
  296. }
  297. }
  298. export class OrganizeImportsAction extends EditorAction {
  299. constructor() {
  300. super({
  301. id: organizeImportsCommandId,
  302. label: nls.localize('organizeImports.label', "Organize Imports"),
  303. alias: 'Organize Imports',
  304. precondition: ContextKeyExpr.and(EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.SourceOrganizeImports)),
  305. kbOpts: {
  306. kbExpr: EditorContextKeys.editorTextFocus,
  307. primary: 1024 /* Shift */ | 512 /* Alt */ | 45 /* KeyO */,
  308. weight: 100 /* EditorContrib */
  309. },
  310. });
  311. }
  312. run(_accessor, editor) {
  313. return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.organize.noneMessage', "No organize imports action available"), { include: CodeActionKind.SourceOrganizeImports, includeSourceActions: true }, "ifSingle" /* IfSingle */);
  314. }
  315. }
  316. export class FixAllAction extends EditorAction {
  317. constructor() {
  318. super({
  319. id: fixAllCommandId,
  320. label: nls.localize('fixAll.label', "Fix All"),
  321. alias: 'Fix All',
  322. precondition: ContextKeyExpr.and(EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.SourceFixAll))
  323. });
  324. }
  325. run(_accessor, editor) {
  326. return triggerCodeActionsForEditorSelection(editor, nls.localize('fixAll.noneMessage', "No fix all action available"), { include: CodeActionKind.SourceFixAll, includeSourceActions: true }, "ifSingle" /* IfSingle */);
  327. }
  328. }
  329. export class AutoFixAction extends EditorAction {
  330. constructor() {
  331. super({
  332. id: AutoFixAction.Id,
  333. label: nls.localize('autoFix.label', "Auto Fix..."),
  334. alias: 'Auto Fix...',
  335. precondition: ContextKeyExpr.and(EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.QuickFix)),
  336. kbOpts: {
  337. kbExpr: EditorContextKeys.editorTextFocus,
  338. primary: 512 /* Alt */ | 1024 /* Shift */ | 84 /* Period */,
  339. mac: {
  340. primary: 2048 /* CtrlCmd */ | 512 /* Alt */ | 84 /* Period */
  341. },
  342. weight: 100 /* EditorContrib */
  343. }
  344. });
  345. }
  346. run(_accessor, editor) {
  347. return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.autoFix.noneMessage', "No auto fixes available"), {
  348. include: CodeActionKind.QuickFix,
  349. onlyIncludePreferredActions: true
  350. }, "ifSingle" /* IfSingle */);
  351. }
  352. }
  353. AutoFixAction.Id = 'editor.action.autoFix';