From e086d4ec549c84947c9111cd0cbb884daf96c443 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 May 2016 09:42:32 +0200 Subject: [PATCH] some refactorings in reference search, prep for #5893 --- .../browser/referenceSearch.ts | 163 +++++++++--------- .../browser/referenceSearchModel.ts | 37 ++-- .../browser/referenceSearchWidget.ts | 134 +++++++------- 3 files changed, 152 insertions(+), 182 deletions(-) diff --git a/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts b/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts index 05ab600f3eb..78755ea96bf 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/referenceSearch.ts @@ -31,7 +31,7 @@ import {ICodeEditor} from 'vs/editor/browser/editorBrowser'; import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions'; import {IPeekViewService, getOuterEditor} from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; import {findReferences} from '../common/referenceSearch'; -import {EventType, ReferencesModel} from './referenceSearchModel'; +import {ReferencesModel, OneReference} from './referenceSearchModel'; import {ReferenceWidget, LayoutData} from './referenceSearchWidget'; import {ServicesAccessor} from 'vs/platform/instantiation/common/instantiation'; @@ -39,19 +39,12 @@ export class FindReferencesController implements editorCommon.IEditorContributio public static ID = 'editor.contrib.findReferencesController'; - private _editor:ICodeEditor; - private _widget:ReferenceWidget; - private _requestIdPool: number; - private _callOnClear:Function[]; - private _model:ReferencesModel; - private _modelRevealing:boolean; - - private editorService: IEditorService; - private telemetryService: ITelemetryService; - private messageService: IMessageService; - private instantiationService: IInstantiationService; - private contextService: IWorkspaceContextService; - private peekViewService: IPeekViewService; + private _editor: ICodeEditor; + private _widget: ReferenceWidget; + private _model: ReferencesModel; + private _requestIdPool = 0; + private _callOnClear: Function[] = []; + private _ignoreModelChangeEvent = false; private _startTime: number = -1; private _referenceSearchVisible: IKeybindingContextKey; @@ -62,24 +55,15 @@ export class FindReferencesController implements editorCommon.IEditorContributio public constructor( editor: ICodeEditor, - @IEditorService editorService: IEditorService, - @ITelemetryService telemetryService: ITelemetryService, - @IMessageService messageService: IMessageService, - @IInstantiationService instantiationService: IInstantiationService, @IKeybindingService keybindingService: IKeybindingService, - @IWorkspaceContextService contextService: IWorkspaceContextService, + @IEditorService private _editorService: IEditorService, + @ITelemetryService private _telemetryService: ITelemetryService, + @IMessageService private _messageService: IMessageService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IWorkspaceContextService private _contextService: IWorkspaceContextService, @IStorageService private _storageService: IStorageService, - @optional(IPeekViewService) peekViewService: IPeekViewService + @optional(IPeekViewService) private _peekViewService: IPeekViewService ) { - this._requestIdPool = 0; - this._callOnClear = []; - this.editorService = editorService; - this.telemetryService = telemetryService; - this.messageService = messageService; - this.instantiationService = instantiationService; - this.peekViewService = peekViewService; - this.contextService = contextService; - this._modelRevealing = false; this._editor = editor; this._referenceSearchVisible = keybindingService.createKey(CONTEXT_REFERENCE_SEARCH_VISIBLE, false); } @@ -97,7 +81,7 @@ export class FindReferencesController implements editorCommon.IEditorContributio } public isInPeekView() : boolean { - return this.peekViewService && this.peekViewService.isActive; + return this._peekViewService && this._peekViewService.isActive; } public closeReferenceSearch(): void { @@ -120,13 +104,13 @@ export class FindReferencesController implements editorCommon.IEditorContributio // close the widget on model/mode changes this._callOnClear.push(this._editor.addListener(editorCommon.EventType.ModelModeChanged, () => { this.clear(); })); this._callOnClear.push(this._editor.addListener(editorCommon.EventType.ModelChanged, () => { - if(!this._modelRevealing) { + if(!this._ignoreModelChangeEvent) { this.clear(); } })); const storageKey = 'peekViewLayout'; const data = JSON.parse(this._storageService.get(storageKey, undefined, '{}')); - this._widget = new ReferenceWidget(this._editor, data, this.editorService, this.contextService, this.instantiationService); + this._widget = new ReferenceWidget(this._editor, data, this._editorService, this._contextService, this._instantiationService); this._widget.setTitle(nls.localize('labelLoading', "Loading...")); this._widget.show(range); this._callOnClear.push(this._widget.onDidClose(() => { @@ -137,31 +121,27 @@ export class FindReferencesController implements editorCommon.IEditorContributio this.clear(); }).dispose); - this._callOnClear.push(this._widget.onDidDoubleClick(event => { - - if(!event.reference) { - return; - } - - // open editor - this.editorService.openEditor({ - resource: event.reference, - options: { selection: event.range } - }, event.originalEvent.ctrlKey || event.originalEvent.metaKey).done(null, onUnexpectedError); - - // close zone - if (!(event.originalEvent.ctrlKey || event.originalEvent.metaKey)) { - this.clear(); + this._callOnClear.push(this._widget.onDidSelectReference(event => { + let {element, kind} = event; + switch (kind) { + case 'side': + case 'open': + this._openReference(element, kind === 'side'); + break; + case 'goto': + this._gotoReference(element); + break; } }).dispose); + var requestId = ++this._requestIdPool, editorModel = this._editor.getModel(); - var timer = this.telemetryService.timedPublicLog('findReferences', { + var timer = this._telemetryService.timedPublicLog('findReferences', { mode: editorModel.getMode().getId() }); - referencesPromise.then((references:IReference[]) => { + referencesPromise.then(references => { // still current request? widget still open? if(requestId !== this._requestIdPool || !this._widget) { @@ -177,38 +157,7 @@ export class FindReferencesController implements editorCommon.IEditorContributio } // create result model - this._model = new ReferencesModel(references, this.editorService); - this._model.currentReference = this._model.findReference(editorModel.getAssociatedResource(), range.getStartPosition()); - - var unbind = this._model.addListener(EventType.CurrentReferenceChanged, () => { - - this._modelRevealing = true; - - this.editorService.openEditor({ - resource: this._model.currentReference.resource, - options: { selection: this._model.currentReference.range } - }).done((openedEditor) => { - if(!openedEditor || openedEditor.getControl() !== this._editor) { - // TODO@Alex TODO@Joh - // when opening the current reference we might end up - // in a different editor instance. that means we also have - // a different instance of this reference search controller - // and cannot hold onto the widget (which likely doesn't - // exist). Instead of bailing out we should find the - // 'sister' action and pass our current model on to it. - this.clear(); - return; - } - this._modelRevealing = false; - this._widget.show(this._model.currentReference.range); - this._widget.focus(); - }, (err) => { - this._modelRevealing = false; - onUnexpectedError(err); - }); - }); - - this._callOnClear.push(unbind); + this._model = new ReferencesModel(references, this._editorService); // show widget this._startTime = Date.now(); @@ -219,17 +168,17 @@ export class FindReferencesController implements editorCommon.IEditorContributio timer.stop(); }, (error:any) => { - this.messageService.show(Severity.Error, error); + this._messageService.show(Severity.Error, error); timer.stop(); }); return this._widget; } - private clear():boolean { + private clear(): boolean { if (this._startTime !== -1) { - this.telemetryService.publicLog('zoneWidgetShown', { + this._telemetryService.publicLog('zoneWidgetShown', { mode: 'reference search', elapsedTime: Date.now() - this._startTime }); @@ -237,7 +186,7 @@ export class FindReferencesController implements editorCommon.IEditorContributio } var result = false; - if(this._widget) { + if (this._widget) { this._widget.dispose(); this._widget = null; result = true; @@ -254,8 +203,50 @@ export class FindReferencesController implements editorCommon.IEditorContributio return result; } -} + private _gotoReference(ref: OneReference): void { + this._ignoreModelChangeEvent = true; + const {resource, range} = ref; + + this._editorService.openEditor({ + resource, + options: { selection: range } + }).done(openedEditor => { + this._ignoreModelChangeEvent = false; + + if (!openedEditor || openedEditor.getControl() !== this._editor) { + // TODO@Alex TODO@Joh + // when opening the current reference we might end up + // in a different editor instance. that means we also have + // a different instance of this reference search controller + // and cannot hold onto the widget (which likely doesn't + // exist). Instead of bailing out we should find the + // 'sister' action and pass our current model on to it. + this.clear(); + return; + } + this._widget.show(range); + this._widget.focus(); + + }, (err) => { + this._ignoreModelChangeEvent = false; + onUnexpectedError(err); + }); + } + + private _openReference(ref: OneReference, sideBySide: boolean): void { + const {resource, range} = ref; + this._editorService.openEditor({ + resource, + options: { selection: range } + }, sideBySide); + + // clear stage + if (!sideBySide) { + this.clear(); + } + } +} export class ReferenceAction extends EditorAction { diff --git a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchModel.ts b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchModel.ts index 80d1662df3a..7e54ddba5e9 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchModel.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchModel.ts @@ -6,6 +6,7 @@ import * as collections from 'vs/base/common/collections'; import {EventEmitter} from 'vs/base/common/eventEmitter'; +import Event, {fromEventEmitter} from 'vs/base/common/event'; import {basename, dirname} from 'vs/base/common/paths'; import * as strings from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; @@ -16,19 +17,16 @@ import {Range} from 'vs/editor/common/core/range'; import {IModel, IPosition, IRange} from 'vs/editor/common/editorCommon'; import {IReference} from 'vs/editor/common/modes'; -export namespace EventType { - export var OnReferenceRangeChanged = 'refrence.rangeChanged'; - export var CurrentReferenceChanged = 'reference.currentChanged'; -} - export class OneReference { private _id: string; - private _range: IRange; - constructor(private _parent: FileReferences, reference: IReference) { + constructor( + private _parent: FileReferences, + private _range: IRange, + private _eventBus: EventEmitter + ) { this._id = generateUuid(); - this._range = reference.range; } public get id(): string { @@ -61,7 +59,7 @@ export class OneReference { public set range(value: IRange) { this._range = value; - this.parent.parent.emit(EventType.OnReferenceRangeChanged, this); + this._eventBus.emit('ref/changed', this); } } @@ -145,16 +143,14 @@ export class FileReferences { } } -export class ReferencesModel extends EventEmitter { +export class ReferencesModel { private _references: FileReferences[]; - private _currentReference: OneReference; + private _eventBus = new EventEmitter(); + + onDidChangeReferenceRange: Event = fromEventEmitter(this._eventBus, 'ref/changed'); constructor(references: IReference[], editorService: IEditorService) { - super([ - EventType.CurrentReferenceChanged, - EventType.OnReferenceRangeChanged - ]); let referencesByFile: { [n: string]: FileReferences } = Object.create(null); let seen: { [n: string]: boolean } = Object.create(null); @@ -169,7 +165,7 @@ export class ReferencesModel extends EventEmitter { let fileReferences = new FileReferences(this, resource, editorService); fileReferences = collections.lookupOrInsert(referencesByFile, fileReferences.id, fileReferences); - fileReferences.children.push(new OneReference(fileReferences, reference)); + fileReferences.children.push(new OneReference(fileReferences, reference.range, this._eventBus)); } }); @@ -181,15 +177,6 @@ export class ReferencesModel extends EventEmitter { return this._references; } - public get currentReference(): OneReference { - return this._currentReference; - } - - public set currentReference(reference: OneReference) { - this._currentReference = reference; - this.emit(EventType.CurrentReferenceChanged, this); - } - public nextReference(reference: OneReference): OneReference { var idx = reference.parent.children.indexOf(reference), diff --git a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts index 5a2ecb7ee09..ca3f188a9c1 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts @@ -13,7 +13,6 @@ import Event, {Emitter} from 'vs/base/common/event'; import {IDisposable, cAll, dispose, Disposables} from 'vs/base/common/lifecycle'; import {Schemas} from 'vs/base/common/network'; import * as strings from 'vs/base/common/strings'; -import URI from 'vs/base/common/uri'; import {TPromise} from 'vs/base/common/winjs.base'; import {$, Builder} from 'vs/base/browser/builder'; import * as dom from 'vs/base/browser/dom'; @@ -37,7 +36,7 @@ import {Model} from 'vs/editor/common/model/model'; import {ICodeEditor, IMouseTarget} from 'vs/editor/browser/editorBrowser'; import {EmbeddedCodeEditorWidget} from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import {PeekViewWidget, IPeekViewService} from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; -import {EventType, FileReferences, OneReference, ReferencesModel} from './referenceSearchModel'; +import {FileReferences, OneReference, ReferencesModel} from './referenceSearchModel'; class DecorationsManager implements IDisposable { @@ -433,6 +432,11 @@ export interface LayoutData { heightInLines: number; } +export interface SelectionEvent { + kind: 'goto' | 'show' | 'side' | 'open'; + element: OneReference; +} + /** * ZoneWidget that is shown inside the editor */ @@ -440,10 +444,11 @@ export class ReferenceWidget extends PeekViewWidget { public static INNER_EDITOR_CONTEXT_KEY = 'inReferenceSearchEditor'; - private _decorationsManager: DecorationsManager; private _model: ReferencesModel; - private _callOnModel: IDisposable[] = []; - private _onDidDoubleClick = new Emitter<{ reference: URI, range: Range, originalEvent: MouseEvent }>(); + private _decorationsManager: DecorationsManager; + + private _disposeOnNewModel: IDisposable[] = []; + private _onDidSelectReference = new Emitter(); private _tree: Tree; private _treeContainer: Builder; @@ -472,23 +477,26 @@ export class ReferenceWidget extends PeekViewWidget { super.dispose(); } - get onDidDoubleClick():Event<{ reference: URI, range: Range, originalEvent: MouseEvent }> { - return this._onDidDoubleClick.event; + get onDidSelectReference(): Event { + return this._onDidSelectReference.event; } show(where: editorCommon.IRange) { + this.editor.revealRangeInCenterIfOutsideViewport(where); super.show(where, this.layoutData.heightInLines || 18); } + focus(): void { + this._tree.DOMFocus(); + } + protected _onTitleClick(e: MouseEvent): void { - if (!this._preview || !this._preview.getModel()) { - return; + if (this._preview && this._preview.getModel()) { + this._onDidSelectReference.fire({ + element: this._getFocusedReference(), + kind: e.ctrlKey || e.metaKey ? 'side' : 'open' + }); } - var model = this._preview.getModel(), - lineNumber = this._preview.getPosition().lineNumber, - titleRange = new Range(lineNumber, 1, lineNumber, model.getLineMaxColumn(lineNumber)); - - this._onDidDoubleClick.fire({ reference: this._getFocusedReference(), range: titleRange, originalEvent: e }); } protected _fillBody(containerElement: HTMLElement): void { @@ -574,65 +582,56 @@ export class ReferenceWidget extends PeekViewWidget { this._preview.layout(); } + public showMessage(message: string): void { + this.setTitle(''); + this._messageContainer.innerHtml(message).show(); + } + public setModel(newModel: ReferencesModel): void { // clean up - this._callOnModel = dispose(this._callOnModel); + this._disposeOnNewModel = dispose(this._disposeOnNewModel); this._model = newModel; if (this._model) { this._onNewModel(); } } - public showMessage(message: string): void { - this.setTitle(''); - this._messageContainer.innerHtml(message).show(); - } - private _onNewModel(): void { this._messageContainer.hide(); this._decorationsManager = new DecorationsManager(this._preview, this._model); - this._callOnModel.push(this._decorationsManager); + this._disposeOnNewModel.push(this._decorationsManager); // listen on model changes - this._callOnModel.push(this._model.addListener2(EventType.OnReferenceRangeChanged, (reference: OneReference) => { - this._tree.refresh(reference); - })); + this._disposeOnNewModel.push(this._model.onDidChangeReferenceRange(reference => this._tree.refresh(reference))); // listen on selection and focus - this._callOnModel.push(this._tree.addListener2(Controller.Events.FOCUSED, (element) => { + this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.FOCUSED, (element) => { if (element instanceof OneReference) { - this._showReferencePreview(element); + this._onDidSelectReference.fire({ element, kind: 'show' }); + this._revealReference(element); } })); - this._callOnModel.push(this._tree.addListener2(Controller.Events.SELECTED, (element: any) => { + this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.SELECTED, (element: any) => { if (element instanceof OneReference) { - this._showReferencePreview(element); - this._model.currentReference = element; + this._onDidSelectReference.fire({ element, kind: 'goto' }); + this._revealReference(element); } })); - this._callOnModel.push(this._tree.addListener2(Controller.Events.OPEN_TO_SIDE, (element: any) => { + this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.OPEN_TO_SIDE, (element: any) => { if (element instanceof OneReference) { - this._editorService.openEditor({ - resource: (element).resource, - options: { - selection: element.range - } - }, true); + this._onDidSelectReference.fire({ element, kind: 'side' }); } })); - var input = this._model.children.length === 1 ? this._model.children[0] : this._model; - - this._tree.setInput(input).then(() => { - this._tree.setSelection([this._model.currentReference]); - }).done(null, onUnexpectedError); - // listen on editor - this._callOnModel.push(this._preview.addListener2(editorCommon.EventType.MouseDown, (e: { event: MouseEvent; target: IMouseTarget; }) => { + this._disposeOnNewModel.push(this._preview.addListener2(editorCommon.EventType.MouseDown, (e: { event: MouseEvent; target: IMouseTarget; }) => { if (e.event.detail === 2) { - this._onDidDoubleClick.fire({ reference: this._getFocusedReference(), range: e.target.range, originalEvent: e.event }); + this._onDidSelectReference.fire({ + element: this._getFocusedReference(), + kind: (e.event.ctrlKey || e.event.metaKey) ? 'side' : 'open' + }); } })); @@ -644,28 +643,31 @@ export class ReferenceWidget extends PeekViewWidget { this._tree.layout(); this.focus(); - // preview the current reference - this._showReferencePreview(this._model.nextReference(this._model.currentReference)); + // pick input and a reference to begin with + const input = this._model.children.length === 1 ? this._model.children[0] : this._model; + const selection = this._model.children[0].children[0]; + this._tree.setInput(input).then(() => this._revealReference(selection)); } - private _getFocusedReference(): URI { - var element = this._tree.getFocus(); + private _getFocusedReference(): OneReference { + const element = this._tree.getFocus(); if (element instanceof OneReference) { - return (element).resource; + return element; } else if (element instanceof FileReferences) { - var referenceFile = (element); - if (referenceFile.children.length > 0) { - return referenceFile.children[0].resource; + if (element.children.length > 0) { + return element.children[0]; } } - return null; } - public focus(): void { - this._tree.DOMFocus(); - } + private _revealReference(reference: OneReference): void { - private _showReferencePreview(reference: OneReference): void { + // Update widget header + if (reference.resource.scheme !== Schemas.inMemory) { + this.setTitle(reference.name, getPathLabel(reference.directory, this._contextService)); + } else { + this.setTitle(nls.localize('peekView.alternateTitle', "References")); + } // show in editor this._editorService.resolveEditorModel({ resource: reference.resource }).done((model) => { @@ -679,22 +681,12 @@ export class ReferenceWidget extends PeekViewWidget { this._preview.setModel(this._previewNotAvailableMessage); } - // Update widget header - if (reference.resource.scheme !== Schemas.inMemory) { - this.setTitle(reference.name, getPathLabel(reference.directory, this._contextService)); - } else { - this.setTitle(nls.localize('peekView.alternateTitle', "References")); - } - }, onUnexpectedError); // show in tree - this._tree.reveal(reference) - .then(() => { - this._tree.setSelection([reference]); - this._tree.setFocus(reference); - }) - .done(null, onUnexpectedError); + this._tree.reveal(reference).then(() => { + this._tree.setSelection([reference]); + this._tree.setFocus(reference); + }); } - } -- GitLab