Files
azuredatastudio/src/vs/editor/contrib/referenceSearch/referenceSearch.ts
2018-06-05 11:24:51 -07:00

302 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorService } from 'vs/platform/editor/common/editor';
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Location, ReferenceProviderRegistry } from 'vs/editor/common/modes';
import { PeekContext, getOuterEditor } from './peekViewWidget';
import { ReferencesController, RequestOptions, ctxReferenceSearchVisible } from './referencesController';
import { ReferencesModel, OneReference } from './referencesModel';
import { asWinJsPromise } from 'vs/base/common/async';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel } from 'vs/editor/common/model';
import { IListService } from 'vs/platform/list/browser/listService';
import { ctxReferenceWidgetSearchTreeFocused } from 'vs/editor/contrib/referenceSearch/referencesWidget';
const defaultReferenceSearchOptions: RequestOptions = {
getMetaTitle(model) {
return model.references.length > 1 && nls.localize('meta.titleReference', " {0} references", model.references.length);
}
};
export class ReferenceController implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.referenceController';
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService
) {
if (editor instanceof EmbeddedCodeEditorWidget) {
PeekContext.inPeekEditor.bindTo(contextKeyService);
}
}
public dispose(): void {
}
public getId(): string {
return ReferenceController.ID;
}
}
export class ReferenceAction extends EditorAction {
constructor() {
super({
id: 'editor.action.referenceSearch.trigger',
label: nls.localize('references.action.label', "Find All References"),
alias: 'Find All References',
precondition: ContextKeyExpr.and(
EditorContextKeys.hasReferenceProvider,
PeekContext.notInPeekEditor,
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.Shift | KeyCode.F12
},
menuOpts: {
group: 'navigation',
order: 1.5
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = ReferencesController.get(editor);
if (!controller) {
return;
}
let range = editor.getSelection();
let model = editor.getModel();
let references = provideReferences(model, range.getStartPosition()).then(references => new ReferencesModel(references));
controller.toggleWidget(range, references, defaultReferenceSearchOptions);
}
}
registerEditorContribution(ReferenceController);
registerEditorAction(ReferenceAction);
let findReferencesCommand: ICommandHandler = (accessor: ServicesAccessor, resource: URI, position: IPosition) => {
if (!(resource instanceof URI)) {
throw new Error('illegal argument, uri');
}
if (!position) {
throw new Error('illegal argument, position');
}
return accessor.get(IEditorService).openEditor({ resource }).then(editor => {
let control = editor.getControl();
if (!isCodeEditor(control)) {
return undefined;
}
let controller = ReferencesController.get(control);
if (!controller) {
return undefined;
}
let references = provideReferences(control.getModel(), Position.lift(position)).then(references => new ReferencesModel(references));
let range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
return TPromise.as(controller.toggleWidget(range, references, defaultReferenceSearchOptions));
});
};
let showReferencesCommand: ICommandHandler = (accessor: ServicesAccessor, resource: URI, position: IPosition, references: Location[]) => {
if (!(resource instanceof URI)) {
throw new Error('illegal argument, uri expected');
}
return accessor.get(IEditorService).openEditor({ resource: resource }).then(editor => {
let control = editor.getControl();
if (!isCodeEditor(control)) {
return undefined;
}
let controller = ReferencesController.get(control);
if (!controller) {
return undefined;
}
return TPromise.as(controller.toggleWidget(
new Range(position.lineNumber, position.column, position.lineNumber, position.column),
TPromise.as(new ReferencesModel(references)),
defaultReferenceSearchOptions)).then(() => true);
});
};
// register commands
CommandsRegistry.registerCommand({
id: 'editor.action.findReferences',
handler: findReferencesCommand
});
CommandsRegistry.registerCommand({
id: 'editor.action.showReferences',
handler: showReferencesCommand,
description: {
description: 'Show references at a position in a file',
args: [
{ name: 'uri', description: 'The text document in which to show references', constraint: URI },
{ name: 'position', description: 'The position at which to show', constraint: Position.isIPosition },
{ name: 'locations', description: 'An array of locations.', constraint: Array },
]
}
});
function closeActiveReferenceSearch(accessor: ServicesAccessor, args: any) {
withController(accessor, controller => controller.closeWidget());
}
function openReferenceToSide(accessor: ServicesAccessor, args: any) {
const listService = accessor.get(IListService);
const focus = listService.lastFocusedList && listService.lastFocusedList.getFocus();
if (focus instanceof OneReference) {
withController(accessor, controller => controller.openReference(focus, true));
}
}
function withController(accessor: ServicesAccessor, fn: (controller: ReferencesController) => void): void {
var outerEditor = getOuterEditor(accessor);
if (!outerEditor) {
return;
}
let controller = ReferencesController.get(outerEditor);
if (!controller) {
return;
}
fn(controller);
}
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'goToNextReference',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
primary: KeyCode.F4,
when: ctxReferenceSearchVisible,
handler(accessor) {
withController(accessor, controller => {
controller.goToNextOrPreviousReference(true);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'goToNextReferenceFromEmbeddedEditor',
weight: KeybindingsRegistry.WEIGHT.editorContrib(50),
primary: KeyCode.F4,
when: PeekContext.inPeekEditor,
handler(accessor) {
withController(accessor, controller => {
controller.goToNextOrPreviousReference(true);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'goToPreviousReference',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
primary: KeyMod.Shift | KeyCode.F4,
when: ctxReferenceSearchVisible,
handler(accessor) {
withController(accessor, controller => {
controller.goToNextOrPreviousReference(false);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'goToPreviousReferenceFromEmbeddedEditor',
weight: KeybindingsRegistry.WEIGHT.editorContrib(50),
primary: KeyMod.Shift | KeyCode.F4,
when: PeekContext.inPeekEditor,
handler(accessor) {
withController(accessor, controller => {
controller.goToNextOrPreviousReference(false);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'closeReferenceSearch',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape],
when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')),
handler: closeActiveReferenceSearch
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'closeReferenceSearchEditor',
weight: KeybindingsRegistry.WEIGHT.editorContrib(-101),
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape],
when: ContextKeyExpr.and(PeekContext.inPeekEditor, ContextKeyExpr.not('config.editor.stablePeek')),
handler: closeActiveReferenceSearch
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'openReferenceToSide',
weight: KeybindingsRegistry.WEIGHT.editorContrib(),
primary: KeyMod.CtrlCmd | KeyCode.Enter,
mac: {
primary: KeyMod.WinCtrl | KeyCode.Enter
},
when: ContextKeyExpr.and(ctxReferenceSearchVisible, ctxReferenceWidgetSearchTreeFocused),
handler: openReferenceToSide
});
export function provideReferences(model: ITextModel, position: Position): TPromise<Location[]> {
// collect references from all providers
const promises = ReferenceProviderRegistry.ordered(model).map(provider => {
return asWinJsPromise((token) => {
return provider.provideReferences(model, position, { includeDeclaration: true }, token);
}).then(result => {
if (Array.isArray(result)) {
return <Location[]>result;
}
return undefined;
}, err => {
onUnexpectedExternalError(err);
});
});
return TPromise.join(promises).then(references => {
let result: Location[] = [];
for (let ref of references) {
if (ref) {
result.push(...ref);
}
}
return result;
});
}
registerDefaultLanguageCommand('_executeReferenceProvider', provideReferences);