b0a48929f4bb5519b0a235215554606c7a5fef550929c8567f878974b55e17af9627f2c1322e3ca4a83133ecc58c96be9e778ec25a9d3f28eac810e881e7b6 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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. import { once } from '../../../../base/common/functional.js';
  6. import { DisposableStore, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
  7. import { withNullAsUndefined } from '../../../../base/common/types.js';
  8. import { getCodeEditor, isDiffEditor } from '../../../browser/editorBrowser.js';
  9. import { OverviewRulerLane } from '../../../common/model.js';
  10. import { overviewRulerRangeHighlight } from '../../../common/core/editorColorRegistry.js';
  11. import { themeColorFromId } from '../../../../platform/theme/common/themeService.js';
  12. /**
  13. * A reusable quick access provider for the editor with support
  14. * for adding decorations for navigating in the currently active file
  15. * (for example "Go to line", "Go to symbol").
  16. */
  17. export class AbstractEditorNavigationQuickAccessProvider {
  18. constructor(options) {
  19. this.options = options;
  20. //#endregion
  21. //#region Decorations Utils
  22. this.rangeHighlightDecorationId = undefined;
  23. }
  24. //#region Provider methods
  25. provide(picker, token) {
  26. var _a;
  27. const disposables = new DisposableStore();
  28. // Apply options if any
  29. picker.canAcceptInBackground = !!((_a = this.options) === null || _a === void 0 ? void 0 : _a.canAcceptInBackground);
  30. // Disable filtering & sorting, we control the results
  31. picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
  32. // Provide based on current active editor
  33. const pickerDisposable = disposables.add(new MutableDisposable());
  34. pickerDisposable.value = this.doProvide(picker, token);
  35. // Re-create whenever the active editor changes
  36. disposables.add(this.onDidActiveTextEditorControlChange(() => {
  37. // Clear old
  38. pickerDisposable.value = undefined;
  39. // Add new
  40. pickerDisposable.value = this.doProvide(picker, token);
  41. }));
  42. return disposables;
  43. }
  44. doProvide(picker, token) {
  45. const disposables = new DisposableStore();
  46. // With text control
  47. const editor = this.activeTextEditorControl;
  48. if (editor && this.canProvideWithTextEditor(editor)) {
  49. const context = { editor };
  50. // Restore any view state if this picker was closed
  51. // without actually going to a line
  52. const codeEditor = getCodeEditor(editor);
  53. if (codeEditor) {
  54. // Remember view state and update it when the cursor position
  55. // changes even later because it could be that the user has
  56. // configured quick access to remain open when focus is lost and
  57. // we always want to restore the current location.
  58. let lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
  59. disposables.add(codeEditor.onDidChangeCursorPosition(() => {
  60. lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
  61. }));
  62. context.restoreViewState = () => {
  63. if (lastKnownEditorViewState && editor === this.activeTextEditorControl) {
  64. editor.restoreViewState(lastKnownEditorViewState);
  65. }
  66. };
  67. disposables.add(once(token.onCancellationRequested)(() => { var _a; return (_a = context.restoreViewState) === null || _a === void 0 ? void 0 : _a.call(context); }));
  68. }
  69. // Clean up decorations on dispose
  70. disposables.add(toDisposable(() => this.clearDecorations(editor)));
  71. // Ask subclass for entries
  72. disposables.add(this.provideWithTextEditor(context, picker, token));
  73. }
  74. // Without text control
  75. else {
  76. disposables.add(this.provideWithoutTextEditor(picker, token));
  77. }
  78. return disposables;
  79. }
  80. /**
  81. * Subclasses to implement if they can operate on the text editor.
  82. */
  83. canProvideWithTextEditor(editor) {
  84. return true;
  85. }
  86. gotoLocation({ editor }, options) {
  87. editor.setSelection(options.range);
  88. editor.revealRangeInCenter(options.range, 0 /* ScrollType.Smooth */);
  89. if (!options.preserveFocus) {
  90. editor.focus();
  91. }
  92. }
  93. getModel(editor) {
  94. var _a;
  95. return isDiffEditor(editor) ?
  96. (_a = editor.getModel()) === null || _a === void 0 ? void 0 : _a.modified :
  97. editor.getModel();
  98. }
  99. addDecorations(editor, range) {
  100. editor.changeDecorations(changeAccessor => {
  101. // Reset old decorations if any
  102. const deleteDecorations = [];
  103. if (this.rangeHighlightDecorationId) {
  104. deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
  105. deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
  106. this.rangeHighlightDecorationId = undefined;
  107. }
  108. // Add new decorations for the range
  109. const newDecorations = [
  110. // highlight the entire line on the range
  111. {
  112. range,
  113. options: {
  114. description: 'quick-access-range-highlight',
  115. className: 'rangeHighlight',
  116. isWholeLine: true
  117. }
  118. },
  119. // also add overview ruler highlight
  120. {
  121. range,
  122. options: {
  123. description: 'quick-access-range-highlight-overview',
  124. overviewRuler: {
  125. color: themeColorFromId(overviewRulerRangeHighlight),
  126. position: OverviewRulerLane.Full
  127. }
  128. }
  129. }
  130. ];
  131. const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
  132. this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
  133. });
  134. }
  135. clearDecorations(editor) {
  136. const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
  137. if (rangeHighlightDecorationId) {
  138. editor.changeDecorations(changeAccessor => {
  139. changeAccessor.deltaDecorations([
  140. rangeHighlightDecorationId.overviewRulerDecorationId,
  141. rangeHighlightDecorationId.rangeHighlightId
  142. ], []);
  143. });
  144. this.rangeHighlightDecorationId = undefined;
  145. }
  146. }
  147. }