Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt list commands for editor's "Find References" peek UI #41275

Merged
merged 6 commits into from Jan 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/vs/editor/contrib/referenceSearch/referenceSearch.ts
Expand Up @@ -19,13 +19,15 @@ import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorCon
import { Location, ReferenceProviderRegistry } from 'vs/editor/common/modes';
import { PeekContext, getOuterEditor } from './peekViewWidget';
import { ReferencesController, RequestOptions, ctxReferenceSearchVisible } from './referencesController';
import { ReferencesModel } from './referencesModel';
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) {
Expand Down Expand Up @@ -166,6 +168,19 @@ CommandsRegistry.registerCommand({
});

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;
Expand All @@ -176,12 +191,12 @@ function closeActiveReferenceSearch(accessor: ServicesAccessor, args: any) {
return;
}

controller.closeWidget();
fn(controller);
}

KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'closeReferenceSearch',
weight: KeybindingsRegistry.WEIGHT.editorContrib(50),
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why workbench and not editor? This feature is also available in the monaco editor....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrieken the workbench is also registering ESC for trees and lists here: https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/electron-browser/commands.ts#L344. This is needed to preserve the behaviour of ESC closing the references widget. It is a bit ugly, I agree. The alternative would be to register this command also from the workbench maybe.

primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape],
when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')),
Expand All @@ -197,6 +212,16 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler: closeActiveReferenceSearch
});

KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'openReferenceToSide',
weight: KeybindingsRegistry.WEIGHT.editorContrib(),
primary: KeyMod.CtrlCmd | KeyCode.Enter,
mac: {
primary: KeyMod.WinCtrl | KeyCode.Enter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not cmd on macs? We use that in the explorer and elsewhere

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrieken you must have changed the keybindings then, the default is Ctrl+Enter on mac for Explorer, Search and Problems view:

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true story...

},
when: ContextKeyExpr.and(ctxReferenceSearchVisible, ctxReferenceWidgetSearchTreeFocused),
handler: openReferenceToSide
});

export function provideReferences(model: ITextModel, position: Position): TPromise<Location[]> {

Expand Down
4 changes: 2 additions & 2 deletions src/vs/editor/contrib/referenceSearch/referencesController.ts
Expand Up @@ -127,7 +127,7 @@ export class ReferencesController implements editorCommon.IEditorContribution {
break;
}
case 'side':
this._openReference(element, kind === 'side');
this.openReference(element, kind === 'side');
break;
case 'goto':
if (options.onGoto) {
Expand Down Expand Up @@ -223,7 +223,7 @@ export class ReferencesController implements editorCommon.IEditorContribution {
});
}

