Skip to content

Commit 6c3cfaf

Browse files
committed
callh - Shift-Enter refocuses onto current item
1 parent 7563aa3 commit 6c3cfaf

File tree

3 files changed

+109
-24
lines changed

3 files changed

+109
-24
lines changed

src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
1919
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
2020
import { PeekContext } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
2121
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
22+
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
23+
import { Range } from 'vs/editor/common/core/range';
24+
import { IPosition } from 'vs/editor/common/core/position';
2225

2326
const _ctxHasCompletionItemProvider = new RawContextKey<boolean>('editorHasCallHierarchyProvider', false);
2427
const _ctxCallHierarchyVisible = new RawContextKey<boolean>('callHierarchyVisible', false);
@@ -44,6 +47,7 @@ class CallHierarchyController implements IEditorContribution {
4447
private readonly _editor: ICodeEditor,
4548
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
4649
@IStorageService private readonly _storageService: IStorageService,
50+
@ICodeEditorService private readonly _editorService: ICodeEditorService,
4751
@IInstantiationService private readonly _instantiationService: IInstantiationService,
4852
) {
4953
this._ctxIsVisible = _ctxCallHierarchyVisible.bindTo(this._contextKeyService);
@@ -60,7 +64,7 @@ class CallHierarchyController implements IEditorContribution {
6064
this._dispoables.dispose();
6165
}
6266

63-
async startCallHierarchy(): Promise<void> {
67+
async startCallHierarchyFromEditor(): Promise<void> {
6468
this._sessionDisposables.clear();
6569

6670
if (!this._editor.hasModel()) {
@@ -73,42 +77,65 @@ class CallHierarchyController implements IEditorContribution {
7377
return;
7478
}
7579

80+
const cts = new CancellationTokenSource();
81+
const model = CallHierarchyModel.create(document, position, cts.token);
7682
const direction = this._storageService.getNumber(CallHierarchyController._StorageDirection, StorageScope.GLOBAL, <number>CallHierarchyDirection.CallsFrom);
7783

78-
Event.any<any>(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDisposables);
79-
this._widget = this._instantiationService.createInstance(
80-
CallHierarchyTreePeekWidget,
81-
this._editor,
82-
position,
83-
direction
84+
this._showCallHierarchyWidget(position, direction, model, cts);
85+
}
86+
87+
async startCallHierarchyFromCallHierarchy(): Promise<void> {
88+
if (!this._widget) {
89+
return;
90+
}
91+
const model = this._widget.getModel();
92+
const call = this._widget.getFocused();
93+
if (!call || !model) {
94+
return;
95+
}
96+
const newEditor = await this._editorService.openCodeEditor({ resource: call.item.uri }, this._editor);
97+
if (!newEditor) {
98+
return;
99+
}
100+
const newModel = model.fork(call.item);
101+
this._sessionDisposables.clear();
102+
103+
CallHierarchyController.get(newEditor)._showCallHierarchyWidget(
104+
Range.lift(newModel.root.selectionRange).getStartPosition(),
105+
this._widget.direction,
106+
Promise.resolve(newModel),
107+
new CancellationTokenSource()
84108
);
109+
}
85110

111+
private _showCallHierarchyWidget(position: IPosition, direction: number, model: Promise<CallHierarchyModel | undefined>, cts: CancellationTokenSource) {
112+
113+
Event.any<any>(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDisposables);
114+
this._widget = this._instantiationService.createInstance(CallHierarchyTreePeekWidget, this._editor, position, direction);
86115
this._widget.showLoading();
87116
this._ctxIsVisible.set(true);
88-
89-
const cts = new CancellationTokenSource();
90-
91117
this._sessionDisposables.add(this._widget.onDidClose(() => {
92118
this.endCallHierarchy();
93119
this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL);
94120
}));
95121
this._sessionDisposables.add({ dispose() { cts.dispose(true); } });
96122
this._sessionDisposables.add(this._widget);
97123

98-
try {
99-
const model = await CallHierarchyModel.create(document, position, cts.token);
124+
model.then(model => {
100125
if (cts.token.isCancellationRequested) {
101126
return; // nothing
102-
} else if (model) {
127+
}
128+
if (model) {
103129
this._sessionDisposables.add(model);
104-
this._widget.showModel(model);
105-
} else {
106-
this._widget.showMessage(localize('no.item', "No results"));
130+
this._widget!.showModel(model);
107131
}
108-
} catch (e) {
109-
this._widget.showMessage(localize('error', "Failed to show call hierarchy"));
132+
else {
133+
this._widget!.showMessage(localize('no.item', "No results"));
134+
}
135+
}).catch(e => {
136+
this._widget!.showMessage(localize('error', "Failed to show call hierarchy"));
110137
console.error(e);
111-
}
138+
});
112139
}
113140

114141
toggleCallHierarchyDirection(): void {
@@ -151,7 +178,7 @@ registerEditorAction(class extends EditorAction {
151178
}
152179

153180
async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
154-
return CallHierarchyController.get(editor).startCallHierarchy();
181+
return CallHierarchyController.get(editor).startCallHierarchyFromEditor();
155182
}
156183
});
157184

@@ -175,6 +202,26 @@ registerEditorAction(class extends EditorAction {
175202
}
176203
});
177204

205+
registerEditorAction(class extends EditorAction {
206+
207+
constructor() {
208+
super({
209+
id: 'editor.refocusCallHierarchy',
210+
label: localize('title.refocus', "Refocus Call Hierarchy"),
211+
alias: 'Refocus Call Hierarchy',
212+
kbOpts: {
213+
weight: KeybindingWeight.WorkbenchContrib,
214+
primary: KeyMod.Shift + KeyCode.Enter
215+
},
216+
precondition: _ctxCallHierarchyVisible
217+
});
218+
}
219+
220+
async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
221+
return CallHierarchyController.get(editor).startCallHierarchyFromCallHierarchy();
222+
}
223+
});
224+
178225

