inPlaceReplace.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. import { createCancelablePromise, timeout } from '../../../base/common/async.js';
  15. import { onUnexpectedError } from '../../../base/common/errors.js';
  16. import { EditorState } from '../../browser/core/editorState.js';
  17. import { EditorAction, registerEditorAction, registerEditorContribution } from '../../browser/editorExtensions.js';
  18. import { Range } from '../../common/core/range.js';
  19. import { Selection } from '../../common/core/selection.js';
  20. import { EditorContextKeys } from '../../common/editorContextKeys.js';
  21. import { ModelDecorationOptions } from '../../common/model/textModel.js';
  22. import { IEditorWorkerService } from '../../common/services/editorWorkerService.js';
  23. import { editorBracketMatchBorder } from '../../common/view/editorColorRegistry.js';
  24. import * as nls from '../../../nls.js';
  25. import { registerThemingParticipant } from '../../../platform/theme/common/themeService.js';
  26. import { InPlaceReplaceCommand } from './inPlaceReplaceCommand.js';
  27. let InPlaceReplaceController = class InPlaceReplaceController {
  28. constructor(editor, editorWorkerService) {
  29. this.decorationIds = [];
  30. this.editor = editor;
  31. this.editorWorkerService = editorWorkerService;
  32. }
  33. static get(editor) {
  34. return editor.getContribution(InPlaceReplaceController.ID);
  35. }
  36. dispose() {
  37. }
  38. run(source, up) {
  39. // cancel any pending request
  40. if (this.currentRequest) {
  41. this.currentRequest.cancel();
  42. }
  43. const editorSelection = this.editor.getSelection();
  44. const model = this.editor.getModel();
  45. if (!model || !editorSelection) {
  46. return undefined;
  47. }
  48. let selection = editorSelection;
  49. if (selection.startLineNumber !== selection.endLineNumber) {
  50. // Can't accept multiline selection
  51. return undefined;
  52. }
  53. const state = new EditorState(this.editor, 1 /* Value */ | 4 /* Position */);
  54. const modelURI = model.uri;
  55. if (!this.editorWorkerService.canNavigateValueSet(modelURI)) {
  56. return Promise.resolve(undefined);
  57. }
  58. this.currentRequest = createCancelablePromise(token => this.editorWorkerService.navigateValueSet(modelURI, selection, up));
  59. return this.currentRequest.then(result => {
  60. if (!result || !result.range || !result.value) {
  61. // No proper result
  62. return;
  63. }
  64. if (!state.validate(this.editor)) {
  65. // state has changed
  66. return;
  67. }
  68. // Selection
  69. let editRange = Range.lift(result.range);
  70. let highlightRange = result.range;
  71. let diff = result.value.length - (selection.endColumn - selection.startColumn);
  72. // highlight
  73. highlightRange = {
  74. startLineNumber: highlightRange.startLineNumber,
  75. startColumn: highlightRange.startColumn,
  76. endLineNumber: highlightRange.endLineNumber,
  77. endColumn: highlightRange.startColumn + result.value.length
  78. };
  79. if (diff > 1) {
  80. selection = new Selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn + diff - 1);
  81. }
  82. // Insert new text
  83. const command = new InPlaceReplaceCommand(editRange, selection, result.value);
  84. this.editor.pushUndoStop();
  85. this.editor.executeCommand(source, command);
  86. this.editor.pushUndoStop();
  87. // add decoration
  88. this.decorationIds = this.editor.deltaDecorations(this.decorationIds, [{
  89. range: highlightRange,
  90. options: InPlaceReplaceController.DECORATION
  91. }]);
  92. // remove decoration after delay
  93. if (this.decorationRemover) {
  94. this.decorationRemover.cancel();
  95. }
  96. this.decorationRemover = timeout(350);
  97. this.decorationRemover.then(() => this.decorationIds = this.editor.deltaDecorations(this.decorationIds, [])).catch(onUnexpectedError);
  98. }).catch(onUnexpectedError);
  99. }
  100. };
  101. InPlaceReplaceController.ID = 'editor.contrib.inPlaceReplaceController';
  102. InPlaceReplaceController.DECORATION = ModelDecorationOptions.register({
  103. description: 'in-place-replace',
  104. className: 'valueSetReplacement'
  105. });
  106. InPlaceReplaceController = __decorate([
  107. __param(1, IEditorWorkerService)
  108. ], InPlaceReplaceController);
  109. class InPlaceReplaceUp extends EditorAction {
  110. constructor() {
  111. super({
  112. id: 'editor.action.inPlaceReplace.up',
  113. label: nls.localize('InPlaceReplaceAction.previous.label', "Replace with Previous Value"),
  114. alias: 'Replace with Previous Value',
  115. precondition: EditorContextKeys.writable,
  116. kbOpts: {
  117. kbExpr: EditorContextKeys.editorTextFocus,
  118. primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 82 /* Comma */,
  119. weight: 100 /* EditorContrib */
  120. }
  121. });
  122. }
  123. run(accessor, editor) {
  124. const controller = InPlaceReplaceController.get(editor);
  125. if (!controller) {
  126. return Promise.resolve(undefined);
  127. }
  128. return controller.run(this.id, true);
  129. }
  130. }
  131. class InPlaceReplaceDown extends EditorAction {
  132. constructor() {
  133. super({
  134. id: 'editor.action.inPlaceReplace.down',
  135. label: nls.localize('InPlaceReplaceAction.next.label', "Replace with Next Value"),
  136. alias: 'Replace with Next Value',
  137. precondition: EditorContextKeys.writable,
  138. kbOpts: {
  139. kbExpr: EditorContextKeys.editorTextFocus,
  140. primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 84 /* Period */,
  141. weight: 100 /* EditorContrib */
  142. }
  143. });
  144. }
  145. run(accessor, editor) {
  146. const controller = InPlaceReplaceController.get(editor);
  147. if (!controller) {
  148. return Promise.resolve(undefined);
  149. }
  150. return controller.run(this.id, false);
  151. }
  152. }
  153. registerEditorContribution(InPlaceReplaceController.ID, InPlaceReplaceController);
  154. registerEditorAction(InPlaceReplaceUp);
  155. registerEditorAction(InPlaceReplaceDown);
  156. registerThemingParticipant((theme, collector) => {
  157. const border = theme.getColor(editorBracketMatchBorder);
  158. if (border) {
  159. collector.addRule(`.monaco-editor.vs .valueSetReplacement { outline: solid 2px ${border}; }`);
  160. }
  161. });