79d1e07bf8b0c98f3bdceece2d0aac4517b13d02eb43536e7e26d2886df266d64a83dfb482fbea0f0c717b7afcfd4e8d4b5e7413aad7a28e1ef91f0dc94b50 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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 { isNonEmptyArray } from '../../../../base/common/arrays.js';
  24. import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
  25. import { onUnexpectedError } from '../../../../base/common/errors.js';
  26. import { KeyChord } from '../../../../base/common/keyCodes.js';
  27. import { DisposableStore } from '../../../../base/common/lifecycle.js';
  28. import { EditorAction, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js';
  29. import { ICodeEditorService } from '../../../browser/services/codeEditorService.js';
  30. import { CharacterSet } from '../../../common/core/characterClassifier.js';
  31. import { Range } from '../../../common/core/range.js';
  32. import { EditorContextKeys } from '../../../common/editorContextKeys.js';
  33. import { IEditorWorkerService } from '../../../common/services/editorWorker.js';
  34. import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
  35. import { alertFormattingEdits, formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, getOnTypeFormattingEdits } from './format.js';
  36. import { FormattingEdit } from './formattingEdit.js';
  37. import * as nls from '../../../../nls.js';
  38. import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
  39. import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
  40. import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
  41. import { IEditorProgressService, Progress } from '../../../../platform/progress/common/progress.js';
  42. let FormatOnType = class FormatOnType {
  43. constructor(_editor, _languageFeaturesService, _workerService) {
  44. this._editor = _editor;
  45. this._languageFeaturesService = _languageFeaturesService;
  46. this._workerService = _workerService;
  47. this._disposables = new DisposableStore();
  48. this._sessionDisposables = new DisposableStore();
  49. this._disposables.add(_languageFeaturesService.onTypeFormattingEditProvider.onDidChange(this._update, this));
  50. this._disposables.add(_editor.onDidChangeModel(() => this._update()));
  51. this._disposables.add(_editor.onDidChangeModelLanguage(() => this._update()));
  52. this._disposables.add(_editor.onDidChangeConfiguration(e => {
  53. if (e.hasChanged(51 /* EditorOption.formatOnType */)) {
  54. this._update();
  55. }
  56. }));
  57. }
  58. dispose() {
  59. this._disposables.dispose();
  60. this._sessionDisposables.dispose();
  61. }
  62. _update() {
  63. // clean up
  64. this._sessionDisposables.clear();
  65. // we are disabled
  66. if (!this._editor.getOption(51 /* EditorOption.formatOnType */)) {
  67. return;
  68. }
  69. // no model
  70. if (!this._editor.hasModel()) {
  71. return;
  72. }
  73. const model = this._editor.getModel();
  74. // no support
  75. const [support] = this._languageFeaturesService.onTypeFormattingEditProvider.ordered(model);
  76. if (!support || !support.autoFormatTriggerCharacters) {
  77. return;
  78. }
  79. // register typing listeners that will trigger the format
  80. const triggerChars = new CharacterSet();
  81. for (const ch of support.autoFormatTriggerCharacters) {
  82. triggerChars.add(ch.charCodeAt(0));
  83. }
  84. this._sessionDisposables.add(this._editor.onDidType((text) => {
  85. const lastCharCode = text.charCodeAt(text.length - 1);
  86. if (triggerChars.has(lastCharCode)) {
  87. this._trigger(String.fromCharCode(lastCharCode));
  88. }
  89. }));
  90. }
  91. _trigger(ch) {
  92. if (!this._editor.hasModel()) {
  93. return;
  94. }
  95. if (this._editor.getSelections().length > 1 || !this._editor.getSelection().isEmpty()) {
  96. return;
  97. }
  98. const model = this._editor.getModel();
  99. const position = this._editor.getPosition();
  100. const cts = new CancellationTokenSource();
  101. // install a listener that checks if edits happens before the
  102. // position on which we format right now. If so, we won't
  103. // apply the format edits
  104. const unbind = this._editor.onDidChangeModelContent((e) => {
  105. if (e.isFlush) {
  106. // a model.setValue() was called
  107. // cancel only once
  108. cts.cancel();
  109. unbind.dispose();
  110. return;
  111. }
  112. for (let i = 0, len = e.changes.length; i < len; i++) {
  113. const change = e.changes[i];
  114. if (change.range.endLineNumber <= position.lineNumber) {
  115. // cancel only once
  116. cts.cancel();
  117. unbind.dispose();
  118. return;
  119. }
  120. }
  121. });
  122. getOnTypeFormattingEdits(this._workerService, this._languageFeaturesService, model, position, ch, model.getFormattingOptions(), cts.token).then(edits => {
  123. if (cts.token.isCancellationRequested) {
  124. return;
  125. }
  126. if (isNonEmptyArray(edits)) {
  127. FormattingEdit.execute(this._editor, edits, true);
  128. alertFormattingEdits(edits);
  129. }
  130. }).finally(() => {
  131. unbind.dispose();
  132. });
  133. }
  134. };
  135. FormatOnType.ID = 'editor.contrib.autoFormat';
  136. FormatOnType = __decorate([
  137. __param(1, ILanguageFeaturesService),
  138. __param(2, IEditorWorkerService)
  139. ], FormatOnType);
  140. let FormatOnPaste = class FormatOnPaste {
  141. constructor(editor, _languageFeaturesService, _instantiationService) {
  142. this.editor = editor;
  143. this._languageFeaturesService = _languageFeaturesService;
  144. this._instantiationService = _instantiationService;
  145. this._callOnDispose = new DisposableStore();
  146. this._callOnModel = new DisposableStore();
  147. this._callOnDispose.add(editor.onDidChangeConfiguration(() => this._update()));
  148. this._callOnDispose.add(editor.onDidChangeModel(() => this._update()));
  149. this._callOnDispose.add(editor.onDidChangeModelLanguage(() => this._update()));
  150. this._callOnDispose.add(_languageFeaturesService.documentRangeFormattingEditProvider.onDidChange(this._update, this));
  151. }
  152. dispose() {
  153. this._callOnDispose.dispose();
  154. this._callOnModel.dispose();
  155. }
  156. _update() {
  157. // clean up
  158. this._callOnModel.clear();
  159. // we are disabled
  160. if (!this.editor.getOption(50 /* EditorOption.formatOnPaste */)) {
  161. return;
  162. }
  163. // no model
  164. if (!this.editor.hasModel()) {
  165. return;
  166. }
  167. // no formatter
  168. if (!this._languageFeaturesService.documentRangeFormattingEditProvider.has(this.editor.getModel())) {
  169. return;
  170. }
  171. this._callOnModel.add(this.editor.onDidPaste(({ range }) => this._trigger(range)));
  172. }
  173. _trigger(range) {
  174. if (!this.editor.hasModel()) {
  175. return;
  176. }
  177. if (this.editor.getSelections().length > 1) {
  178. return;
  179. }
  180. this._instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, this.editor, range, 2 /* FormattingMode.Silent */, Progress.None, CancellationToken.None).catch(onUnexpectedError);
  181. }
  182. };
  183. FormatOnPaste.ID = 'editor.contrib.formatOnPaste';
  184. FormatOnPaste = __decorate([
  185. __param(1, ILanguageFeaturesService),
  186. __param(2, IInstantiationService)
  187. ], FormatOnPaste);
  188. class FormatDocumentAction extends EditorAction {
  189. constructor() {
  190. super({
  191. id: 'editor.action.formatDocument',
  192. label: nls.localize('formatDocument.label', "Format Document"),
  193. alias: 'Format Document',
  194. precondition: ContextKeyExpr.and(EditorContextKeys.notInCompositeEditor, EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider),
  195. kbOpts: {
  196. kbExpr: EditorContextKeys.editorTextFocus,
  197. primary: 1024 /* KeyMod.Shift */ | 512 /* KeyMod.Alt */ | 36 /* KeyCode.KeyF */,
  198. linux: { primary: 2048 /* KeyMod.CtrlCmd */ | 1024 /* KeyMod.Shift */ | 39 /* KeyCode.KeyI */ },
  199. weight: 100 /* KeybindingWeight.EditorContrib */
  200. },
  201. contextMenuOpts: {
  202. group: '1_modification',
  203. order: 1.3
  204. }
  205. });
  206. }
  207. run(accessor, editor) {
  208. return __awaiter(this, void 0, void 0, function* () {
  209. if (editor.hasModel()) {
  210. const instaService = accessor.get(IInstantiationService);
  211. const progressService = accessor.get(IEditorProgressService);
  212. yield progressService.showWhile(instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, 1 /* FormattingMode.Explicit */, Progress.None, CancellationToken.None), 250);
  213. }
  214. });
  215. }
  216. }
  217. class FormatSelectionAction extends EditorAction {
  218. constructor() {
  219. super({
  220. id: 'editor.action.formatSelection',
  221. label: nls.localize('formatSelection.label', "Format Selection"),
  222. alias: 'Format Selection',
  223. precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider),
  224. kbOpts: {
  225. kbExpr: EditorContextKeys.editorTextFocus,
  226. primary: KeyChord(2048 /* KeyMod.CtrlCmd */ | 41 /* KeyCode.KeyK */, 2048 /* KeyMod.CtrlCmd */ | 36 /* KeyCode.KeyF */),
  227. weight: 100 /* KeybindingWeight.EditorContrib */
  228. },
  229. contextMenuOpts: {
  230. when: EditorContextKeys.hasNonEmptySelection,
  231. group: '1_modification',
  232. order: 1.31
  233. }
  234. });
  235. }
  236. run(accessor, editor) {
  237. return __awaiter(this, void 0, void 0, function* () {
  238. if (!editor.hasModel()) {
  239. return;
  240. }
  241. const instaService = accessor.get(IInstantiationService);
  242. const model = editor.getModel();
  243. const ranges = editor.getSelections().map(range => {
  244. return range.isEmpty()
  245. ? new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber))
  246. : range;
  247. });
  248. const progressService = accessor.get(IEditorProgressService);
  249. yield progressService.showWhile(instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, ranges, 1 /* FormattingMode.Explicit */, Progress.None, CancellationToken.None), 250);
  250. });
  251. }
  252. }
  253. registerEditorContribution(FormatOnType.ID, FormatOnType);
  254. registerEditorContribution(FormatOnPaste.ID, FormatOnPaste);
  255. registerEditorAction(FormatDocumentAction);
  256. registerEditorAction(FormatSelectionAction);
  257. // this is the old format action that does both (format document OR format selection)
  258. // and we keep it here such that existing keybinding configurations etc will still work
  259. CommandsRegistry.registerCommand('editor.action.format', (accessor) => __awaiter(void 0, void 0, void 0, function* () {
  260. const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
  261. if (!editor || !editor.hasModel()) {
  262. return;
  263. }
  264. const commandService = accessor.get(ICommandService);
  265. if (editor.getSelection().isEmpty()) {
  266. yield commandService.executeCommand('editor.action.formatDocument');
  267. }
  268. else {
  269. yield commandService.executeCommand('editor.action.formatSelection');
  270. }
  271. }));