37d9f08bdcc4108dca38397d364d3f63f382d3cb0ed36991645a1a5e26ef0135008385bd7540fce49f979b4b124af4602a44534d071100530e8a0230ef919f 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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 * as dom from '../../../../../base/browser/dom.js';
  24. import { Sizing, SplitView } from '../../../../../base/browser/ui/splitview/splitview.js';
  25. import { Color } from '../../../../../base/common/color.js';
  26. import { Emitter, Event } from '../../../../../base/common/event.js';
  27. import { DisposableStore, dispose } from '../../../../../base/common/lifecycle.js';
  28. import { Schemas } from '../../../../../base/common/network.js';
  29. import { basenameOrAuthority, dirname } from '../../../../../base/common/resources.js';
  30. import './referencesWidget.css';
  31. import { EmbeddedCodeEditorWidget } from '../../../../browser/widget/embeddedCodeEditorWidget.js';
  32. import { Range } from '../../../../common/core/range.js';
  33. import { ModelDecorationOptions, TextModel } from '../../../../common/model/textModel.js';
  34. import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js';
  35. import { PLAINTEXT_LANGUAGE_ID } from '../../../../common/languages/modesRegistry.js';
  36. import { ILanguageService } from '../../../../common/languages/language.js';
  37. import { ITextModelService } from '../../../../common/services/resolverService.js';
  38. import { AccessibilityProvider, DataSource, Delegate, FileReferencesRenderer, IdentityProvider, OneReferenceRenderer, StringRepresentationProvider } from './referencesTree.js';
  39. import * as peekView from '../../../peekView/browser/peekView.js';
  40. import * as nls from '../../../../../nls.js';
  41. import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
  42. import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
  43. import { ILabelService } from '../../../../../platform/label/common/label.js';
  44. import { WorkbenchAsyncDataTree } from '../../../../../platform/list/browser/listService.js';
  45. import { IThemeService } from '../../../../../platform/theme/common/themeService.js';
  46. import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js';
  47. import { FileReferences, OneReference } from '../referencesModel.js';
  48. class DecorationsManager {
  49. constructor(_editor, _model) {
  50. this._editor = _editor;
  51. this._model = _model;
  52. this._decorations = new Map();
  53. this._decorationIgnoreSet = new Set();
  54. this._callOnDispose = new DisposableStore();
  55. this._callOnModelChange = new DisposableStore();
  56. this._callOnDispose.add(this._editor.onDidChangeModel(() => this._onModelChanged()));
  57. this._onModelChanged();
  58. }
  59. dispose() {
  60. this._callOnModelChange.dispose();
  61. this._callOnDispose.dispose();
  62. this.removeDecorations();
  63. }
  64. _onModelChanged() {
  65. this._callOnModelChange.clear();
  66. const model = this._editor.getModel();
  67. if (!model) {
  68. return;
  69. }
  70. for (const ref of this._model.references) {
  71. if (ref.uri.toString() === model.uri.toString()) {
  72. this._addDecorations(ref.parent);
  73. return;
  74. }
  75. }
  76. }
  77. _addDecorations(reference) {
  78. if (!this._editor.hasModel()) {
  79. return;
  80. }
  81. this._callOnModelChange.add(this._editor.getModel().onDidChangeDecorations(() => this._onDecorationChanged()));
  82. const newDecorations = [];
  83. const newDecorationsActualIndex = [];
  84. for (let i = 0, len = reference.children.length; i < len; i++) {
  85. const oneReference = reference.children[i];
  86. if (this._decorationIgnoreSet.has(oneReference.id)) {
  87. continue;
  88. }
  89. if (oneReference.uri.toString() !== this._editor.getModel().uri.toString()) {
  90. continue;
  91. }
  92. newDecorations.push({
  93. range: oneReference.range,
  94. options: DecorationsManager.DecorationOptions
  95. });
  96. newDecorationsActualIndex.push(i);
  97. }
  98. this._editor.changeDecorations((changeAccessor) => {
  99. const decorations = changeAccessor.deltaDecorations([], newDecorations);
  100. for (let i = 0; i < decorations.length; i++) {
  101. this._decorations.set(decorations[i], reference.children[newDecorationsActualIndex[i]]);
  102. }
  103. });
  104. }
  105. _onDecorationChanged() {
  106. const toRemove = [];
  107. const model = this._editor.getModel();
  108. if (!model) {
  109. return;
  110. }
  111. for (const [decorationId, reference] of this._decorations) {
  112. const newRange = model.getDecorationRange(decorationId);
  113. if (!newRange) {
  114. continue;
  115. }
  116. let ignore = false;
  117. if (Range.equalsRange(newRange, reference.range)) {
  118. continue;
  119. }
  120. if (Range.spansMultipleLines(newRange)) {
  121. ignore = true;
  122. }
  123. else {
  124. const lineLength = reference.range.endColumn - reference.range.startColumn;
  125. const newLineLength = newRange.endColumn - newRange.startColumn;
  126. if (lineLength !== newLineLength) {
  127. ignore = true;
  128. }
  129. }
  130. if (ignore) {
  131. this._decorationIgnoreSet.add(reference.id);
  132. toRemove.push(decorationId);
  133. }
  134. else {
  135. reference.range = newRange;
  136. }
  137. }
  138. for (let i = 0, len = toRemove.length; i < len; i++) {
  139. this._decorations.delete(toRemove[i]);
  140. }
  141. this._editor.removeDecorations(toRemove);
  142. }
  143. removeDecorations() {
  144. this._editor.removeDecorations([...this._decorations.keys()]);
  145. this._decorations.clear();
  146. }
  147. }
  148. DecorationsManager.DecorationOptions = ModelDecorationOptions.register({
  149. description: 'reference-decoration',
  150. stickiness: 1 /* TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges */,
  151. className: 'reference-decoration'
  152. });
  153. export class LayoutData {
  154. constructor() {
  155. this.ratio = 0.7;
  156. this.heightInLines = 18;
  157. }
  158. static fromJSON(raw) {
  159. let ratio;
  160. let heightInLines;
  161. try {
  162. const data = JSON.parse(raw);
  163. ratio = data.ratio;
  164. heightInLines = data.heightInLines;
  165. }
  166. catch (_a) {
  167. //
  168. }
  169. return {
  170. ratio: ratio || 0.7,
  171. heightInLines: heightInLines || 18
  172. };
  173. }
  174. }
  175. class ReferencesTree extends WorkbenchAsyncDataTree {
  176. }
  177. /**
  178. * ZoneWidget that is shown inside the editor
  179. */
  180. let ReferenceWidget = class ReferenceWidget extends peekView.PeekViewWidget {
  181. constructor(editor, _defaultTreeKeyboardSupport, layoutData, themeService, _textModelResolverService, _instantiationService, _peekViewService, _uriLabel, _undoRedoService, _keybindingService, _languageService, _languageConfigurationService) {
  182. super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true, supportOnTitleClick: true }, _instantiationService);
  183. this._defaultTreeKeyboardSupport = _defaultTreeKeyboardSupport;
  184. this.layoutData = layoutData;
  185. this._textModelResolverService = _textModelResolverService;
  186. this._instantiationService = _instantiationService;
  187. this._peekViewService = _peekViewService;
  188. this._uriLabel = _uriLabel;
  189. this._undoRedoService = _undoRedoService;
  190. this._keybindingService = _keybindingService;
  191. this._languageService = _languageService;
  192. this._languageConfigurationService = _languageConfigurationService;
  193. this._disposeOnNewModel = new DisposableStore();
  194. this._callOnDispose = new DisposableStore();
  195. this._onDidSelectReference = new Emitter();
  196. this.onDidSelectReference = this._onDidSelectReference.event;
  197. this._dim = new dom.Dimension(0, 0);
  198. this._applyTheme(themeService.getColorTheme());
  199. this._callOnDispose.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this)));
  200. this._peekViewService.addExclusiveWidget(editor, this);
  201. this.create();
  202. }
  203. dispose() {
  204. this.setModel(undefined);
  205. this._callOnDispose.dispose();
  206. this._disposeOnNewModel.dispose();
  207. dispose(this._preview);
  208. dispose(this._previewNotAvailableMessage);
  209. dispose(this._tree);
  210. dispose(this._previewModelReference);
  211. this._splitView.dispose();
  212. super.dispose();
  213. }
  214. _applyTheme(theme) {
  215. const borderColor = theme.getColor(peekView.peekViewBorder) || Color.transparent;
  216. this.style({
  217. arrowColor: borderColor,
  218. frameColor: borderColor,
  219. headerBackgroundColor: theme.getColor(peekView.peekViewTitleBackground) || Color.transparent,
  220. primaryHeadingColor: theme.getColor(peekView.peekViewTitleForeground),
  221. secondaryHeadingColor: theme.getColor(peekView.peekViewTitleInfoForeground)
  222. });
  223. }
  224. show(where) {
  225. super.show(where, this.layoutData.heightInLines || 18);
  226. }
  227. focusOnReferenceTree() {
  228. this._tree.domFocus();
  229. }
  230. focusOnPreviewEditor() {
  231. this._preview.focus();
  232. }
  233. isPreviewEditorFocused() {
  234. return this._preview.hasTextFocus();
  235. }
  236. _onTitleClick(e) {
  237. if (this._preview && this._preview.getModel()) {
  238. this._onDidSelectReference.fire({
  239. element: this._getFocusedReference(),
  240. kind: e.ctrlKey || e.metaKey || e.altKey ? 'side' : 'open',
  241. source: 'title'
  242. });
  243. }
  244. }
  245. _fillBody(containerElement) {
  246. this.setCssClass('reference-zone-widget');
  247. // message pane
  248. this._messageContainer = dom.append(containerElement, dom.$('div.messages'));
  249. dom.hide(this._messageContainer);
  250. this._splitView = new SplitView(containerElement, { orientation: 1 /* Orientation.HORIZONTAL */ });
  251. // editor
  252. this._previewContainer = dom.append(containerElement, dom.$('div.preview.inline'));
  253. const options = {
  254. scrollBeyondLastLine: false,
  255. scrollbar: {
  256. verticalScrollbarSize: 14,
  257. horizontal: 'auto',
  258. useShadows: true,
  259. verticalHasArrows: false,
  260. horizontalHasArrows: false,
  261. alwaysConsumeMouseWheel: false
  262. },
  263. overviewRulerLanes: 2,
  264. fixedOverflowWidgets: true,
  265. minimap: {
  266. enabled: false
  267. }
  268. };
  269. this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, this._previewContainer, options, this.editor);
  270. dom.hide(this._previewContainer);
  271. this._previewNotAvailableMessage = new TextModel(nls.localize('missingPreviewMessage', "no preview available"), PLAINTEXT_LANGUAGE_ID, TextModel.DEFAULT_CREATION_OPTIONS, null, this._undoRedoService, this._languageService, this._languageConfigurationService);
  272. // tree
  273. this._treeContainer = dom.append(containerElement, dom.$('div.ref-tree.inline'));
  274. const treeOptions = {
  275. keyboardSupport: this._defaultTreeKeyboardSupport,
  276. accessibilityProvider: new AccessibilityProvider(),
  277. keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider),
  278. identityProvider: new IdentityProvider(),
  279. openOnSingleClick: true,
  280. selectionNavigation: true,
  281. overrideStyles: {
  282. listBackground: peekView.peekViewResultsBackground
  283. }
  284. };
  285. if (this._defaultTreeKeyboardSupport) {
  286. // the tree will consume `Escape` and prevent the widget from closing
  287. this._callOnDispose.add(dom.addStandardDisposableListener(this._treeContainer, 'keydown', (e) => {
  288. if (e.equals(9 /* KeyCode.Escape */)) {
  289. this._keybindingService.dispatchEvent(e, e.target);
  290. e.stopPropagation();
  291. }
  292. }, true));
  293. }
  294. this._tree = this._instantiationService.createInstance(ReferencesTree, 'ReferencesWidget', this._treeContainer, new Delegate(), [
  295. this._instantiationService.createInstance(FileReferencesRenderer),
  296. this._instantiationService.createInstance(OneReferenceRenderer),
  297. ], this._instantiationService.createInstance(DataSource), treeOptions);
  298. // split stuff
  299. this._splitView.addView({
  300. onDidChange: Event.None,
  301. element: this._previewContainer,
  302. minimumSize: 200,
  303. maximumSize: Number.MAX_VALUE,
  304. layout: (width) => {
  305. this._preview.layout({ height: this._dim.height, width });
  306. }
  307. }, Sizing.Distribute);
  308. this._splitView.addView({
  309. onDidChange: Event.None,
  310. element: this._treeContainer,
  311. minimumSize: 100,
  312. maximumSize: Number.MAX_VALUE,
  313. layout: (width) => {
  314. this._treeContainer.style.height = `${this._dim.height}px`;
  315. this._treeContainer.style.width = `${width}px`;
  316. this._tree.layout(this._dim.height, width);
  317. }
  318. }, Sizing.Distribute);
  319. this._disposables.add(this._splitView.onDidSashChange(() => {
  320. if (this._dim.width) {
  321. this.layoutData.ratio = this._splitView.getViewSize(0) / this._dim.width;
  322. }
  323. }, undefined));
  324. // listen on selection and focus
  325. const onEvent = (element, kind) => {
  326. if (element instanceof OneReference) {
  327. if (kind === 'show') {
  328. this._revealReference(element, false);
  329. }
  330. this._onDidSelectReference.fire({ element, kind, source: 'tree' });
  331. }
  332. };
  333. this._tree.onDidOpen(e => {
  334. if (e.sideBySide) {
  335. onEvent(e.element, 'side');
  336. }
  337. else if (e.editorOptions.pinned) {
  338. onEvent(e.element, 'goto');
  339. }
  340. else {
  341. onEvent(e.element, 'show');
  342. }
  343. });
  344. dom.hide(this._treeContainer);
  345. }
  346. _onWidth(width) {
  347. if (this._dim) {
  348. this._doLayoutBody(this._dim.height, width);
  349. }
  350. }
  351. _doLayoutBody(heightInPixel, widthInPixel) {
  352. super._doLayoutBody(heightInPixel, widthInPixel);
  353. this._dim = new dom.Dimension(widthInPixel, heightInPixel);
  354. this.layoutData.heightInLines = this._viewZone ? this._viewZone.heightInLines : this.layoutData.heightInLines;
  355. this._splitView.layout(widthInPixel);
  356. this._splitView.resizeView(0, widthInPixel * this.layoutData.ratio);
  357. }
  358. setSelection(selection) {
  359. return this._revealReference(selection, true).then(() => {
  360. if (!this._model) {
  361. // disposed
  362. return;
  363. }
  364. // show in tree
  365. this._tree.setSelection([selection]);
  366. this._tree.setFocus([selection]);
  367. });
  368. }
  369. setModel(newModel) {
  370. // clean up
  371. this._disposeOnNewModel.clear();
  372. this._model = newModel;
  373. if (this._model) {
  374. return this._onNewModel();
  375. }
  376. return Promise.resolve();
  377. }
  378. _onNewModel() {
  379. if (!this._model) {
  380. return Promise.resolve(undefined);
  381. }
  382. if (this._model.isEmpty) {
  383. this.setTitle('');
  384. this._messageContainer.innerText = nls.localize('noResults', "No results");
  385. dom.show(this._messageContainer);
  386. return Promise.resolve(undefined);
  387. }
  388. dom.hide(this._messageContainer);
  389. this._decorationsManager = new DecorationsManager(this._preview, this._model);
  390. this._disposeOnNewModel.add(this._decorationsManager);
  391. // listen on model changes
  392. this._disposeOnNewModel.add(this._model.onDidChangeReferenceRange(reference => this._tree.rerender(reference)));
  393. // listen on editor
  394. this._disposeOnNewModel.add(this._preview.onMouseDown(e => {
  395. const { event, target } = e;
  396. if (event.detail !== 2) {
  397. return;
  398. }
  399. const element = this._getFocusedReference();
  400. if (!element) {
  401. return;
  402. }
  403. this._onDidSelectReference.fire({
  404. element: { uri: element.uri, range: target.range },
  405. kind: (event.ctrlKey || event.metaKey || event.altKey) ? 'side' : 'open',
  406. source: 'editor'
  407. });
  408. }));
  409. // make sure things are rendered
  410. this.container.classList.add('results-loaded');
  411. dom.show(this._treeContainer);
  412. dom.show(this._previewContainer);
  413. this._splitView.layout(this._dim.width);
  414. this.focusOnReferenceTree();
  415. // pick input and a reference to begin with
  416. return this._tree.setInput(this._model.groups.length === 1 ? this._model.groups[0] : this._model);
  417. }
  418. _getFocusedReference() {
  419. const [element] = this._tree.getFocus();
  420. if (element instanceof OneReference) {
  421. return element;
  422. }
  423. else if (element instanceof FileReferences) {
  424. if (element.children.length > 0) {
  425. return element.children[0];
  426. }
  427. }
  428. return undefined;
  429. }
  430. revealReference(reference) {
  431. return __awaiter(this, void 0, void 0, function* () {
  432. yield this._revealReference(reference, false);
  433. this._onDidSelectReference.fire({ element: reference, kind: 'goto', source: 'tree' });
  434. });
  435. }
  436. _revealReference(reference, revealParent) {
  437. return __awaiter(this, void 0, void 0, function* () {
  438. // check if there is anything to do...
  439. if (this._revealedReference === reference) {
  440. return;
  441. }
  442. this._revealedReference = reference;
  443. // Update widget header
  444. if (reference.uri.scheme !== Schemas.inMemory) {
  445. this.setTitle(basenameOrAuthority(reference.uri), this._uriLabel.getUriLabel(dirname(reference.uri)));
  446. }
  447. else {
  448. this.setTitle(nls.localize('peekView.alternateTitle', "References"));
  449. }
  450. const promise = this._textModelResolverService.createModelReference(reference.uri);
  451. if (this._tree.getInput() === reference.parent) {
  452. this._tree.reveal(reference);
  453. }
  454. else {
  455. if (revealParent) {
  456. this._tree.reveal(reference.parent);
  457. }
  458. yield this._tree.expand(reference.parent);
  459. this._tree.reveal(reference);
  460. }
  461. const ref = yield promise;
  462. if (!this._model) {
  463. // disposed
  464. ref.dispose();
  465. return;
  466. }
  467. dispose(this._previewModelReference);
  468. // show in editor
  469. const model = ref.object;
  470. if (model) {
  471. const scrollType = this._preview.getModel() === model.textEditorModel ? 0 /* ScrollType.Smooth */ : 1 /* ScrollType.Immediate */;
  472. const sel = Range.lift(reference.range).collapseToStart();
  473. this._previewModelReference = ref;
  474. this._preview.setModel(model.textEditorModel);
  475. this._preview.setSelection(sel);
  476. this._preview.revealRangeInCenter(sel, scrollType);
  477. }
  478. else {
  479. this._preview.setModel(this._previewNotAvailableMessage);
  480. ref.dispose();
  481. }
  482. });
  483. }
  484. };
  485. ReferenceWidget = __decorate([
  486. __param(3, IThemeService),
  487. __param(4, ITextModelService),
  488. __param(5, IInstantiationService),
  489. __param(6, peekView.IPeekViewService),
  490. __param(7, ILabelService),
  491. __param(8, IUndoRedoService),
  492. __param(9, IKeybindingService),
  493. __param(10, ILanguageService),
  494. __param(11, ILanguageConfigurationService)
  495. ], ReferenceWidget);
  496. export { ReferenceWidget };