Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { alert } from '../../../../base/browser/ui/aria/aria.js';
7
import { createCancelablePromise, raceCancellation } from '../../../../base/common/async.js';
8
import { CancellationToken } from '../../../../base/common/cancellation.js';
9
import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
10
import { assertType } from '../../../../base/common/types.js';
11
import { URI } from '../../../../base/common/uri.js';
12
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js';
13
import { IActiveCodeEditor, ICodeEditor, isCodeEditor } from '../../../browser/editorBrowser.js';
14
import { EditorAction2, ServicesAccessor } from '../../../browser/editorExtensions.js';
15
import { ICodeEditorService } from '../../../browser/services/codeEditorService.js';
16
import { EmbeddedCodeEditorWidget } from '../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js';
17
import { EditorOption, GoToLocationValues } from '../../../common/config/editorOptions.js';
18
import * as corePosition from '../../../common/core/position.js';
19
import { IRange, Range } from '../../../common/core/range.js';
20
import { ScrollType } from '../../../common/editorCommon.js';
21
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
22
import { ITextModel } from '../../../common/model.js';
23
import { isLocationLink, Location, LocationLink } from '../../../common/languages.js';
24
import { ReferencesController } from './peek/referencesController.js';
25
import { ReferencesModel } from './referencesModel.js';
26
import { ISymbolNavigationService } from './symbolNavigation.js';
27
import { MessageController } from '../../message/browser/messageController.js';
28
import { PeekContext } from '../../peekView/browser/peekView.js';
29
import * as nls from '../../../../nls.js';
30
import { IAction2F1RequiredOptions, IAction2Options, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js';
31
import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
32
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
33
import { TextEditorSelectionRevealType, TextEditorSelectionSource } from '../../../../platform/editor/common/editor.js';
34
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
35
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
36
import { INotificationService } from '../../../../platform/notification/common/notification.js';
37
import { IEditorProgressService } from '../../../../platform/progress/common/progress.js';
38
import { getDeclarationsAtPosition, getDefinitionsAtPosition, getImplementationsAtPosition, getReferencesAtPosition, getTypeDefinitionsAtPosition } from './goToSymbol.js';
39
import { IWordAtPosition } from '../../../common/core/wordHelper.js';
40
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
41
import { Iterable } from '../../../../base/common/iterator.js';
42
import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js';
43
44
MenuRegistry.appendMenuItem(MenuId.EditorContext, {
45
submenu: MenuId.EditorContextPeek,
46
title: nls.localize('peek.submenu', "Peek"),
47
group: 'navigation',
48
order: 100
49
} satisfies ISubmenuItem);
50
51
export interface SymbolNavigationActionConfig {
52
openToSide: boolean;
53
openInPeek: boolean;
54
muteMessage: boolean;
55
}
56
57
export class SymbolNavigationAnchor {
58
59
static is(thing: any): thing is SymbolNavigationAnchor {
60
if (!thing || typeof thing !== 'object') {
61
return false;
62
}
63
if (thing instanceof SymbolNavigationAnchor) {
64
return true;
65
}
66
if (corePosition.Position.isIPosition((<SymbolNavigationAnchor>thing).position) && (<SymbolNavigationAnchor>thing).model) {
67
return true;
68
}
69
return false;
70
}
71
72
constructor(readonly model: ITextModel, readonly position: corePosition.Position) { }
73
}
74
75
export abstract class SymbolNavigationAction extends EditorAction2 {
76
77
private static _allSymbolNavigationCommands = new Map<string, SymbolNavigationAction>();
78
private static _activeAlternativeCommands = new Set<string>();
79
80
static all(): IterableIterator<SymbolNavigationAction> {
81
return SymbolNavigationAction._allSymbolNavigationCommands.values();
82
}
83
84
private static _patchConfig(opts: IAction2Options & IAction2F1RequiredOptions): IAction2Options {
85
const result = { ...opts, f1: true };
86
// patch context menu when clause
87
if (result.menu) {
88
for (const item of Iterable.wrap(result.menu)) {
89
if (item.id === MenuId.EditorContext || item.id === MenuId.EditorContextPeek) {
90
item.when = ContextKeyExpr.and(opts.precondition, item.when);
91
}
92
}
93
}
94
return <typeof opts>result;
95
}
96
97
readonly configuration: SymbolNavigationActionConfig;
98
99
constructor(configuration: SymbolNavigationActionConfig, opts: IAction2Options & IAction2F1RequiredOptions) {
100
super(SymbolNavigationAction._patchConfig(opts));
101
this.configuration = configuration;
102
SymbolNavigationAction._allSymbolNavigationCommands.set(opts.id, this);
103
}
104
105
override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, arg?: SymbolNavigationAnchor | unknown, range?: Range): Promise<void> {
106
if (!editor.hasModel()) {
107
return Promise.resolve(undefined);
108
}
109
const notificationService = accessor.get(INotificationService);
110
const editorService = accessor.get(ICodeEditorService);
111
const progressService = accessor.get(IEditorProgressService);
112
const symbolNavService = accessor.get(ISymbolNavigationService);
113
const languageFeaturesService = accessor.get(ILanguageFeaturesService);
114
const instaService = accessor.get(IInstantiationService);
115
116
const model = editor.getModel();
117
const position = editor.getPosition();
118
const anchor = SymbolNavigationAnchor.is(arg) ? arg : new SymbolNavigationAnchor(model, position);
119
120
const cts = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
121
122
const promise = raceCancellation(this._getLocationModel(languageFeaturesService, anchor.model, anchor.position, cts.token), cts.token).then(async references => {
123
124
if (!references || cts.token.isCancellationRequested) {
125
return;
126
}
127
128
alert(references.ariaMessage);
129
130
let altAction: SymbolNavigationAction | null | undefined;
131
if (references.referenceAt(model.uri, position)) {
132
const altActionId = this._getAlternativeCommand(editor);
133
if (altActionId !== undefined && !SymbolNavigationAction._activeAlternativeCommands.has(altActionId) && SymbolNavigationAction._allSymbolNavigationCommands.has(altActionId)) {
134
altAction = SymbolNavigationAction._allSymbolNavigationCommands.get(altActionId)!;
135
}
136
}
137
138
const referenceCount = references.references.length;
139
140
if (referenceCount === 0) {
141
// no result -> show message
142
if (!this.configuration.muteMessage) {
143
const info = model.getWordAtPosition(position);
144
MessageController.get(editor)?.showMessage(this._getNoResultFoundMessage(info), position);
145
}
146
} else if (referenceCount === 1 && altAction) {
147
// already at the only result, run alternative
148
SymbolNavigationAction._activeAlternativeCommands.add(this.desc.id);
149
instaService.invokeFunction((accessor) => altAction.runEditorCommand(accessor, editor, arg, range).finally(() => {
150
SymbolNavigationAction._activeAlternativeCommands.delete(this.desc.id);
151
}));
152
153
} else {
154
// normal results handling
155
return this._onResult(editorService, symbolNavService, editor, references, range);
156
}
157
158
}, (err) => {
159
// report an error
160
notificationService.error(err);
161
}).finally(() => {
162
cts.dispose();
163
});
164
165
progressService.showWhile(promise, 250);
166
return promise;
167
}
168
169
protected abstract _getLocationModel(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel | undefined>;
170
171
protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string;
172
173
protected abstract _getAlternativeCommand(editor: IActiveCodeEditor): string | undefined;
174
175
protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues;
176
177
private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel, range?: Range): Promise<void> {
178
179
const gotoLocation = this._getGoToPreference(editor);
180
if (!(editor instanceof EmbeddedCodeEditorWidget) && (this.configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1))) {
181
this._openInPeek(editor, model, range);
182
183
} else {
184
const next = model.firstReference()!;
185
const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek';
186
const targetEditor = await this._openReference(editor, editorService, next, this.configuration.openToSide, !peek);
187
if (peek && targetEditor) {
188
this._openInPeek(targetEditor, model, range);
189
} else {
190
model.dispose();
191
}
192
193
// keep remaining locations around when using
194
// 'goto'-mode
195
if (gotoLocation === 'goto') {
196
symbolNavService.put(next);
197
}
198
}
199
}
200
201
private async _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean, highlight: boolean): Promise<ICodeEditor | undefined> {
202
// range is the target-selection-range when we have one
203
// and the fallback is the 'full' range
204
let range: IRange | undefined = undefined;
205
if (isLocationLink(reference)) {
206
range = reference.targetSelectionRange;
207
}
208
if (!range) {
209
range = reference.range;
210
}
211
if (!range) {
212
return undefined;
213
}
214
215
const targetEditor = await editorService.openCodeEditor({
216
resource: reference.uri,
217
options: {
218
selection: Range.collapseToStart(range),
219
selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport,
220
selectionSource: TextEditorSelectionSource.JUMP
221
}
222
}, editor, sideBySide);
223
224
if (!targetEditor) {
225
return undefined;
226
}
227
228
if (highlight) {
229
const modelNow = targetEditor.getModel();
230
const decorations = targetEditor.createDecorationsCollection([{ range, options: { description: 'symbol-navigate-action-highlight', className: 'symbolHighlight' } }]);
231
setTimeout(() => {
232
if (targetEditor.getModel() === modelNow) {
233
decorations.clear();
234
}
235
}, 350);
236
}
237
238
return targetEditor;
239
}
240
241
private _openInPeek(target: ICodeEditor, model: ReferencesModel, range?: Range) {
242
const controller = ReferencesController.get(target);
243
if (controller && target.hasModel()) {
244
controller.toggleWidget(range ?? target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this.configuration.openInPeek);
245
} else {
246
model.dispose();
247
}
248
}
249
}
250
251
//#region --- DEFINITION
252
253
export class DefinitionAction extends SymbolNavigationAction {
254
255
protected async _getLocationModel(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
256
return new ReferencesModel(await getDefinitionsAtPosition(languageFeaturesService.definitionProvider, model, position, false, token), nls.localize('def.title', 'Definitions'));
257
}
258
259
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
260
return info && info.word
261
? nls.localize('noResultWord', "No definition found for '{0}'", info.word)
262
: nls.localize('generic.noResults', "No definition found");
263
}
264
265
protected _getAlternativeCommand(editor: IActiveCodeEditor): string {
266
return editor.getOption(EditorOption.gotoLocation).alternativeDefinitionCommand;
267
}
268
269
protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues {
270
return editor.getOption(EditorOption.gotoLocation).multipleDefinitions;
271
}
272
}
273
274
registerAction2(class GoToDefinitionAction extends DefinitionAction {
275
276
static readonly id = 'editor.action.revealDefinition';
277
278
constructor() {
279
super({
280
openToSide: false,
281
openInPeek: false,
282
muteMessage: false
283
}, {
284
id: GoToDefinitionAction.id,
285
title: {
286
...nls.localize2('actions.goToDecl.label', "Go to Definition"),
287
mnemonicTitle: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition"),
288
},
289
precondition: EditorContextKeys.hasDefinitionProvider,
290
keybinding: [{
291
when: EditorContextKeys.editorTextFocus,
292
primary: KeyCode.F12,
293
weight: KeybindingWeight.EditorContrib
294
}, {
295
when: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, IsWebContext),
296
primary: KeyMod.CtrlCmd | KeyCode.F12,
297
weight: KeybindingWeight.EditorContrib
298
}],
299
menu: [{
300
id: MenuId.EditorContext,
301
group: 'navigation',
302
order: 1.1
303
}, {
304
id: MenuId.MenubarGoMenu,
305
precondition: null,
306
group: '4_symbol_nav',
307
order: 2,
308
}]
309
});
310
CommandsRegistry.registerCommandAlias('editor.action.goToDeclaration', GoToDefinitionAction.id);
311
}
312
});
313
314
registerAction2(class OpenDefinitionToSideAction extends DefinitionAction {
315
316
static readonly id = 'editor.action.revealDefinitionAside';
317
318
constructor() {
319
super({
320
openToSide: true,
321
openInPeek: false,
322
muteMessage: false
323
}, {
324
id: OpenDefinitionToSideAction.id,
325
title: nls.localize2('actions.goToDeclToSide.label', "Open Definition to the Side"),
326
precondition: ContextKeyExpr.and(
327
EditorContextKeys.hasDefinitionProvider,
328
EditorContextKeys.isInEmbeddedEditor.toNegated()),
329
keybinding: [{
330
when: EditorContextKeys.editorTextFocus,
331
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.F12),
332
weight: KeybindingWeight.EditorContrib
333
}, {
334
when: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, IsWebContext),
335
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.F12),
336
weight: KeybindingWeight.EditorContrib
337
}]
338
});
339
CommandsRegistry.registerCommandAlias('editor.action.openDeclarationToTheSide', OpenDefinitionToSideAction.id);
340
}
341
});
342
343
registerAction2(class PeekDefinitionAction extends DefinitionAction {
344
345
static readonly id = 'editor.action.peekDefinition';
346
347
constructor() {
348
super({
349
openToSide: false,
350
openInPeek: true,
351
muteMessage: false
352
}, {
353
id: PeekDefinitionAction.id,
354
title: nls.localize2('actions.previewDecl.label', "Peek Definition"),
355
precondition: ContextKeyExpr.and(
356
EditorContextKeys.hasDefinitionProvider,
357
PeekContext.notInPeekEditor,
358
EditorContextKeys.isInEmbeddedEditor.toNegated()
359
),
360
keybinding: {
361
when: EditorContextKeys.editorTextFocus,
362
primary: KeyMod.Alt | KeyCode.F12,
363
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 },
364
weight: KeybindingWeight.EditorContrib
365
},
366
menu: {
367
id: MenuId.EditorContextPeek,
368
group: 'peek',
369
order: 2
370
}
371
});
372
CommandsRegistry.registerCommandAlias('editor.action.previewDeclaration', PeekDefinitionAction.id);
373
}
374
});
375
376
//#endregion
377
378
//#region --- DECLARATION
379
380
class DeclarationAction extends SymbolNavigationAction {
381
382
protected async _getLocationModel(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
383
return new ReferencesModel(await getDeclarationsAtPosition(languageFeaturesService.declarationProvider, model, position, false, token), nls.localize('decl.title', 'Declarations'));
384
}
385
386
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
387
return info && info.word
388
? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word)
389
: nls.localize('decl.generic.noResults', "No declaration found");
390
}
391
392
protected _getAlternativeCommand(editor: IActiveCodeEditor): string {
393
return editor.getOption(EditorOption.gotoLocation).alternativeDeclarationCommand;
394
}
395
396
protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues {
397
return editor.getOption(EditorOption.gotoLocation).multipleDeclarations;
398
}
399
}
400
401
registerAction2(class GoToDeclarationAction extends DeclarationAction {
402
403
static readonly id = 'editor.action.revealDeclaration';
404
405
constructor() {
406
super({
407
openToSide: false,
408
openInPeek: false,
409
muteMessage: false
410
}, {
411
id: GoToDeclarationAction.id,
412
title: {
413
...nls.localize2('actions.goToDeclaration.label', "Go to Declaration"),
414
mnemonicTitle: nls.localize({ key: 'miGotoDeclaration', comment: ['&& denotes a mnemonic'] }, "Go to &&Declaration"),
415
},
416
precondition: ContextKeyExpr.and(
417
EditorContextKeys.hasDeclarationProvider,
418
EditorContextKeys.isInEmbeddedEditor.toNegated()
419
),
420
menu: [{
421
id: MenuId.EditorContext,
422
group: 'navigation',
423
order: 1.3
424
}, {
425
id: MenuId.MenubarGoMenu,
426
precondition: null,
427
group: '4_symbol_nav',
428
order: 3,
429
}],
430
});
431
}
432
433
protected override _getNoResultFoundMessage(info: IWordAtPosition | null): string {
434
return info && info.word
435
? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word)
436
: nls.localize('decl.generic.noResults', "No declaration found");
437
}
438
});
439
440
registerAction2(class PeekDeclarationAction extends DeclarationAction {
441
constructor() {
442
super({
443
openToSide: false,
444
openInPeek: true,
445
muteMessage: false
446
}, {
447
id: 'editor.action.peekDeclaration',
448
title: nls.localize2('actions.peekDecl.label', "Peek Declaration"),
449
precondition: ContextKeyExpr.and(
450
EditorContextKeys.hasDeclarationProvider,
451
PeekContext.notInPeekEditor,
452
EditorContextKeys.isInEmbeddedEditor.toNegated()
453
),
454
menu: {
455
id: MenuId.EditorContextPeek,
456
group: 'peek',
457
order: 3
458
}
459
});
460
}
461
});
462
463
//#endregion
464
465
//#region --- TYPE DEFINITION
466
467
class TypeDefinitionAction extends SymbolNavigationAction {
468
469
protected async _getLocationModel(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
470
return new ReferencesModel(await getTypeDefinitionsAtPosition(languageFeaturesService.typeDefinitionProvider, model, position, false, token), nls.localize('typedef.title', 'Type Definitions'));
471
}
472
473
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
474
return info && info.word
475
? nls.localize('goToTypeDefinition.noResultWord', "No type definition found for '{0}'", info.word)
476
: nls.localize('goToTypeDefinition.generic.noResults', "No type definition found");
477
}
478
479
protected _getAlternativeCommand(editor: IActiveCodeEditor): string {
480
return editor.getOption(EditorOption.gotoLocation).alternativeTypeDefinitionCommand;
481
}
482
483
protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues {
484
return editor.getOption(EditorOption.gotoLocation).multipleTypeDefinitions;
485
}
486
}
487
488
registerAction2(class GoToTypeDefinitionAction extends TypeDefinitionAction {
489
490
public static readonly ID = 'editor.action.goToTypeDefinition';
491
492
constructor() {
493
super({
494
openToSide: false,
495
openInPeek: false,
496
muteMessage: false
497
}, {
498
id: GoToTypeDefinitionAction.ID,
499
title: {
500
...nls.localize2('actions.goToTypeDefinition.label', "Go to Type Definition"),
501
mnemonicTitle: nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition"),
502
},
503
precondition: EditorContextKeys.hasTypeDefinitionProvider,
504
keybinding: {
505
when: EditorContextKeys.editorTextFocus,
506
primary: 0,
507
weight: KeybindingWeight.EditorContrib
508
},
509
menu: [{
510
id: MenuId.EditorContext,
511
group: 'navigation',
512
order: 1.4
513
}, {
514
id: MenuId.MenubarGoMenu,
515
precondition: null,
516
group: '4_symbol_nav',
517
order: 3,
518
}]
519
});
520
}
521
});
522
523
registerAction2(class PeekTypeDefinitionAction extends TypeDefinitionAction {
524
525
public static readonly ID = 'editor.action.peekTypeDefinition';
526
527
constructor() {
528
super({
529
openToSide: false,
530
openInPeek: true,
531
muteMessage: false
532
}, {
533
id: PeekTypeDefinitionAction.ID,
534
title: nls.localize2('actions.peekTypeDefinition.label', "Peek Type Definition"),
535
precondition: ContextKeyExpr.and(
536
EditorContextKeys.hasTypeDefinitionProvider,
537
PeekContext.notInPeekEditor,
538
EditorContextKeys.isInEmbeddedEditor.toNegated()
539
),
540
menu: {
541
id: MenuId.EditorContextPeek,
542
group: 'peek',
543
order: 4
544
}
545
});
546
}
547
});
548
549
//#endregion
550
551
//#region --- IMPLEMENTATION
552
553
class ImplementationAction extends SymbolNavigationAction {
554
555
protected async _getLocationModel(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
556
return new ReferencesModel(await getImplementationsAtPosition(languageFeaturesService.implementationProvider, model, position, false, token), nls.localize('impl.title', 'Implementations'));
557
}
558
559
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
560
return info && info.word
561
? nls.localize('goToImplementation.noResultWord', "No implementation found for '{0}'", info.word)
562
: nls.localize('goToImplementation.generic.noResults', "No implementation found");
563
}
564
565
protected _getAlternativeCommand(editor: IActiveCodeEditor): string {
566
return editor.getOption(EditorOption.gotoLocation).alternativeImplementationCommand;
567
}
568
569
protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues {
570
return editor.getOption(EditorOption.gotoLocation).multipleImplementations;
571
}
572
}
573
574
registerAction2(class GoToImplementationAction extends ImplementationAction {
575
576
public static readonly ID = 'editor.action.goToImplementation';
577
578
constructor() {
579
super({
580
openToSide: false,
581
openInPeek: false,
582
muteMessage: false
583
}, {
584
id: GoToImplementationAction.ID,
585
title: {
586
...nls.localize2('actions.goToImplementation.label', "Go to Implementations"),
587
mnemonicTitle: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementations"),
588
},
589
precondition: EditorContextKeys.hasImplementationProvider,
590
keybinding: {
591
when: EditorContextKeys.editorTextFocus,
592
primary: KeyMod.CtrlCmd | KeyCode.F12,
593
weight: KeybindingWeight.EditorContrib
594
},
595
menu: [{
596
id: MenuId.EditorContext,
597
group: 'navigation',
598
order: 1.45
599
}, {
600
id: MenuId.MenubarGoMenu,
601
precondition: null,
602
group: '4_symbol_nav',
603
order: 4,
604
}]
605
});
606
}
607
});
608
609
registerAction2(class PeekImplementationAction extends ImplementationAction {
610
611
public static readonly ID = 'editor.action.peekImplementation';
612
613
constructor() {
614
super({
615
openToSide: false,
616
openInPeek: true,
617
muteMessage: false
618
}, {
619
id: PeekImplementationAction.ID,
620
title: nls.localize2('actions.peekImplementation.label', "Peek Implementations"),
621
precondition: ContextKeyExpr.and(
622
EditorContextKeys.hasImplementationProvider,
623
PeekContext.notInPeekEditor,
624
EditorContextKeys.isInEmbeddedEditor.toNegated()
625
),
626
keybinding: {
627
when: EditorContextKeys.editorTextFocus,
628
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12,
629
weight: KeybindingWeight.EditorContrib
630
},
631
menu: {
632
id: MenuId.EditorContextPeek,
633
group: 'peek',
634
order: 5
635
}
636
});
637
}
638
});
639
640
//#endregion
641
642
//#region --- REFERENCES
643
644
abstract class ReferencesAction extends SymbolNavigationAction {
645
646
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
647
return info
648
? nls.localize('references.no', "No references found for '{0}'", info.word)
649
: nls.localize('references.noGeneric', "No references found");
650
}
651
652
protected _getAlternativeCommand(editor: IActiveCodeEditor): string {
653
return editor.getOption(EditorOption.gotoLocation).alternativeReferenceCommand;
654
}
655
656
protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues {
657
return editor.getOption(EditorOption.gotoLocation).multipleReferences;
658
}
659
}
660
661
registerAction2(class GoToReferencesAction extends ReferencesAction {
662
663
constructor() {
664
super({
665
openToSide: false,
666
openInPeek: false,
667
muteMessage: false
668
}, {
669
id: 'editor.action.goToReferences',
670
title: {
671
...nls.localize2('goToReferences.label', "Go to References"),
672
mnemonicTitle: nls.localize({ key: 'miGotoReference', comment: ['&& denotes a mnemonic'] }, "Go to &&References"),
673
},
674
precondition: ContextKeyExpr.and(
675
EditorContextKeys.hasReferenceProvider,
676
PeekContext.notInPeekEditor,
677
EditorContextKeys.isInEmbeddedEditor.toNegated()
678
),
679
keybinding: {
680
when: EditorContextKeys.editorTextFocus,
681
primary: KeyMod.Shift | KeyCode.F12,
682
weight: KeybindingWeight.EditorContrib
683
},
684
menu: [{
685
id: MenuId.EditorContext,
686
group: 'navigation',
687
order: 1.45
688
}, {
689
id: MenuId.MenubarGoMenu,
690
precondition: null,
691
group: '4_symbol_nav',
692
order: 5,
693
}]
694
});
695
}
696
697
protected async _getLocationModel(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
698
return new ReferencesModel(await getReferencesAtPosition(languageFeaturesService.referenceProvider, model, position, true, false, token), nls.localize('ref.title', 'References'));
699
}
700
});
701
702
registerAction2(class PeekReferencesAction extends ReferencesAction {
703
704
constructor() {
705
super({
706
openToSide: false,
707
openInPeek: true,
708
muteMessage: false
709
}, {
710
id: 'editor.action.referenceSearch.trigger',
711
title: nls.localize2('references.action.label', "Peek References"),
712
precondition: ContextKeyExpr.and(
713
EditorContextKeys.hasReferenceProvider,
714
PeekContext.notInPeekEditor,
715
EditorContextKeys.isInEmbeddedEditor.toNegated()
716
),
717
menu: {
718
id: MenuId.EditorContextPeek,
719
group: 'peek',
720
order: 6
721
}
722
});
723
}
724
725
protected async _getLocationModel(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
726
return new ReferencesModel(await getReferencesAtPosition(languageFeaturesService.referenceProvider, model, position, false, false, token), nls.localize('ref.title', 'References'));
727
}
728
});
729
730
//#endregion
731
732
733
//#region --- GENERIC goto symbols command
734
735
class GenericGoToLocationAction extends SymbolNavigationAction {
736
737
constructor(
738
config: SymbolNavigationActionConfig,
739
private readonly _references: Location[],
740
private readonly _gotoMultipleBehaviour: GoToLocationValues | undefined,
741
) {
742
super(config, {
743
id: 'editor.action.goToLocation',
744
title: nls.localize2('label.generic', "Go to Any Symbol"),
745
precondition: ContextKeyExpr.and(
746
PeekContext.notInPeekEditor,
747
EditorContextKeys.isInEmbeddedEditor.toNegated()
748
),
749
});
750
}
751
752
protected async _getLocationModel(languageFeaturesService: ILanguageFeaturesService, _model: ITextModel, _position: corePosition.Position, _token: CancellationToken): Promise<ReferencesModel | undefined> {
753
return new ReferencesModel(this._references, nls.localize('generic.title', 'Locations'));
754
}
755
756
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
757
return info && nls.localize('generic.noResult', "No results for '{0}'", info.word) || '';
758
}
759
760
protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues {
761
return this._gotoMultipleBehaviour ?? editor.getOption(EditorOption.gotoLocation).multipleReferences;
762
}
763
764
protected _getAlternativeCommand(): undefined {
765
return undefined;
766
}
767
}
768
769
CommandsRegistry.registerCommand({
770
id: 'editor.action.goToLocations',
771
metadata: {
772
description: 'Go to locations from a position in a file',
773
args: [
774
{ name: 'uri', description: 'The text document in which to start', constraint: URI },
775
{ name: 'position', description: 'The position at which to start', constraint: corePosition.Position.isIPosition },
776
{ name: 'locations', description: 'An array of locations.', constraint: Array },
777
{ name: 'multiple', description: 'Define what to do when having multiple results, either `peek`, `gotoAndPeek`, or `goto`' },
778
{ name: 'noResultsMessage', description: 'Human readable message that shows when locations is empty.' },
779
]
780
},
781
handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any, noResultsMessage?: string, openInPeek?: boolean) => {
782
assertType(URI.isUri(resource));
783
assertType(corePosition.Position.isIPosition(position));
784
assertType(Array.isArray(references));
785
assertType(typeof multiple === 'undefined' || typeof multiple === 'string');
786
assertType(typeof openInPeek === 'undefined' || typeof openInPeek === 'boolean');
787
788
const editorService = accessor.get(ICodeEditorService);
789
const editor = await editorService.openCodeEditor({ resource }, editorService.getFocusedCodeEditor());
790
791
if (isCodeEditor(editor)) {
792
editor.setPosition(position);
793
editor.revealPositionInCenterIfOutsideViewport(position, ScrollType.Smooth);
794
795
return editor.invokeWithinContext(accessor => {
796
const command = new class extends GenericGoToLocationAction {
797
protected override _getNoResultFoundMessage(info: IWordAtPosition | null) {
798
return noResultsMessage || super._getNoResultFoundMessage(info);
799
}
800
}({
801
muteMessage: !Boolean(noResultsMessage),
802
openInPeek: Boolean(openInPeek),
803
openToSide: false
804
}, references, multiple as GoToLocationValues);
805
806
accessor.get(IInstantiationService).invokeFunction(command.run.bind(command), editor);
807
});
808
}
809
}
810
});
811
812
CommandsRegistry.registerCommand({
813
id: 'editor.action.peekLocations',
814
metadata: {
815
description: 'Peek locations from a position in a file',
816
args: [
817
{ name: 'uri', description: 'The text document in which to start', constraint: URI },
818
{ name: 'position', description: 'The position at which to start', constraint: corePosition.Position.isIPosition },
819
{ name: 'locations', description: 'An array of locations.', constraint: Array },
820
{ name: 'multiple', description: 'Define what to do when having multiple results, either `peek`, `gotoAndPeek`, or `goto`' },
821
]
822
},
823
handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any) => {
824
accessor.get(ICommandService).executeCommand('editor.action.goToLocations', resource, position, references, multiple, undefined, true);
825
}
826
});
827
828
//#endregion
829
830
831
//#region --- REFERENCE search special commands
832
833
CommandsRegistry.registerCommand({
834
id: 'editor.action.findReferences',
835
handler: (accessor: ServicesAccessor, resource: any, position: any) => {
836
assertType(URI.isUri(resource));
837
assertType(corePosition.Position.isIPosition(position));
838
839
const languageFeaturesService = accessor.get(ILanguageFeaturesService);
840
const codeEditorService = accessor.get(ICodeEditorService);
841
return codeEditorService.openCodeEditor({ resource }, codeEditorService.getFocusedCodeEditor()).then(control => {
842
if (!isCodeEditor(control) || !control.hasModel()) {
843
return undefined;
844
}
845
846
const controller = ReferencesController.get(control);
847
if (!controller) {
848
return undefined;
849
}
850
851
const references = createCancelablePromise(token => getReferencesAtPosition(languageFeaturesService.referenceProvider, control.getModel(), corePosition.Position.lift(position), false, false, token).then(references => new ReferencesModel(references, nls.localize('ref.title', 'References'))));
852
const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
853
return Promise.resolve(controller.toggleWidget(range, references, false));
854
});
855
}
856
});
857
858
// use NEW command
859
CommandsRegistry.registerCommandAlias('editor.action.showReferences', 'editor.action.peekLocations');
860
861
//#endregion
862
863