private _openReference(ref: Location, sideBySide: boolean): void {
public openReference(ref: Location, sideBySide: boolean): void {
const { uri, range } = ref;
this._editorService.openEditor({
resource: uri,
Expand Down
125 changes: 38 additions & 87 deletions src/vs/editor/contrib/referenceSearch/referencesWidget.ts
Expand Up @@ -24,7 +24,6 @@ import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { FileLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import * as tree from 'vs/base/parts/tree/browser/tree';
import { DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Range, IRange } from 'vs/editor/common/core/range';
Expand All @@ -42,6 +41,8 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import URI from 'vs/base/common/uri';
import { TrackedRangeStickiness, IModelDeltaDecoration } from 'vs/editor/common/model';
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Location } from 'vs/editor/common/modes';

class DecorationsManager implements IDisposable {
Expand Down Expand Up @@ -285,65 +286,6 @@ class Controller extends DefaultController {
return false;
}

public onEnter(tree: tree.ITree, event: IKeyboardEvent): boolean {
var element = tree.getFocus();
if (element instanceof FileReferences) {
return this._expandCollapse(tree, element);
}

var result = super.onEnter(tree, event);
if (event.ctrlKey || event.metaKey) {
this._onDidOpenToSide.fire(element);
} else {
this._onDidSelect.fire(element);
}
return result;
}

public onUp(tree: tree.ITree, event: IKeyboardEvent): boolean {
super.onUp(tree, event);
this._fakeFocus(tree, event);
return true;
}

public onPageUp(tree: tree.ITree, event: IKeyboardEvent): boolean {
super.onPageUp(tree, event);
this._fakeFocus(tree, event);
return true;
}

public onLeft(tree: tree.ITree, event: IKeyboardEvent): boolean {
super.onLeft(tree, event);
this._fakeFocus(tree, event);
return true;
}

public onDown(tree: tree.ITree, event: IKeyboardEvent): boolean {
super.onDown(tree, event);
this._fakeFocus(tree, event);
return true;
}

public onPageDown(tree: tree.ITree, event: IKeyboardEvent): boolean {
super.onPageDown(tree, event);
this._fakeFocus(tree, event);
return true;
}

public onRight(tree: tree.ITree, event: IKeyboardEvent): boolean {
super.onRight(tree, event);
this._fakeFocus(tree, event);
return true;
}

private _fakeFocus(tree: tree.ITree, event: IKeyboardEvent): void {
// focus next item
var focus = tree.getFocus();
tree.setSelection([focus]);
// send out event
this._onDidFocus.fire(focus);
}

dispose(): void {
this._onDidFocus.dispose();
this._onDidSelect.dispose();
Expand Down Expand Up @@ -562,6 +504,8 @@ export interface SelectionEvent {
element: Location;
}

export const ctxReferenceWidgetSearchTreeFocused = new RawContextKey<boolean>('referenceSearchTreeFocused', true);

/**
* ZoneWidget that is shown inside the editor
*/
Expand All @@ -574,7 +518,7 @@ export class ReferenceWidget extends PeekViewWidget {
private _callOnDispose: IDisposable[] = [];
private _onDidSelectReference = new Emitter<SelectionEvent>();

private _tree: Tree;
private _tree: WorkbenchTree;
private _treeContainer: Builder;
private _sash: VSash;
private _preview: ICodeEditor;
Expand Down Expand Up @@ -695,32 +639,39 @@ export class ReferenceWidget extends PeekViewWidget {
accessibilityProvider: new AriaProvider()
};

var options: tree.ITreeOptions = {
twistiePixels: 20,
ariaLabel: nls.localize('treeAriaLabel', "References"),
keyboardSupport: false
};

this._tree = this._instantiationService.createInstance(WorkbenchTree, div.getHTMLElement(), config, options);
this._callOnDispose.push(attachListStyler(this._tree, this._themeService));

ctxReferenceWidgetSearchTreeFocused.bindTo(this._tree.contextKeyService);

// listen on selection and focus
this._disposables.push(controller.onDidFocus((element) => {
var onEvent = (element: any, kind: 'show' | 'goto' | 'side') => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var 😱

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrieken that is consistent with other usages of var in _fillBody, I did not want to change that style. There are more vars throughout the file, you can clean that up afterwards if you want.

if (element instanceof OneReference) {
this._revealReference(element);
this._onDidSelectReference.fire({ element, kind: 'show', source: 'tree' });
if (kind === 'show') {
this._revealReference(element);
}
this._onDidSelectReference.fire({ element, kind, source: 'tree' });
}
}));

this._disposables.push(controller.onDidSelect((element: any) => {
if (element instanceof OneReference) {
this._onDidSelectReference.fire({ element, kind: 'goto', source: 'tree' });
};
this._disposables.push(this._tree.onDidChangeFocus(event => {
if (event && event.payload && event.payload.origin === 'keyboard') {
onEvent(event.focus, 'show'); // only handle events from keyboard, mouse/touch is handled by other listeners below
}
}));
this._disposables.push(controller.onDidOpenToSide((element: any) => {
if (element instanceof OneReference) {
this._onDidSelectReference.fire({ element, kind: 'side', source: 'tree' });
this._disposables.push(this._tree.onDidChangeSelection(event => {
if (event && event.payload && event.payload.origin === 'keyboard') {
onEvent(event.selection[0], 'goto'); // only handle events from keyboard, mouse/touch is handled by other listeners below
}
}));

var options = {
allowHorizontalScroll: false,
twistiePixels: 20,
ariaLabel: nls.localize('treeAriaLabel', "References")
};
this._tree = new Tree(div.getHTMLElement(), config, options);
this._callOnDispose.push(attachListStyler(this._tree, this._themeService));
this._disposables.push(controller.onDidFocus(element => onEvent(element, 'show')));
this._disposables.push(controller.onDidSelect(element => onEvent(element, 'goto')));
this._disposables.push(controller.onDidOpenToSide(element => onEvent(element, 'side')));

this._treeContainer = div.hide();
});
Expand Down Expand Up @@ -755,7 +706,12 @@ export class ReferenceWidget extends PeekViewWidget {
}

public setSelection(selection: OneReference): TPromise<any> {
return this._revealReference(selection);
return this._revealReference(selection).then(() => {

// show in tree
this._tree.setSelection([selection]);
this._tree.setFocus(selection);
});
}

public setModel(newModel: ReferencesModel): TPromise<any> {
Expand Down Expand Up @@ -820,7 +776,7 @@ export class ReferenceWidget extends PeekViewWidget {
return undefined;
}

private _revealReference(reference: OneReference) {
private _revealReference(reference: OneReference): TPromise<void> {

// Update widget header
if (reference.uri.scheme !== Schemas.inMemory) {
Expand Down Expand Up @@ -855,11 +811,6 @@ export class ReferenceWidget extends PeekViewWidget {
this._preview.setModel(this._previewNotAvailableMessage);
ref.dispose();
}

// show in tree
this._tree.setSelection([reference]);
this._tree.setFocus(reference);

}, onUnexpectedError);
}
}
Expand Down