179226
registerEditorCommand(new class extends EditorCommand {
180227

src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { URI } from 'vs/base/common/uri';
1212
import { IPosition } from 'vs/editor/common/core/position';
1313
import { isNonEmptyArray } from 'vs/base/common/arrays';
1414
import { onUnexpectedExternalError } from 'vs/base/common/errors';
15+
import { IDisposable } from 'vs/base/common/lifecycle';
1516

1617
export const enum CallHierarchyDirection {
1718
CallsTo = 1,
@@ -55,6 +56,26 @@ export interface CallHierarchyProvider {
5556
export const CallHierarchyProviderRegistry = new LanguageFeatureRegistry<CallHierarchyProvider>();
5657

5758

59+
class RefCountedDisposabled {
60+
61+
constructor(
62+
private readonly _disposable: IDisposable,
63+
private _counter = 1
64+
) { }
65+
66+
acquire() {
67+
this._counter++;
68+
return this;
69+
}
70+
71+
release() {
72+
if (--this._counter === 0) {
73+
this._disposable.dispose();
74+
}
75+
return this;
76+
}
77+
}
78+
5879
export class CallHierarchyModel {
5980

6081
static async create(model: ITextModel, position: IPosition, token: CancellationToken): Promise<CallHierarchyModel | undefined> {
@@ -66,17 +87,26 @@ export class CallHierarchyModel {
6687
if (!session) {
6788
return undefined;
6889
}
69-
return new CallHierarchyModel(provider, session);
90+
return new CallHierarchyModel(provider, session.root, new RefCountedDisposabled(session));
7091
}
7192

7293
private constructor(
7394
readonly provider: CallHierarchyProvider,
74-
readonly session: CallHierarchySession,
75-
readonly root = session.root
95+
readonly root: CallHierarchyItem,
96+
readonly ref: RefCountedDisposabled,
7697
) { }
7798

7899
dispose(): void {
79-
this.session.dispose();
100+
this.ref.release();
101+
}
102+
103+
fork(item: CallHierarchyItem): CallHierarchyModel {
104+
const that = this;
105+
return new class extends CallHierarchyModel {
106+
constructor() {
107+
super(that.provider, item, that.ref.acquire());
108+
}
109+
};
80110
}
81111

82112
async resolveIncomingCalls(item: CallHierarchyItem, token: CancellationToken): Promise<IncomingCall[]> {

src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,14 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget {
390390
}
391391
}
392392

393+
getModel(): CallHierarchyModel | undefined {
394+
return this._tree.getInput();
395+
}
396+
397+
getFocused(): callHTree.Call | undefined {
398+
return this._tree.getFocus()[0];
399+
}
400+
393401
async toggleDirection(): Promise<void> {
394402
const model = this._tree.getInput();
395403
if (model) {

0 commit comments

Comments
 (0)