/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 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; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import * as dom from '../../../../../base/browser/dom.js'; import { Sizing, SplitView } from '../../../../../base/browser/ui/splitview/splitview.js'; import { Color } from '../../../../../base/common/color.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { DisposableStore, dispose } from '../../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../../base/common/network.js'; import { basenameOrAuthority, dirname } from '../../../../../base/common/resources.js'; import './referencesWidget.css'; import { EmbeddedCodeEditorWidget } from '../../../../browser/widget/embeddedCodeEditorWidget.js'; import { Range } from '../../../../common/core/range.js'; import { ModelDecorationOptions, TextModel } from '../../../../common/model/textModel.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../common/languages/modesRegistry.js'; import { ILanguageService } from '../../../../common/languages/language.js'; import { ITextModelService } from '../../../../common/services/resolverService.js'; import { AccessibilityProvider, DataSource, Delegate, FileReferencesRenderer, IdentityProvider, OneReferenceRenderer, StringRepresentationProvider } from './referencesTree.js'; import * as peekView from '../../../peekView/browser/peekView.js'; import * as nls from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { WorkbenchAsyncDataTree } from '../../../../../platform/list/browser/listService.js'; import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { FileReferences, OneReference } from '../referencesModel.js'; class DecorationsManager { constructor(_editor, _model) { this._editor = _editor; this._model = _model; this._decorations = new Map(); this._decorationIgnoreSet = new Set(); this._callOnDispose = new DisposableStore(); this._callOnModelChange = new DisposableStore(); this._callOnDispose.add(this._editor.onDidChangeModel(() => this._onModelChanged())); this._onModelChanged(); } dispose() { this._callOnModelChange.dispose(); this._callOnDispose.dispose(); this.removeDecorations(); } _onModelChanged() { this._callOnModelChange.clear(); const model = this._editor.getModel(); if (!model) { return; } for (const ref of this._model.references) { if (ref.uri.toString() === model.uri.toString()) { this._addDecorations(ref.parent); return; } } } _addDecorations(reference) { if (!this._editor.hasModel()) { return; } this._callOnModelChange.add(this._editor.getModel().onDidChangeDecorations(() => this._onDecorationChanged())); const newDecorations = []; const newDecorationsActualIndex = []; for (let i = 0, len = reference.children.length; i < len; i++) { const oneReference = reference.children[i]; if (this._decorationIgnoreSet.has(oneReference.id)) { continue; } if (oneReference.uri.toString() !== this._editor.getModel().uri.toString()) { continue; } newDecorations.push({ range: oneReference.range, options: DecorationsManager.DecorationOptions }); newDecorationsActualIndex.push(i); } this._editor.changeDecorations((changeAccessor) => { const decorations = changeAccessor.deltaDecorations([], newDecorations); for (let i = 0; i < decorations.length; i++) { this._decorations.set(decorations[i], reference.children[newDecorationsActualIndex[i]]); } }); } _onDecorationChanged() { const toRemove = []; const model = this._editor.getModel(); if (!model) { return; } for (const [decorationId, reference] of this._decorations) { const newRange = model.getDecorationRange(decorationId); if (!newRange) { continue; } let ignore = false; if (Range.equalsRange(newRange, reference.range)) { continue; } if (Range.spansMultipleLines(newRange)) { ignore = true; } else { const lineLength = reference.range.endColumn - reference.range.startColumn; const newLineLength = newRange.endColumn - newRange.startColumn; if (lineLength !== newLineLength) { ignore = true; } } if (ignore) { this._decorationIgnoreSet.add(reference.id); toRemove.push(decorationId); } else { reference.range = newRange; } } for (let i = 0, len = toRemove.length; i < len; i++) { this._decorations.delete(toRemove[i]); } this._editor.removeDecorations(toRemove); } removeDecorations() { this._editor.removeDecorations([...this._decorations.keys()]); this._decorations.clear(); } } DecorationsManager.DecorationOptions = ModelDecorationOptions.register({ description: 'reference-decoration', stickiness: 1 /* TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges */, className: 'reference-decoration' }); export class LayoutData { constructor() { this.ratio = 0.7; this.heightInLines = 18; } static fromJSON(raw) { let ratio; let heightInLines; try { const data = JSON.parse(raw); ratio = data.ratio; heightInLines = data.heightInLines; } catch (_a) { // } return { ratio: ratio || 0.7, heightInLines: heightInLines || 18 }; } } class ReferencesTree extends WorkbenchAsyncDataTree { } /** * ZoneWidget that is shown inside the editor */ let ReferenceWidget = class ReferenceWidget extends peekView.PeekViewWidget { constructor(editor, _defaultTreeKeyboardSupport, layoutData, themeService, _textModelResolverService, _instantiationService, _peekViewService, _uriLabel, _undoRedoService, _keybindingService, _languageService, _languageConfigurationService) { super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true, supportOnTitleClick: true }, _instantiationService); this._defaultTreeKeyboardSupport = _defaultTreeKeyboardSupport; this.layoutData = layoutData; this._textModelResolverService = _textModelResolverService; this._instantiationService = _instantiationService; this._peekViewService = _peekViewService; this._uriLabel = _uriLabel; this._undoRedoService = _undoRedoService; this._keybindingService = _keybindingService; this._languageService = _languageService; this._languageConfigurationService = _languageConfigurationService; this._disposeOnNewModel = new DisposableStore(); this._callOnDispose = new DisposableStore(); this._onDidSelectReference = new Emitter(); this.onDidSelectReference = this._onDidSelectReference.event; this._dim = new dom.Dimension(0, 0); this._applyTheme(themeService.getColorTheme()); this._callOnDispose.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); this._peekViewService.addExclusiveWidget(editor, this); this.create(); } dispose() { this.setModel(undefined); this._callOnDispose.dispose(); this._disposeOnNewModel.dispose(); dispose(this._preview); dispose(this._previewNotAvailableMessage); dispose(this._tree); dispose(this._previewModelReference); this._splitView.dispose(); super.dispose(); } _applyTheme(theme) { const borderColor = theme.getColor(peekView.peekViewBorder) || Color.transparent; this.style({ arrowColor: borderColor, frameColor: borderColor, headerBackgroundColor: theme.getColor(peekView.peekViewTitleBackground) || Color.transparent, primaryHeadingColor: theme.getColor(peekView.peekViewTitleForeground), secondaryHeadingColor: theme.getColor(peekView.peekViewTitleInfoForeground) }); } show(where) { super.show(where, this.layoutData.heightInLines || 18); } focusOnReferenceTree() { this._tree.domFocus(); } focusOnPreviewEditor() { this._preview.focus(); } isPreviewEditorFocused() { return this._preview.hasTextFocus(); } _onTitleClick(e) { if (this._preview && this._preview.getModel()) { this._onDidSelectReference.fire({ element: this._getFocusedReference(), kind: e.ctrlKey || e.metaKey || e.altKey ? 'side' : 'open', source: 'title' }); } } _fillBody(containerElement) { this.setCssClass('reference-zone-widget'); // message pane this._messageContainer = dom.append(containerElement, dom.$('div.messages')); dom.hide(this._messageContainer); this._splitView = new SplitView(containerElement, { orientation: 1 /* Orientation.HORIZONTAL */ }); // editor this._previewContainer = dom.append(containerElement, dom.$('div.preview.inline')); const options = { scrollBeyondLastLine: false, scrollbar: { verticalScrollbarSize: 14, horizontal: 'auto', useShadows: true, verticalHasArrows: false, horizontalHasArrows: false, alwaysConsumeMouseWheel: false }, overviewRulerLanes: 2, fixedOverflowWidgets: true, minimap: { enabled: false } }; this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, this._previewContainer, options, this.editor); dom.hide(this._previewContainer); this._previewNotAvailableMessage = new TextModel(nls.localize('missingPreviewMessage', "no preview available"), PLAINTEXT_LANGUAGE_ID, TextModel.DEFAULT_CREATION_OPTIONS, null, this._undoRedoService, this._languageService, this._languageConfigurationService); // tree this._treeContainer = dom.append(containerElement, dom.$('div.ref-tree.inline')); const treeOptions = { keyboardSupport: this._defaultTreeKeyboardSupport, accessibilityProvider: new AccessibilityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider), identityProvider: new IdentityProvider(), openOnSingleClick: true, selectionNavigation: true, overrideStyles: { listBackground: peekView.peekViewResultsBackground } }; if (this._defaultTreeKeyboardSupport) { // the tree will consume `Escape` and prevent the widget from closing this._callOnDispose.add(dom.addStandardDisposableListener(this._treeContainer, 'keydown', (e) => { if (e.equals(9 /* KeyCode.Escape */)) { this._keybindingService.dispatchEvent(e, e.target); e.stopPropagation(); } }, true)); } this._tree = this._instantiationService.createInstance(ReferencesTree, 'ReferencesWidget', this._treeContainer, new Delegate(), [ this._instantiationService.createInstance(FileReferencesRenderer), this._instantiationService.createInstance(OneReferenceRenderer), ], this._instantiationService.createInstance(DataSource), treeOptions); // split stuff this._splitView.addView({ onDidChange: Event.None, element: this._previewContainer, minimumSize: 200, maximumSize: Number.MAX_VALUE, layout: (width) => { this._preview.layout({ height: this._dim.height, width }); } }, Sizing.Distribute); this._splitView.addView({ onDidChange: Event.None, element: this._treeContainer, minimumSize: 100, maximumSize: Number.MAX_VALUE, layout: (width) => { this._treeContainer.style.height = `${this._dim.height}px`; this._treeContainer.style.width = `${width}px`; this._tree.layout(this._dim.height, width); } }, Sizing.Distribute); this._disposables.add(this._splitView.onDidSashChange(() => { if (this._dim.width) { this.layoutData.ratio = this._splitView.getViewSize(0) / this._dim.width; } }, undefined)); // listen on selection and focus const onEvent = (element, kind) => { if (element instanceof OneReference) { if (kind === 'show') { this._revealReference(element, false); } this._onDidSelectReference.fire({ element, kind, source: 'tree' }); } }; this._tree.onDidOpen(e => { if (e.sideBySide) { onEvent(e.element, 'side'); } else if (e.editorOptions.pinned) { onEvent(e.element, 'goto'); } else { onEvent(e.element, 'show'); } }); dom.hide(this._treeContainer); } _onWidth(width) { if (this._dim) { this._doLayoutBody(this._dim.height, width); } } _doLayoutBody(heightInPixel, widthInPixel) { super._doLayoutBody(heightInPixel, widthInPixel); this._dim = new dom.Dimension(widthInPixel, heightInPixel); this.layoutData.heightInLines = this._viewZone ? this._viewZone.heightInLines : this.layoutData.heightInLines; this._splitView.layout(widthInPixel); this._splitView.resizeView(0, widthInPixel * this.layoutData.ratio); } setSelection(selection) { return this._revealReference(selection, true).then(() => { if (!this._model) { // disposed return; } // show in tree this._tree.setSelection([selection]); this._tree.setFocus([selection]); }); } setModel(newModel) { // clean up this._disposeOnNewModel.clear(); this._model = newModel; if (this._model) { return this._onNewModel(); } return Promise.resolve(); } _onNewModel() { if (!this._model) { return Promise.resolve(undefined); } if (this._model.isEmpty) { this.setTitle(''); this._messageContainer.innerText = nls.localize('noResults', "No results"); dom.show(this._messageContainer); return Promise.resolve(undefined); } dom.hide(this._messageContainer); this._decorationsManager = new DecorationsManager(this._preview, this._model); this._disposeOnNewModel.add(this._decorationsManager); // listen on model changes this._disposeOnNewModel.add(this._model.onDidChangeReferenceRange(reference => this._tree.rerender(reference))); // listen on editor this._disposeOnNewModel.add(this._preview.onMouseDown(e => { const { event, target } = e; if (event.detail !== 2) { return; } const element = this._getFocusedReference(); if (!element) { return; } this._onDidSelectReference.fire({ element: { uri: element.uri, range: target.range }, kind: (event.ctrlKey || event.metaKey || event.altKey) ? 'side' : 'open', source: 'editor' }); })); // make sure things are rendered this.container.classList.add('results-loaded'); dom.show(this._treeContainer); dom.show(this._previewContainer); this._splitView.layout(this._dim.width); this.focusOnReferenceTree(); // pick input and a reference to begin with return this._tree.setInput(this._model.groups.length === 1 ? this._model.groups[0] : this._model); } _getFocusedReference() { const [element] = this._tree.getFocus(); if (element instanceof OneReference) { return element; } else if (element instanceof FileReferences) { if (element.children.length > 0) { return element.children[0]; } } return undefined; } revealReference(reference) { return __awaiter(this, void 0, void 0, function* () { yield this._revealReference(reference, false); this._onDidSelectReference.fire({ element: reference, kind: 'goto', source: 'tree' }); }); } _revealReference(reference, revealParent) { return __awaiter(this, void 0, void 0, function* () { // check if there is anything to do... if (this._revealedReference === reference) { return; } this._revealedReference = reference; // Update widget header if (reference.uri.scheme !== Schemas.inMemory) { this.setTitle(basenameOrAuthority(reference.uri), this._uriLabel.getUriLabel(dirname(reference.uri))); } else { this.setTitle(nls.localize('peekView.alternateTitle', "References")); } const promise = this._textModelResolverService.createModelReference(reference.uri); if (this._tree.getInput() === reference.parent) { this._tree.reveal(reference); } else { if (revealParent) { this._tree.reveal(reference.parent); } yield this._tree.expand(reference.parent); this._tree.reveal(reference); } const ref = yield promise; if (!this._model) { // disposed ref.dispose(); return; } dispose(this._previewModelReference); // show in editor const model = ref.object; if (model) { const scrollType = this._preview.getModel() === model.textEditorModel ? 0 /* ScrollType.Smooth */ : 1 /* ScrollType.Immediate */; const sel = Range.lift(reference.range).collapseToStart(); this._previewModelReference = ref; this._preview.setModel(model.textEditorModel); this._preview.setSelection(sel); this._preview.revealRangeInCenter(sel, scrollType); } else { this._preview.setModel(this._previewNotAvailableMessage); ref.dispose(); } }); } }; ReferenceWidget = __decorate([ __param(3, IThemeService), __param(4, ITextModelService), __param(5, IInstantiationService), __param(6, peekView.IPeekViewService), __param(7, ILabelService), __param(8, IUndoRedoService), __param(9, IKeybindingService), __param(10, ILanguageService), __param(11, ILanguageConfigurationService) ], ReferenceWidget); export { ReferenceWidget };