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
Changes from all commits
c167bf4
278b78f
64f9e8a
daeac8e
6ebd589
23c3596
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
|
@@ -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; | ||
|
@@ -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), | ||
primary: KeyCode.Escape, | ||
secondary: [KeyMod.Shift | KeyCode.Escape], | ||
when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')), | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jrieken you must have changed the keybindings then, the default is There was a problem hiding this comment. Choose a reason for hiding this commentThe 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[]> { | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'; | ||
|
@@ -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 { | ||
|
@@ -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(); | ||
|
@@ -562,6 +504,8 @@ export interface SelectionEvent { | |
element: Location; | ||
} | ||
|
||
export const ctxReferenceWidgetSearchTreeFocused = new RawContextKey<boolean>('referenceSearchTreeFocused', true); | ||
|
||
/** | ||
* ZoneWidget that is shown inside the editor | ||
*/ | ||
|
@@ -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; | ||
|
@@ -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') => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jrieken that is consistent with other usages of |
||
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(); | ||
}); | ||
|
@@ -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> { | ||
|
@@ -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) { | ||
|
@@ -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); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
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....
There was a problem hiding this comment.
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.