提交 0d827d8e 编写于 作者: J Johannes Rieken

for multiple definitions open peek at the first location and close it when...

for multiple definitions open peek at the first location and close it when jumping to another, fixes #5893
上级 e086d4ec
......@@ -7,7 +7,6 @@
import 'vs/css!./goToDeclaration';
import * as nls from 'vs/nls';
import {coalesce} from 'vs/base/common/arrays';
import {Throttler} from 'vs/base/common/async';
import {onUnexpectedError} from 'vs/base/common/errors';
import {ListenerUnbind} from 'vs/base/common/eventEmitter';
......@@ -16,7 +15,6 @@ import {KeyCode, KeyMod} from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import * as strings from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import {TPromise} from 'vs/base/common/winjs.base';
import * as browser from 'vs/base/browser/browser';
import {IKeyboardEvent} from 'vs/base/browser/keyboardEvent';
......@@ -32,98 +30,38 @@ import {tokenizeToHtmlContent} from 'vs/editor/common/modes/textToHtmlTokenizer'
import {ICodeEditor, IEditorMouseEvent, IMouseTarget} from 'vs/editor/browser/editorBrowser';
import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions';
import {getDeclarationsAtPosition} from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration';
import {FindReferencesController} from 'vs/editor/contrib/referenceSearch/browser/referenceSearch';
import {ReferencesController} from 'vs/editor/contrib/referenceSearch/browser/referenceSearch';
const DEFAULT_BEHAVIOR = Behaviour.WidgetFocus | Behaviour.ShowInContextMenu | Behaviour.UpdateOnCursorPositionChange;
function metaTitle(references: IReference[]): string {
if (references.length > 1) {
return nls.localize('meta.title', " – {0} definitions", references.length);
}
}
export abstract class GoToTypeAction extends EditorAction {
export class DefitiontionActionConfig {
constructor(
descriptor: editorCommon.IEditorActionDescriptorData,
editor: editorCommon.ICommonCodeEditor,
private _messageService: IMessageService,
private _editorService: IEditorService,
condition = DEFAULT_BEHAVIOR
public condition = Behaviour.WidgetFocus | Behaviour.ShowInContextMenu | Behaviour.UpdateOnCursorPositionChange,
public openToSide = false,
public openInPeek = false,
public filterCurrent = true
) {
super(descriptor, editor, condition);
}
public run(): TPromise<any> {
let model = this.editor.getModel();
let position = this.editor.getPosition();
let promise = this._resolve(model.getAssociatedResource(), { lineNumber: position.lineNumber, column: position.column });
return promise.then(references => {
// remove falsy entries
references = coalesce(references);
if (!references || references.length === 0) {
return;
}
// only use the start position
references = references.map(reference => {
return {
resource: reference.resource,
range: Range.collapseToStart(reference.range)
};
});
// open and reveal
if (references.length === 1 && !this._showSingleReferenceInPeek()) {
return this._editorService.openEditor({
resource: references[0].resource,
options: { selection: references[0].range }
}, this.openToTheSide);
} else {
let controller = FindReferencesController.getController(this.editor);
return controller.processRequest(this.editor.getSelection(), TPromise.as(references), metaTitle);
}
}, (err) => {
// report an error
this._messageService.show(Severity.Error, err);
return false;
});
}
protected get openToTheSide(): boolean {
return false;
}
protected abstract _resolve(resource: URI, position: editorCommon.IPosition): TPromise<IReference[]>;
protected _showSingleReferenceInPeek() {
return false;
//
}
}
export class GoToTypeDeclarationActions extends GoToTypeAction {
public static ID = 'editor.action.goToTypeDeclaration';
export class DefinitionAction extends EditorAction {
constructor(
descriptor: editorCommon.IEditorActionDescriptorData,
editor: editorCommon.ICommonCodeEditor,
@IMessageService messageService: IMessageService,
@IEditorService editorService: IEditorService
private _messageService: IMessageService,
private _editorService: IEditorService,
private _configuration: DefitiontionActionConfig
) {
super(descriptor, editor, messageService, editorService);
super(descriptor, editor, _configuration.condition);
}
public getGroupId(): string {
return '1_goto/3_visitTypeDefinition';
return '1_goto/2_visitDefinition';
}
public isSupported(): boolean {
return !!this.editor.getModel().getMode().typeDeclarationSupport && super.isSupported();
return DeclarationRegistry.has(this.editor.getModel()) && super.isSupported();
}
public getEnablementState(): boolean {
......@@ -131,65 +69,95 @@ export class GoToTypeDeclarationActions extends GoToTypeAction {
return false;
}
let model = this.editor.getModel(),
position = this.editor.getSelection().getStartPosition();
return model.getMode().typeDeclarationSupport.canFindTypeDeclaration(
model.getLineContext(position.lineNumber),
position.column - 1
);
const model = this.editor.getModel();
const position = this.editor.getSelection().getStartPosition();
return DeclarationRegistry.all(model).some(provider => {
return provider.canFindDeclaration(
model.getLineContext(position.lineNumber),
position.column - 1);
});
}
protected _resolve(resource: URI, position: editorCommon.IPosition): TPromise<IReference[]> {
let typeDeclarationSupport = this.editor.getModel().getMode().typeDeclarationSupport;
if (typeDeclarationSupport) {
return typeDeclarationSupport.findTypeDeclaration(<any>resource, position).then(value => [value]);
}
}
}
public run(): TPromise<any> {
export abstract class BaseGoToDeclarationAction extends GoToTypeAction {
let model = this.editor.getModel();
let pos = this.editor.getPosition();
constructor(
descriptor: editorCommon.IEditorActionDescriptorData,
editor: editorCommon.ICommonCodeEditor,
messageService: IMessageService,
editorService: IEditorService,
condition: Behaviour
) {
super(descriptor, editor, messageService, editorService, condition);
}
return getDeclarationsAtPosition(model, pos).then(references => {
public getGroupId(): string {
return '1_goto/2_visitDefinition';
}
if (!references) {
return;
}
public isSupported(): boolean {
return DeclarationRegistry.has(this.editor.getModel()) && super.isSupported();
}
// * remove falsy references
// * remove reference at the current pos
// * collapse ranges to start pos
let result: IReference[] = [];
for (let i = 0; i < references.length; i++) {
let reference = references[i];
if (!reference) {
continue;
}
let {resource, range} = reference;
if (!this._configuration.filterCurrent
|| resource.toString() !== model.getAssociatedResource().toString()
|| !Range.containsPosition(range, pos)) {
result.push({
resource,
range: Range.collapseToStart(range)
});
}
}
public getEnablementState(): boolean {
if (!super.getEnablementState()) {
return false;
}
if (result.length === 0) {
return;
}
let model = this.editor.getModel(),
position = this.editor.getSelection().getStartPosition();
return this._onResult(result);
return DeclarationRegistry.all(model).some(provider => {
return provider.canFindDeclaration(
model.getLineContext(position.lineNumber),
position.column - 1);
}, (err) => {
// report an error
this._messageService.show(Severity.Error, err);
return false;
});
}
private _onResult(references: IReference[]) {
if (this._configuration.openInPeek) {
this._openInPeek(this.editor, references);
} else {
let [first] = references;
this._openReference(first, this._configuration.openToSide).then(editor => {
if (references.length > 1) {
this._openInPeek(editor, references);
}
});
}
}
private _openReference(reference: IReference, sideBySide: boolean): TPromise<editorCommon.ICommonCodeEditor>{
let {resource, range} = reference;
return this._editorService.openEditor({ resource, options: { selection: range } }, sideBySide).then(editor => {
return <editorCommon.IEditor> editor.getControl();
});
}
protected _resolve(resource: URI, position: editorCommon.IPosition): TPromise<IReference[]> {
return getDeclarationsAtPosition(this.editor.getModel(), this.editor.getPosition());
private _openInPeek(target: editorCommon.ICommonCodeEditor, references: IReference[]) {
let controller = ReferencesController.getController(target);
controller.processRequest(target.getSelection(), TPromise.as(references), {
getMetaTitle: (references) => {
return references.length > 1 && nls.localize('meta.title', " – {0} definitions", references.length);
},
onGoto: (reference) => {
controller.closeReferenceSearch();
return this._openReference(reference, false);
}
});
}
}
export class GoToDeclarationAction extends BaseGoToDeclarationAction {
export class GoToDefinitionAction extends DefinitionAction {
public static ID = 'editor.action.goToDeclaration';
......@@ -199,11 +167,12 @@ export class GoToDeclarationAction extends BaseGoToDeclarationAction {
@IMessageService messageService: IMessageService,
@IEditorService editorService: IEditorService
) {
super(descriptor, editor, messageService, editorService, DEFAULT_BEHAVIOR);
super(descriptor, editor, messageService, editorService, new DefitiontionActionConfig());
}
}
export class OpenDeclarationToTheSideAction extends BaseGoToDeclarationAction {
export class OpenDefinitionToSideAction extends DefinitionAction {
public static ID = 'editor.action.openDeclarationToTheSide';
......@@ -213,15 +182,11 @@ export class OpenDeclarationToTheSideAction extends BaseGoToDeclarationAction {
@IMessageService messageService: IMessageService,
@IEditorService editorService: IEditorService
) {
super(descriptor, editor, messageService, editorService, Behaviour.WidgetFocus | Behaviour.UpdateOnCursorPositionChange);
}
protected get openToTheSide(): boolean {
return true;
super(descriptor, editor, messageService, editorService, new DefitiontionActionConfig(Behaviour.WidgetFocus | Behaviour.UpdateOnCursorPositionChange, true));
}
}
export class PreviewDeclarationAction extends BaseGoToDeclarationAction {
export class PeekDefinitionAction extends DefinitionAction {
public static ID = 'editor.action.previewDeclaration';
......@@ -231,11 +196,7 @@ export class PreviewDeclarationAction extends BaseGoToDeclarationAction {
@IMessageService messageService: IMessageService,
@IEditorService editorService: IEditorService
) {
super(descriptor, editor, messageService, editorService, DEFAULT_BEHAVIOR);
}
protected _showSingleReferenceInPeek() {
return true;
super(descriptor, editor, messageService, editorService, new DefitiontionActionConfig(void 0, void 0, true, false));
}
}
......@@ -251,7 +212,6 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
private editor: ICodeEditor;
private toUnhook: ListenerUnbind[];
private hasRequiredServices: boolean;
private decorations: string[];
private currentWordUnderMouse: editorCommon.IWordAtPosition;
private throttler: Throttler;
......@@ -262,8 +222,6 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
editor: ICodeEditor,
@IEditorService private editorService: IEditorService
) {
this.hasRequiredServices = !!this.editorService;
this.toUnhook = [];
this.decorations = [];
this.editor = editor;
......@@ -472,8 +430,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
}
private isEnabled(mouseEvent: IEditorMouseEvent, withKey?: IKeyboardEvent): boolean {
return this.hasRequiredServices &&
this.editor.getModel() &&
return this.editor.getModel() &&
(browser.isIE11orEarlier || mouseEvent.event.detail <= 1) && // IE does not support event.detail properly
mouseEvent.target.type === editorCommon.MouseTargetType.CONTENT_TEXT &&
(mouseEvent.event[GotoDefinitionWithMouseEditorContribution.TRIGGER_MODIFIER] || (withKey && withKey.keyCode === GotoDefinitionWithMouseEditorContribution.TRIGGER_KEY_VALUE)) &&
......@@ -490,44 +447,14 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
}
private gotoDefinition(target: IMouseTarget, sideBySide: boolean): TPromise<any> {
let state = this.editor.captureState(editorCommon.CodeEditorStateFlag.Position, editorCommon.CodeEditorStateFlag.Value, editorCommon.CodeEditorStateFlag.Selection, editorCommon.CodeEditorStateFlag.Scroll);
return this.findDefinition(target).then((results: IReference[]) => {
if (!results || !results.length || !state.validate(this.editor)) {
return;
}
const targetAction = sideBySide
? OpenDefinitionToSideAction.ID
: GoToDefinitionAction.ID;
let position = target.position;
let word = this.editor.getModel().getWordAtPosition(position);
// Find valid target (and not the same position as the current hovered word)
let validResults = results
.filter(result => result.range && !(word && result.range.startColumn === word.startColumn && result.range.startLineNumber === target.position.lineNumber))
.map((result) => {
return {
resource: result.resource,
range: Range.collapseToStart(result.range)
};
});
if (!validResults.length) {
return;
}
// Muli result: Show in references UI
if (validResults.length > 1) {
let controller = FindReferencesController.getController(this.editor);
return controller.processRequest(this.editor.getSelection(), TPromise.as(validResults), metaTitle);
}
// Single result: Open
return this.editorService.openEditor({
resource: validResults[0].resource,
options: {
selection: validResults[0].range
}
}, sideBySide);
});
// just run the corresponding action
this.editor.setPosition(target.position);
return this.editor.getAction(targetAction).run();
}
public getId(): string {
......@@ -542,7 +469,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
}
// register actions
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(PreviewDeclarationAction, PreviewDeclarationAction.ID, nls.localize('actions.previewDecl.label', "Peek Definition"), {
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(PeekDefinitionAction, PeekDefinitionAction.ID, nls.localize('actions.previewDecl.label', "Peek Definition"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.Alt | KeyCode.F12,
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 },
......@@ -555,15 +482,14 @@ if (platform.isWeb) {
goToDeclarationKb = KeyCode.F12;
}
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(GoToDeclarationAction, GoToDeclarationAction.ID, nls.localize('actions.goToDecl.label', "Go to Definition"), {
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(GoToDefinitionAction, GoToDefinitionAction.ID, nls.localize('actions.goToDecl.label', "Go to Definition"), {
context: ContextKey.EditorTextFocus,
primary: goToDeclarationKb
}, 'Go to Definition'));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(OpenDeclarationToTheSideAction, OpenDeclarationToTheSideAction.ID, nls.localize('actions.goToDeclToSide.label', "Open Definition to the Side"), {
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(OpenDefinitionToSideAction, OpenDefinitionToSideAction.ID, nls.localize('actions.goToDeclToSide.label', "Open Definition to the Side"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDeclarationKb)
}, 'Open Definition to the Side'));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(GoToTypeDeclarationActions, GoToTypeDeclarationActions.ID, nls.localize('actions.gotoTypeDecl.label', "Go to Type Definition"), void 0, 'Go to Type Definition'));
EditorBrowserRegistry.registerEditorContribution(GotoDefinitionWithMouseEditorContribution);
......@@ -35,9 +35,21 @@ import {ReferencesModel, OneReference} from './referenceSearchModel';
import {ReferenceWidget, LayoutData} from './referenceSearchWidget';
import {ServicesAccessor} from 'vs/platform/instantiation/common/instantiation';
export class FindReferencesController implements editorCommon.IEditorContribution {
export interface RequestOptions {
getMetaTitle(references: IReference[]): string;
onGoto(reference: IReference): TPromise<any>;
}
const defaultReferenceSearchOptions: RequestOptions = {
getMetaTitle(references: IReference[]) {
return references.length > 1 && nls.localize('meta.titleReference', " – {0} references", references.length);
},
onGoto: undefined
};
export class ReferencesController implements editorCommon.IEditorContribution {
public static ID = 'editor.contrib.findReferencesController';
public static ID = 'editor.contrib.referencesController';
private _editor: ICodeEditor;
private _widget: ReferenceWidget;
......@@ -49,8 +61,8 @@ export class FindReferencesController implements editorCommon.IEditorContributio
private _startTime: number = -1;
private _referenceSearchVisible: IKeybindingContextKey<boolean>;
static getController(editor:editorCommon.ICommonCodeEditor): FindReferencesController {
return <FindReferencesController> editor.getContribution(FindReferencesController.ID);
static getController(editor:editorCommon.ICommonCodeEditor): ReferencesController {
return <ReferencesController> editor.getContribution(ReferencesController.ID);
}
public constructor(
......@@ -69,7 +81,7 @@ export class FindReferencesController implements editorCommon.IEditorContributio
}
public getId(): string {
return FindReferencesController.ID;
return ReferencesController.ID;
}
public dispose(): void {
......@@ -88,7 +100,7 @@ export class FindReferencesController implements editorCommon.IEditorContributio
this.clear();
}
public processRequest(range: editorCommon.IEditorRange, referencesPromise: TPromise<IReference[]>, metaTitleFn:(references:IReference[])=>string) : ReferenceWidget {
public processRequest(range: editorCommon.IEditorRange, referencesPromise: TPromise<IReference[]>, options: RequestOptions) : void {
var widgetPosition = !this._widget ? null : this._widget.position;
// clean up from previous invocation
......@@ -129,7 +141,11 @@ export class FindReferencesController implements editorCommon.IEditorContributio
this._openReference(element, kind === 'side');
break;
case 'goto':
this._gotoReference(element);
if (options.onGoto) {
options.onGoto(element);
} else {
this._gotoReference(element);
}
break;
}
}).dispose);
......@@ -162,7 +178,7 @@ export class FindReferencesController implements editorCommon.IEditorContributio
// show widget
this._startTime = Date.now();
if (this._widget) {
this._widget.setMetaTitle(metaTitleFn(references));
this._widget.setMetaTitle(options.getMetaTitle(references));
this._widget.setModel(this._model);
}
timer.stop();
......@@ -171,8 +187,6 @@ export class FindReferencesController implements editorCommon.IEditorContributio
this._messageService.show(Severity.Error, error);
timer.stop();
});
return this._widget;
}
private clear(): boolean {
......@@ -299,14 +313,8 @@ export class ReferenceAction extends EditorAction {
let range = this.editor.getSelection();
let model = this.editor.getModel();
let request = findReferences(model, range.getStartPosition());
let controller = FindReferencesController.getController(this.editor);
return TPromise.as(controller.processRequest(range, request, metaTitle)).then(() => true);
}
}
function metaTitle(references: IReference[]): string {
if (references.length > 1) {
return nls.localize('meta.titleReference', " – {0} references", references.length);
let controller = ReferencesController.getController(this.editor);
return TPromise.as(controller.processRequest(range, request, defaultReferenceSearchOptions)).then(() => true);
}
}
......@@ -327,9 +335,9 @@ let findReferencesCommand: ICommandHandler = (accessor:ServicesAccessor, resourc
}
let request = findReferences(control.getModel(), position);
let controller = FindReferencesController.getController(control);
let controller = ReferencesController.getController(control);
let range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
return TPromise.as(controller.processRequest(range, request, metaTitle));
return TPromise.as(controller.processRequest(range, request, defaultReferenceSearchOptions));
});
};
......@@ -345,16 +353,16 @@ let showReferencesCommand: ICommandHandler = (accessor:ServicesAccessor, resourc
return;
}
let controller = FindReferencesController.getController(control);
let controller = ReferencesController.getController(control);
let range = Position.asEmptyRange(position);
return TPromise.as(controller.processRequest(Range.lift(range), TPromise.as(references), metaTitle)).then(() => true);
return TPromise.as(controller.processRequest(Range.lift(range), TPromise.as(references), defaultReferenceSearchOptions)).then(() => true);
});
};
var CONTEXT_REFERENCE_SEARCH_VISIBLE = 'referenceSearchVisible';
// register action
EditorBrowserRegistry.registerEditorContribution(FindReferencesController);
EditorBrowserRegistry.registerEditorContribution(ReferencesController);
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ReferenceAction, ReferenceAction.ID, nls.localize('references.action.name', "Show References"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.Shift | KeyCode.F12
......@@ -385,7 +393,7 @@ KeybindingsRegistry.registerCommandDesc({
function closeActiveReferenceSearch(accessor, args) {
var outerEditor = getOuterEditor(accessor, args);
if (outerEditor) {
var controller = FindReferencesController.getController(outerEditor);
var controller = ReferencesController.getController(outerEditor);
controller.closeReferenceSearch();
}
}
......
......@@ -609,14 +609,14 @@ export class ReferenceWidget extends PeekViewWidget {
// listen on selection and focus
this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.FOCUSED, (element) => {
if (element instanceof OneReference) {
this._onDidSelectReference.fire({ element, kind: 'show' });
this._revealReference(element);
this._onDidSelectReference.fire({ element, kind: 'show' });
}
}));
this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.SELECTED, (element: any) => {
if (element instanceof OneReference) {
this._onDidSelectReference.fire({ element, kind: 'goto' });
this._revealReference(element);
this._onDidSelectReference.fire({ element, kind: 'goto' });
}
}));
this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.OPEN_TO_SIDE, (element: any) => {
......@@ -669,9 +669,17 @@ export class ReferenceWidget extends PeekViewWidget {
this.setTitle(nls.localize('peekView.alternateTitle', "References"));
}
// show in editor
this._editorService.resolveEditorModel({ resource: reference.resource }).done((model) => {
TPromise.join([
this._editorService.resolveEditorModel({ resource: reference.resource }),
this._tree.reveal(reference)
]).done(values => {
if (!this._model) {
// disposed
return;
}
// show in editor
let [model] = values;
if (model) {
this._preview.setModel(model.textEditorModel);
var sel = Range.lift(reference.range).collapseToStart();
......@@ -681,12 +689,10 @@ export class ReferenceWidget extends PeekViewWidget {
this._preview.setModel(this._previewNotAvailableMessage);
}
}, onUnexpectedError);
// show in tree
this._tree.reveal(reference).then(() => {
// show in tree
this._tree.setSelection([reference]);
this._tree.setFocus(reference);
});
}, onUnexpectedError);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册