codeActionModel.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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 __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
  6. if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
  7. if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
  8. return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
  9. };
  10. var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
  11. if (kind === "m") throw new TypeError("Private method is not writable");
  12. if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
  13. if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
  14. return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
  15. };
  16. var _CodeActionModel_isDisposed;
  17. import { createCancelablePromise, TimeoutTimer } from '../../../base/common/async.js';
  18. import { isPromiseCanceledError } from '../../../base/common/errors.js';
  19. import { Emitter } from '../../../base/common/event.js';
  20. import { Disposable, MutableDisposable } from '../../../base/common/lifecycle.js';
  21. import { isEqual } from '../../../base/common/resources.js';
  22. import { Range } from '../../common/core/range.js';
  23. import { CodeActionProviderRegistry } from '../../common/modes.js';
  24. import { RawContextKey } from '../../../platform/contextkey/common/contextkey.js';
  25. import { Progress } from '../../../platform/progress/common/progress.js';
  26. import { getCodeActions } from './codeAction.js';
  27. export const SUPPORTED_CODE_ACTIONS = new RawContextKey('supportedCodeAction', '');
  28. class CodeActionOracle extends Disposable {
  29. constructor(_editor, _markerService, _signalChange, _delay = 250) {
  30. super();
  31. this._editor = _editor;
  32. this._markerService = _markerService;
  33. this._signalChange = _signalChange;
  34. this._delay = _delay;
  35. this._autoTriggerTimer = this._register(new TimeoutTimer());
  36. this._register(this._markerService.onMarkerChanged(e => this._onMarkerChanges(e)));
  37. this._register(this._editor.onDidChangeCursorPosition(() => this._onCursorChange()));
  38. }
  39. trigger(trigger) {
  40. const selection = this._getRangeOfSelectionUnlessWhitespaceEnclosed(trigger);
  41. return this._createEventAndSignalChange(trigger, selection);
  42. }
  43. _onMarkerChanges(resources) {
  44. const model = this._editor.getModel();
  45. if (!model) {
  46. return;
  47. }
  48. if (resources.some(resource => isEqual(resource, model.uri))) {
  49. this._autoTriggerTimer.cancelAndSet(() => {
  50. this.trigger({ type: 2 /* Auto */ });
  51. }, this._delay);
  52. }
  53. }
  54. _onCursorChange() {
  55. this._autoTriggerTimer.cancelAndSet(() => {
  56. this.trigger({ type: 2 /* Auto */ });
  57. }, this._delay);
  58. }
  59. _getRangeOfMarker(selection) {
  60. const model = this._editor.getModel();
  61. if (!model) {
  62. return undefined;
  63. }
  64. for (const marker of this._markerService.read({ resource: model.uri })) {
  65. const markerRange = model.validateRange(marker);
  66. if (Range.intersectRanges(markerRange, selection)) {
  67. return Range.lift(markerRange);
  68. }
  69. }
  70. return undefined;
  71. }
  72. _getRangeOfSelectionUnlessWhitespaceEnclosed(trigger) {
  73. if (!this._editor.hasModel()) {
  74. return undefined;
  75. }
  76. const model = this._editor.getModel();
  77. const selection = this._editor.getSelection();
  78. if (selection.isEmpty() && trigger.type === 2 /* Auto */) {
  79. const { lineNumber, column } = selection.getPosition();
  80. const line = model.getLineContent(lineNumber);
  81. if (line.length === 0) {
  82. // empty line
  83. return undefined;
  84. }
  85. else if (column === 1) {
  86. // look only right
  87. if (/\s/.test(line[0])) {
  88. return undefined;
  89. }
  90. }
  91. else if (column === model.getLineMaxColumn(lineNumber)) {
  92. // look only left
  93. if (/\s/.test(line[line.length - 1])) {
  94. return undefined;
  95. }
  96. }
  97. else {
  98. // look left and right
  99. if (/\s/.test(line[column - 2]) && /\s/.test(line[column - 1])) {
  100. return undefined;
  101. }
  102. }
  103. }
  104. return selection;
  105. }
  106. _createEventAndSignalChange(trigger, selection) {
  107. const model = this._editor.getModel();
  108. if (!selection || !model) {
  109. // cancel
  110. this._signalChange(undefined);
  111. return undefined;
  112. }
  113. const markerRange = this._getRangeOfMarker(selection);
  114. const position = markerRange ? markerRange.getStartPosition() : selection.getStartPosition();
  115. const e = {
  116. trigger,
  117. selection,
  118. position
  119. };
  120. this._signalChange(e);
  121. return e;
  122. }
  123. }
  124. export var CodeActionsState;
  125. (function (CodeActionsState) {
  126. CodeActionsState.Empty = { type: 0 /* Empty */ };
  127. class Triggered {
  128. constructor(trigger, rangeOrSelection, position, _cancellablePromise) {
  129. this.trigger = trigger;
  130. this.rangeOrSelection = rangeOrSelection;
  131. this.position = position;
  132. this._cancellablePromise = _cancellablePromise;
  133. this.type = 1 /* Triggered */;
  134. this.actions = _cancellablePromise.catch((e) => {
  135. if (isPromiseCanceledError(e)) {
  136. return emptyCodeActionSet;
  137. }
  138. throw e;
  139. });
  140. }
  141. cancel() {
  142. this._cancellablePromise.cancel();
  143. }
  144. }
  145. CodeActionsState.Triggered = Triggered;
  146. })(CodeActionsState || (CodeActionsState = {}));
  147. const emptyCodeActionSet = {
  148. allActions: [],
  149. validActions: [],
  150. dispose: () => { },
  151. documentation: [],
  152. hasAutoFix: false
  153. };
  154. export class CodeActionModel extends Disposable {
  155. constructor(_editor, _markerService, contextKeyService, _progressService) {
  156. super();
  157. this._editor = _editor;
  158. this._markerService = _markerService;
  159. this._progressService = _progressService;
  160. this._codeActionOracle = this._register(new MutableDisposable());
  161. this._state = CodeActionsState.Empty;
  162. this._onDidChangeState = this._register(new Emitter());
  163. this.onDidChangeState = this._onDidChangeState.event;
  164. _CodeActionModel_isDisposed.set(this, false);
  165. this._supportedCodeActions = SUPPORTED_CODE_ACTIONS.bindTo(contextKeyService);
  166. this._register(this._editor.onDidChangeModel(() => this._update()));
  167. this._register(this._editor.onDidChangeModelLanguage(() => this._update()));
  168. this._register(CodeActionProviderRegistry.onDidChange(() => this._update()));
  169. this._update();
  170. }
  171. dispose() {
  172. if (__classPrivateFieldGet(this, _CodeActionModel_isDisposed, "f")) {
  173. return;
  174. }
  175. __classPrivateFieldSet(this, _CodeActionModel_isDisposed, true, "f");
  176. super.dispose();
  177. this.setState(CodeActionsState.Empty, true);
  178. }
  179. _update() {
  180. if (__classPrivateFieldGet(this, _CodeActionModel_isDisposed, "f")) {
  181. return;
  182. }
  183. this._codeActionOracle.value = undefined;
  184. this.setState(CodeActionsState.Empty);
  185. const model = this._editor.getModel();
  186. if (model
  187. && CodeActionProviderRegistry.has(model)
  188. && !this._editor.getOption(80 /* readOnly */)) {
  189. const supportedActions = [];
  190. for (const provider of CodeActionProviderRegistry.all(model)) {
  191. if (Array.isArray(provider.providedCodeActionKinds)) {
  192. supportedActions.push(...provider.providedCodeActionKinds);
  193. }
  194. }
  195. this._supportedCodeActions.set(supportedActions.join(' '));
  196. this._codeActionOracle.value = new CodeActionOracle(this._editor, this._markerService, trigger => {
  197. var _a;
  198. if (!trigger) {
  199. this.setState(CodeActionsState.Empty);
  200. return;
  201. }
  202. const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token));
  203. if (trigger.trigger.type === 1 /* Invoke */) {
  204. (_a = this._progressService) === null || _a === void 0 ? void 0 : _a.showWhile(actions, 250);
  205. }
  206. this.setState(new CodeActionsState.Triggered(trigger.trigger, trigger.selection, trigger.position, actions));
  207. }, undefined);
  208. this._codeActionOracle.value.trigger({ type: 2 /* Auto */ });
  209. }
  210. else {
  211. this._supportedCodeActions.reset();
  212. }
  213. }
  214. trigger(trigger) {
  215. if (this._codeActionOracle.value) {
  216. this._codeActionOracle.value.trigger(trigger);
  217. }
  218. }
  219. setState(newState, skipNotify) {
  220. if (newState === this._state) {
  221. return;
  222. }
  223. // Cancel old request
  224. if (this._state.type === 1 /* Triggered */) {
  225. this._state.cancel();
  226. }
  227. this._state = newState;
  228. if (!skipNotify && !__classPrivateFieldGet(this, _CodeActionModel_isDisposed, "f")) {
  229. this._onDidChangeState.fire(newState);
  230. }
  231. }
  232. }
  233. _CodeActionModel_isDisposed = new WeakMap();