@theia/plugin-ext-vscode 1.53.0-next.4 → 1.53.0-next.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,951 +1,987 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2018 Red Hat, Inc. and others.
3
- //
4
- // This program and the accompanying materials are made available under the
5
- // terms of the Eclipse Public License v. 2.0 which is available at
6
- // http://www.eclipse.org/legal/epl-2.0.
7
- //
8
- // This Source Code may also be made available under the following Secondary
9
- // Licenses when the conditions for such availability set forth in the Eclipse
10
- // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- // with the GNU Classpath Exception which is available at
12
- // https://www.gnu.org/software/classpath/license.html.
13
- //
14
- // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
- // *****************************************************************************
16
-
17
- import { Command, CommandContribution, CommandRegistry, environment, isOSX, CancellationTokenSource, MessageService } from '@theia/core';
18
- import {
19
- ApplicationShell,
20
- CommonCommands,
21
- NavigatableWidget,
22
- OpenerService, OpenHandler,
23
- QuickInputService,
24
- Saveable,
25
- TabBar,
26
- Title,
27
- Widget
28
- } from '@theia/core/lib/browser';
29
- import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
30
- import { ApplicationShellMouseTracker } from '@theia/core/lib/browser/shell/application-shell-mouse-tracker';
31
- import { CommandService } from '@theia/core/lib/common/command';
32
- import TheiaURI from '@theia/core/lib/common/uri';
33
- import { EditorManager, EditorCommands } from '@theia/editor/lib/browser';
34
- import {
35
- TextDocumentShowOptions,
36
- Location,
37
- CallHierarchyItem,
38
- CallHierarchyIncomingCall,
39
- CallHierarchyOutgoingCall,
40
- TypeHierarchyItem,
41
- Hover,
42
- TextEdit,
43
- FormattingOptions,
44
- DocumentHighlight
45
- } from '@theia/plugin-ext/lib/common/plugin-api-rpc-model';
46
- import { DocumentsMainImpl } from '@theia/plugin-ext/lib/main/browser/documents-main';
47
- import { isUriComponents, toDocumentSymbol, toPosition } from '@theia/plugin-ext/lib/plugin/type-converters';
48
- import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl';
49
- import { WorkspaceCommands } from '@theia/workspace/lib/browser';
50
- import { WorkspaceService, WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
51
- import { DiffService } from '@theia/workspace/lib/browser/diff-service';
52
- import { inject, injectable, optional } from '@theia/core/shared/inversify';
53
- import { Position } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
54
- import { URI } from '@theia/core/shared/vscode-uri';
55
- import { PluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
56
- import { TerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
57
- import { QuickOpenWorkspace } from '@theia/workspace/lib/browser/quick-open-workspace';
58
- import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
59
- import {
60
- FileNavigatorCommands,
61
- FILE_NAVIGATOR_TOGGLE_COMMAND_ID
62
- } from '@theia/navigator/lib/browser/navigator-contribution';
63
- import { FILE_NAVIGATOR_ID, FileNavigatorWidget } from '@theia/navigator/lib/browser';
64
- import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection';
65
- import { UriComponents } from '@theia/plugin-ext/lib/common/uri-components';
66
- import { FileService } from '@theia/filesystem/lib/browser/file-service';
67
- import { CallHierarchyServiceProvider, CallHierarchyService } from '@theia/callhierarchy/lib/browser';
68
- import { TypeHierarchyServiceProvider, TypeHierarchyService } from '@theia/typehierarchy/lib/browser';
69
- import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
70
- import {
71
- fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall,
72
- fromCallHierarchyCallerToModelCallHierarchyIncomingCall,
73
- fromItemHierarchyDefinition,
74
- toItemHierarchyDefinition
75
- } from '@theia/plugin-ext/lib/main/browser/hierarchy/hierarchy-types-converters';
76
- import { CustomEditorOpener } from '@theia/plugin-ext/lib/main/browser/custom-editors/custom-editor-opener';
77
- import { nls } from '@theia/core/lib/common/nls';
78
- import { WindowService } from '@theia/core/lib/browser/window/window-service';
79
- import * as monaco from '@theia/monaco-editor-core';
80
- import { VSCodeExtensionUri } from '../common/plugin-vscode-uri';
81
- import { CodeEditorWidgetUtil } from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings';
82
- import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
83
-
84
- export namespace VscodeCommands {
85
-
86
- export const GET_CODE_EXCHANGE_ENDPOINTS: Command = {
87
- id: 'workbench.getCodeExchangeProxyEndpoints' // this command is used in the github auth built-in
88
- // see: https://github.com/microsoft/vscode/blob/191be39e5ac872e03f9d79cc859d9917f40ad935/extensions/github-authentication/src/githubServer.ts#L60
89
- };
90
-
91
- export const OPEN: Command = {
92
- id: 'vscode.open'
93
- };
94
-
95
- export const OPEN_WITH: Command = {
96
- id: 'vscode.openWith'
97
- };
98
-
99
- export const OPEN_FOLDER: Command = {
100
- id: 'vscode.openFolder'
101
- };
102
-
103
- export const DIFF: Command = {
104
- id: 'vscode.diff'
105
- };
106
-
107
- export const INSTALL_FROM_VSIX: Command = {
108
- id: 'workbench.extensions.installExtension'
109
- };
110
- }
111
-
112
- // https://wicg.github.io/webusb/
113
-
114
- export interface UsbDeviceData {
115
- readonly deviceClass: number;
116
- readonly deviceProtocol: number;
117
- readonly deviceSubclass: number;
118
- readonly deviceVersionMajor: number;
119
- readonly deviceVersionMinor: number;
120
- readonly deviceVersionSubminor: number;
121
- readonly manufacturerName?: string;
122
- readonly productId: number;
123
- readonly productName?: string;
124
- readonly serialNumber?: string;
125
- readonly usbVersionMajor: number;
126
- readonly usbVersionMinor: number;
127
- readonly usbVersionSubminor: number;
128
- readonly vendorId: number;
129
- }
130
-
131
- // https://wicg.github.io/serial/
132
-
133
- export interface SerialPortData {
134
- readonly usbVendorId?: number | undefined;
135
- readonly usbProductId?: number | undefined;
136
- }
137
-
138
- // https://wicg.github.io/webhid/
139
-
140
- export interface HidDeviceData {
141
- readonly opened: boolean;
142
- readonly vendorId: number;
143
- readonly productId: number;
144
- readonly productName: string;
145
- readonly collections: [];
146
- }
147
-
148
- @injectable()
149
- export class PluginVscodeCommandsContribution implements CommandContribution {
150
- @inject(CommandService)
151
- protected readonly commandService: CommandService;
152
- @inject(ContextKeyService)
153
- protected readonly contextKeyService: ContextKeyService;
154
- @inject(EditorManager)
155
- protected readonly editorManager: EditorManager;
156
- @inject(ApplicationShell)
157
- protected readonly shell: ApplicationShell;
158
- @inject(DiffService)
159
- protected readonly diffService: DiffService;
160
- @inject(OpenerService)
161
- protected readonly openerService: OpenerService;
162
- @inject(ApplicationShellMouseTracker)
163
- protected readonly mouseTracker: ApplicationShellMouseTracker;
164
- @inject(QuickInputService) @optional()
165
- protected readonly quickInput: QuickInputService;
166
- @inject(WorkspaceService)
167
- protected readonly workspaceService: WorkspaceService;
168
- @inject(TerminalFrontendContribution)
169
- protected readonly terminalContribution: TerminalFrontendContribution;
170
- @inject(QuickOpenWorkspace)
171
- protected readonly quickOpenWorkspace: QuickOpenWorkspace;
172
- @inject(TerminalService)
173
- protected readonly terminalService: TerminalService;
174
- @inject(CodeEditorWidgetUtil)
175
- protected readonly codeEditorWidgetUtil: CodeEditorWidgetUtil;
176
- @inject(PluginServer)
177
- protected readonly pluginServer: PluginServer;
178
- @inject(FileService)
179
- protected readonly fileService: FileService;
180
- @inject(CallHierarchyServiceProvider)
181
- protected readonly callHierarchyProvider: CallHierarchyServiceProvider;
182
- @inject(TypeHierarchyServiceProvider)
183
- protected readonly typeHierarchyProvider: TypeHierarchyServiceProvider;
184
- @inject(MonacoTextModelService)
185
- protected readonly textModelService: MonacoTextModelService;
186
- @inject(WindowService)
187
- protected readonly windowService: WindowService;
188
- @inject(MessageService)
189
- protected readonly messageService: MessageService;
190
- @inject(OutlineViewContribution)
191
- protected outlineViewContribution: OutlineViewContribution;
192
-
193
- private async openWith(commandId: string, resource: URI, columnOrOptions?: ViewColumn | TextDocumentShowOptions, openerId?: string): Promise<boolean> {
194
- if (!resource) {
195
- throw new Error(`${commandId} command requires at least URI argument.`);
196
- }
197
- if (!URI.isUri(resource)) {
198
- throw new Error(`Invalid argument for ${commandId} command with URI argument. Found ${resource}`);
199
- }
200
-
201
- let options: TextDocumentShowOptions | undefined;
202
- if (typeof columnOrOptions === 'number') {
203
- options = {
204
- viewColumn: columnOrOptions
205
- };
206
- } else if (columnOrOptions) {
207
- options = {
208
- ...columnOrOptions
209
- };
210
- }
211
-
212
- const uri = new TheiaURI(resource);
213
- const editorOptions = DocumentsMainImpl.toEditorOpenerOptions(this.shell, options);
214
-
215
- let openHandler: OpenHandler | undefined;
216
- if (typeof openerId === 'string') {
217
- const lowerViewType = openerId.toLowerCase();
218
- const openers = await this.openerService.getOpeners();
219
- for (const opener of openers) {
220
- const idLowerCase = opener.id.toLowerCase();
221
- if (lowerViewType === idLowerCase) {
222
- openHandler = opener;
223
- break;
224
- }
225
- }
226
- } else {
227
- openHandler = await this.openerService.getOpener(uri, editorOptions);
228
- }
229
-
230
- if (openHandler) {
231
- await openHandler.open(uri, editorOptions);
232
- return true;
233
- }
234
-
235
- return false;
236
- }
237
-
238
- registerCommands(commands: CommandRegistry): void {
239
- commands.registerCommand(VscodeCommands.GET_CODE_EXCHANGE_ENDPOINTS, {
240
- execute: () => undefined // this is a dummy implementation: only used in the case of web apps, which is not supported yet.
241
- });
242
-
243
- commands.registerCommand(VscodeCommands.OPEN, {
244
- isVisible: () => false,
245
- execute: async (resource: URI | string, columnOrOptions?: ViewColumn | TextDocumentShowOptions) => {
246
- if (typeof resource === 'string') {
247
- resource = URI.parse(resource);
248
- }
249
- try {
250
- await this.openWith(VscodeCommands.OPEN.id, resource, columnOrOptions);
251
- } catch (error) {
252
- const message = nls.localizeByDefault("Unable to open '{0}'", resource.path);
253
- const reason = nls.localizeByDefault('Error: {0}', error.message);
254
- this.messageService.error(`${message}\n${reason}`);
255
- console.warn(error);
256
- }
257
- }
258
- });
259
-
260
- commands.registerCommand(VscodeCommands.OPEN_WITH, {
261
- isVisible: () => false,
262
- execute: async (resource: URI, viewType: string, columnOrOptions?: ViewColumn | TextDocumentShowOptions) => {
263
- if (!viewType) {
264
- throw new Error(`Running the contributed command: ${VscodeCommands.OPEN_WITH} failed.`);
265
- }
266
-
267
- if (viewType.toLowerCase() === 'default') {
268
- return commands.executeCommand(VscodeCommands.OPEN.id, resource, columnOrOptions);
269
- }
270
-
271
- let result = await this.openWith(VscodeCommands.OPEN_WITH.id, resource, columnOrOptions, viewType);
272
- if (!result) {
273
- result = await this.openWith(VscodeCommands.OPEN_WITH.id, resource, columnOrOptions, CustomEditorOpener.toCustomEditorId(viewType));
274
- }
275
-
276
- if (!result) {
277
- throw new Error(`Could not find an editor for '${viewType}'`);
278
- }
279
- }
280
- });
281
-
282
- interface IOpenFolderAPICommandOptions {
283
- forceNewWindow?: boolean;
284
- forceReuseWindow?: boolean;
285
- noRecentEntry?: boolean;
286
- }
287
-
288
- commands.registerCommand(VscodeCommands.OPEN_FOLDER, {
289
- isVisible: () => false,
290
- execute: async (resource?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}) => {
291
- if (!resource) {
292
- return commands.executeCommand(WorkspaceCommands.OPEN_WORKSPACE.id);
293
- }
294
- if (!URI.isUri(resource)) {
295
- throw new Error(`Invalid argument for ${VscodeCommands.OPEN_FOLDER.id} command with URI argument. Found ${resource}`);
296
- }
297
- let options: WorkspaceInput | undefined;
298
- if (typeof arg === 'boolean') {
299
- options = { preserveWindow: !arg };
300
- } else {
301
- options = { preserveWindow: !arg.forceNewWindow };
302
- }
303
- this.workspaceService.open(new TheiaURI(resource), options);
304
- }
305
- });
306
-
307
- commands.registerCommand(VscodeCommands.DIFF, {
308
- isVisible: () => false,
309
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
310
- execute: async (left: URI, right: URI, label?: string, options?: TextDocumentShowOptions) => {
311
- if (!left || !right) {
312
- throw new Error(`${VscodeCommands.DIFF} command requires at least two URI arguments. Found left=${left}, right=${right} as arguments`);
313
- }
314
- if (!URI.isUri(left)) {
315
- throw new Error(`Invalid argument for ${VscodeCommands.DIFF.id} command with left argument. Expecting URI left type but found ${left}`);
316
- }
317
- if (!URI.isUri(right)) {
318
- throw new Error(`Invalid argument for ${VscodeCommands.DIFF.id} command with right argument. Expecting URI right type but found ${right}`);
319
- }
320
-
321
- const leftURI = new TheiaURI(left);
322
- const editorOptions = DocumentsMainImpl.toEditorOpenerOptions(this.shell, options);
323
- await this.diffService.openDiffEditor(leftURI, new TheiaURI(right), label, editorOptions);
324
- }
325
- });
326
-
327
- // https://code.visualstudio.com/docs/getstarted/keybindings#_navigation
328
- /*
329
- * internally, in VS Code, any widget opened in the main area is represented as an editor
330
- * operations below apply to them, but not to side-bar widgets, like the explorer
331
- *
332
- * in Theia, there are not such difference and any widget can be put in any area
333
- * because of it we filter out editors from views based on `NavigatableWidget.is`
334
- * and apply actions only to them
335
- */
336
- if (!environment.electron.is() || isOSX) {
337
- commands.registerCommand({ id: 'workbench.action.files.openFileFolder' }, {
338
- execute: () => commands.executeCommand(WorkspaceCommands.OPEN.id)
339
- });
340
- }
341
-
342
- commands.registerCommand({ id: 'workbench.action.files.openFile' }, {
343
- execute: () => commands.executeCommand(WorkspaceCommands.OPEN_FILE.id)
344
- });
345
- commands.registerCommand({ id: 'workbench.action.files.openFolder' }, {
346
- execute: () => commands.executeCommand(WorkspaceCommands.OPEN_FOLDER.id)
347
- });
348
- commands.registerCommand({ id: 'workbench.action.addRootFolder' }, {
349
- execute: () => commands.executeCommand(WorkspaceCommands.ADD_FOLDER.id)
350
- });
351
- commands.registerCommand({ id: 'workbench.action.saveWorkspaceAs' }, {
352
- execute: () => commands.executeCommand(WorkspaceCommands.SAVE_WORKSPACE_AS.id)
353
- });
354
- commands.registerCommand({ id: 'workbench.action.gotoLine' }, {
355
- execute: () => commands.executeCommand(EditorCommands.GOTO_LINE_COLUMN.id)
356
- });
357
- commands.registerCommand({ id: 'workbench.action.quickOpen' }, {
358
- execute: (prefix?: unknown) => this.quickInput.open(typeof prefix === 'string' ? prefix : '')
359
- });
360
- commands.registerCommand({ id: 'workbench.action.openSettings' }, {
361
- execute: (query?: string) => commands.executeCommand(CommonCommands.OPEN_PREFERENCES.id, query)
362
- });
363
- commands.registerCommand({ id: 'workbench.action.openWorkspaceConfigFile' }, {
364
- execute: () => commands.executeCommand(WorkspaceCommands.OPEN_WORKSPACE_FILE.id)
365
- });
366
- commands.registerCommand({ id: 'workbench.files.action.refreshFilesExplorer' }, {
367
- execute: () => commands.executeCommand(FileNavigatorCommands.REFRESH_NAVIGATOR.id)
368
- });
369
- commands.registerCommand({ id: VscodeCommands.INSTALL_FROM_VSIX.id }, {
370
- execute: async (vsixUriOrExtensionId: TheiaURI | UriComponents | string) => {
371
- if (typeof vsixUriOrExtensionId === 'string') {
372
- await this.pluginServer.deploy(VSCodeExtensionUri.fromId(vsixUriOrExtensionId).toString());
373
- } else {
374
- const uriPath = isUriComponents(vsixUriOrExtensionId) ? URI.revive(vsixUriOrExtensionId).fsPath : await this.fileService.fsPath(vsixUriOrExtensionId);
375
- await this.pluginServer.deploy(`local-file:${uriPath}`);
376
- }
377
- }
378
- });
379
- commands.registerCommand({ id: 'workbench.action.files.save', }, {
380
- execute: (uri?: monaco.Uri) => {
381
- if (uri) {
382
- const uriString = uri.toString();
383
- const widget = this.shell.widgets.find(w => {
384
- const resourceUri = Saveable.is(w) && NavigatableWidget.is(w) && w.getResourceUri();
385
- return (resourceUri && resourceUri.toString()) === uriString;
386
- });
387
- if (Saveable.is(widget)) {
388
- Saveable.save(widget);
389
- }
390
- } else {
391
- this.shell.save();
392
- }
393
- }
394
- });
395
- commands.registerCommand({ id: 'workbench.action.files.saveAll', }, {
396
- execute: () => this.shell.saveAll()
397
- });
398
- commands.registerCommand({ id: 'workbench.action.closeActiveEditor' }, {
399
- execute: () => commands.executeCommand(CommonCommands.CLOSE_MAIN_TAB.id)
400
- });
401
- commands.registerCommand({ id: 'workbench.action.closeOtherEditors' }, {
402
- execute: async (uri?: monaco.Uri) => {
403
- let editor = this.editorManager.currentEditor || this.shell.currentWidget;
404
- if (uri) {
405
- const uriString = uri.toString();
406
- editor = this.editorManager.all.find(e => {
407
- const resourceUri = e.getResourceUri();
408
- return (resourceUri && resourceUri.toString()) === uriString;
409
- });
410
- }
411
- const toClose = this.shell.widgets.filter(widget => widget !== editor && this.codeEditorWidgetUtil.is(widget));
412
- await this.shell.closeMany(toClose);
413
- }
414
- });
415
-
416
- const performActionOnGroup = (
417
- cb: (
418
- tabBarOrArea: TabBar<Widget> | ApplicationShell.Area,
419
- filter?: ((title: Title<Widget>, index: number) => boolean) | undefined
420
- ) => void,
421
- uri?: monaco.Uri
422
- ): void => {
423
- let editor = this.editorManager.currentEditor || this.shell.currentWidget;
424
- if (uri) {
425
- const uriString = uri.toString();
426
- editor = this.editorManager.all.find(e => {
427
- const resourceUri = e.getResourceUri();
428
- return (resourceUri && resourceUri.toString()) === uriString;
429
- });
430
- }
431
- if (editor) {
432
- const tabBar = this.shell.getTabBarFor(editor);
433
- if (tabBar) {
434
- cb(tabBar, ({ owner }) => this.codeEditorWidgetUtil.is(owner));
435
- }
436
- }
437
- };
438
-
439
- commands.registerCommand({
440
- id: 'workbench.action.closeEditorsInGroup',
441
- label: nls.localizeByDefault('Close All Editors in Group')
442
- }, {
443
- execute: (uri?: monaco.Uri) => performActionOnGroup(this.shell.closeTabs, uri)
444
- });
445
- commands.registerCommand({
446
- id: 'workbench.files.saveAllInGroup',
447
- label: nls.localizeByDefault('Save All in Group')
448
- }, {
449
- execute: (uri?: monaco.Uri) => performActionOnGroup(this.shell.saveTabs, uri)
450
- });
451
- commands.registerCommand({ id: 'workbench.action.closeEditorsInOtherGroups' }, {
452
- execute: () => {
453
- const editor = this.editorManager.currentEditor || this.shell.currentWidget;
454
- if (editor) {
455
- const editorTabBar = this.shell.getTabBarFor(editor);
456
- for (const tabBar of this.shell.allTabBars) {
457
- if (tabBar !== editorTabBar) {
458
- this.shell.closeTabs(tabBar,
459
- ({ owner }) => this.codeEditorWidgetUtil.is(owner)
460
- );
461
- }
462
- }
463
- }
464
- }
465
- });
466
- commands.registerCommand({ id: 'workbench.action.closeEditorsToTheLeft' }, {
467
- execute: () => {
468
- const editor = this.editorManager.currentEditor || this.shell.currentWidget;
469
- if (editor) {
470
- const tabBar = this.shell.getTabBarFor(editor);
471
- if (tabBar) {
472
- let left = true;
473
- this.shell.closeTabs(tabBar,
474
- ({ owner }) => {
475
- if (owner === editor) {
476
- left = false;
477
- return false;
478
- }
479
- return left && this.codeEditorWidgetUtil.is(owner);
480
- }
481
- );
482
- }
483
- }
484
- }
485
- });
486
- commands.registerCommand({ id: 'workbench.action.closeEditorsToTheRight' }, {
487
- execute: () => {
488
- const editor = this.editorManager.currentEditor || this.shell.currentWidget;
489
- if (editor) {
490
- const tabBar = this.shell.getTabBarFor(editor);
491
- if (tabBar) {
492
- let left = true;
493
- this.shell.closeTabs(tabBar,
494
- ({ owner }) => {
495
- if (owner === editor) {
496
- left = false;
497
- return false;
498
- }
499
- return !left && this.codeEditorWidgetUtil.is(owner);
500
- }
501
- );
502
- }
503
- }
504
- }
505
- });
506
- commands.registerCommand({ id: 'workbench.action.closeAllEditors' }, {
507
- execute: async () => {
508
- const toClose = this.shell.widgets.filter(widget => this.codeEditorWidgetUtil.is(widget));
509
- await this.shell.closeMany(toClose);
510
- }
511
- });
512
- commands.registerCommand({ id: 'workbench.action.nextEditor' }, {
513
- execute: () => this.shell.activateNextTab()
514
- });
515
- commands.registerCommand({ id: 'workbench.action.previousEditor' }, {
516
- execute: () => this.shell.activatePreviousTab()
517
- });
518
- commands.registerCommand({ id: 'workbench.action.navigateBack' }, {
519
- execute: () => commands.executeCommand(EditorCommands.GO_BACK.id)
520
- });
521
- commands.registerCommand({ id: 'workbench.action.navigateForward' }, {
522
- execute: () => commands.executeCommand(EditorCommands.GO_FORWARD.id)
523
- });
524
- commands.registerCommand({ id: 'workbench.action.navigateToLastEditLocation' }, {
525
- execute: () => commands.executeCommand(EditorCommands.GO_LAST_EDIT.id)
526
- });
527
-
528
- commands.registerCommand({ id: 'openInTerminal' }, {
529
- execute: (resource: URI) => this.terminalContribution.openInTerminal(new TheiaURI(resource.toString()))
530
- });
531
-
532
- commands.registerCommand({ id: 'workbench.action.reloadWindow' }, {
533
- execute: () => {
534
- this.windowService.reload();
535
- }
536
- });
537
-
538
- /**
539
- * TODO:
540
- * Open Next: workbench.action.openNextRecentlyUsedEditorInGroup
541
- * Open Previous: workbench.action.openPreviousRecentlyUsedEditorInGroup
542
- * Copy Path of Active File: workbench.action.files.copyPathOfActiveFile
543
- * Reveal Active File in Windows: workbench.action.files.revealActiveFileInWindows
544
- * Show Opened File in New Window: workbench.action.files.showOpenedFileInNewWindow
545
- * Compare Opened File With: workbench.files.action.compareFileWith
546
- */
547
-
548
- // Register built-in language service commands
549
- // see https://code.visualstudio.com/api/references/commands
550
- /* eslint-disable @typescript-eslint/no-explicit-any */
551
-
552
- // TODO register other `vscode.execute...` commands.
553
- // see https://github.com/microsoft/vscode/blob/master/src/vs/workbench/api/common/extHostApiCommands.ts
554
- commands.registerCommand(
555
- {
556
- id: 'vscode.executeDefinitionProvider'
557
- },
558
- {
559
- execute: ((resource: URI, position: Position) =>
560
- commands.executeCommand<Location[]>('_executeDefinitionProvider', monaco.Uri.from(resource), position))
561
- }
562
- );
563
- commands.registerCommand(
564
- {
565
- id: 'vscode.executeDeclarationProvider'
566
- },
567
- {
568
- execute: ((resource: URI, position: Position) =>
569
- commands.executeCommand<Location[]>('_executeDeclarationProvider', monaco.Uri.from(resource), position))
570
- }
571
- );
572
- commands.registerCommand(
573
- {
574
- id: 'vscode.executeTypeDefinitionProvider'
575
- },
576
- {
577
- execute: ((resource: URI, position: Position) =>
578
- commands.executeCommand<Location[]>('_executeTypeDefinitionProvider', monaco.Uri.from(resource), position))
579
- }
580
- );
581
- commands.registerCommand(
582
- {
583
- id: 'vscode.executeImplementationProvider'
584
- },
585
- {
586
- execute: ((resource: URI, position: Position) =>
587
- commands.executeCommand<Location[]>('_executeImplementationProvider', monaco.Uri.from(resource), position))
588
- }
589
- );
590
- commands.registerCommand(
591
- {
592
- id: 'vscode.executeHoverProvider'
593
- },
594
- {
595
- execute: ((resource: URI, position: Position) =>
596
- commands.executeCommand<Hover[]>('_executeHoverProvider', monaco.Uri.from(resource), position))
597
- }
598
- );
599
- commands.registerCommand(
600
- {
601
- id: 'vscode.executeDocumentHighlights'
602
- },
603
- {
604
- execute: ((resource: URI, position: Position) =>
605
- commands.executeCommand<DocumentHighlight[]>('_executeDocumentHighlights', monaco.Uri.from(resource), position))
606
- }
607
- );
608
- commands.registerCommand(
609
- {
610
- id: 'vscode.executeReferenceProvider'
611
- },
612
- {
613
- execute: ((resource: URI, position: Position) => commands.executeCommand<Location[]>('_executeReferenceProvider', monaco.Uri.from(resource), position))
614
- }
615
- );
616
- commands.registerCommand(
617
- {
618
- id: 'vscode.executeDocumentSymbolProvider'
619
- },
620
- {
621
- execute: (resource: URI) => commands.executeCommand('_executeDocumentSymbolProvider',
622
- monaco.Uri.parse(resource.toString())
623
- ).then((value: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
624
- if (!Array.isArray(value) || value === undefined) {
625
- return undefined;
626
- }
627
- return value.map(loc => toDocumentSymbol(loc));
628
- })
629
- }
630
- );
631
- commands.registerCommand(
632
- {
633
- id: 'vscode.executeFormatDocumentProvider'
634
- },
635
- {
636
- execute: ((resource: URI, options: FormattingOptions) =>
637
- commands.executeCommand<TextEdit[]>('_executeFormatDocumentProvider', monaco.Uri.from(resource), options))
638
- }
639
- );
640
- commands.registerCommand(
641
- {
642
- id: 'vscode.executeFormatRangeProvider'
643
- },
644
- {
645
- execute: ((resource: URI, range: Range, options: FormattingOptions) =>
646
- commands.executeCommand<TextEdit[]>('_executeFormatRangeProvider', monaco.Uri.from(resource), range, options))
647
- }
648
- );
649
- commands.registerCommand(
650
- {
651
- id: 'vscode.executeFormatOnTypeProvider'
652
- },
653
- {
654
- execute: ((resource: URI, position: Position, ch: string, options: FormattingOptions) =>
655
- commands.executeCommand<TextEdit[]>('_executeFormatOnTypeProvider', monaco.Uri.from(resource), position, ch, options))
656
- }
657
- );
658
- commands.registerCommand(
659
- {
660
- id: 'vscode.prepareCallHierarchy'
661
- },
662
- {
663
- execute: async (resource: URI, position: Position): Promise<CallHierarchyItem[]> => {
664
- const provider = await this.getCallHierarchyServiceForUri(resource);
665
- const definition = await provider?.getRootDefinition(
666
- resource.path,
667
- toPosition(position),
668
- new CancellationTokenSource().token
669
- );
670
- if (definition) {
671
- return definition.items.map(item => fromItemHierarchyDefinition(item));
672
- };
673
- return [];
674
- }
675
- }
676
- );
677
- commands.registerCommand(
678
- {
679
- id: 'vscode.provideIncomingCalls'
680
- },
681
- {
682
- execute: async (item: CallHierarchyItem): Promise<CallHierarchyIncomingCall[]> => {
683
- const resource = URI.from(item.uri);
684
- const provider = await this.getCallHierarchyServiceForUri(resource);
685
- const incomingCalls = await provider?.getCallers(
686
- toItemHierarchyDefinition(item),
687
- new CancellationTokenSource().token,
688
- );
689
- if (incomingCalls) {
690
- return incomingCalls.map(fromCallHierarchyCallerToModelCallHierarchyIncomingCall);
691
- }
692
- return [];
693
- },
694
- },
695
- );
696
- commands.registerCommand(
697
- {
698
- id: 'vscode.provideOutgoingCalls'
699
- },
700
- {
701
- execute: async (item: CallHierarchyItem): Promise<CallHierarchyOutgoingCall[]> => {
702
- const resource = URI.from(item.uri);
703
- const provider = await this.getCallHierarchyServiceForUri(resource);
704
- const outgoingCalls = await provider?.getCallees?.(
705
- toItemHierarchyDefinition(item),
706
- new CancellationTokenSource().token,
707
- );
708
- if (outgoingCalls) {
709
- return outgoingCalls.map(fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall);
710
- }
711
- return [];
712
- }
713
- }
714
- );
715
- commands.registerCommand(
716
- {
717
- id: 'vscode.prepareTypeHierarchy'
718
- },
719
- {
720
- execute: async (resource: URI, position: Position): Promise<TypeHierarchyItem[]> => {
721
- const provider = await this.getTypeHierarchyServiceForUri(resource);
722
- const session = await provider?.prepareSession(
723
- resource.path,
724
- toPosition(position),
725
- new CancellationTokenSource().token
726
- );
727
- return session ? session.items.map(item => fromItemHierarchyDefinition(item)) : [];
728
- }
729
- }
730
- );
731
- commands.registerCommand(
732
- {
733
- id: 'vscode.provideSupertypes'
734
- },
735
- {
736
- execute: async (item: TypeHierarchyItem): Promise<TypeHierarchyItem[]> => {
737
- if (!item._sessionId || !item._itemId) {
738
- return [];
739
- }
740
- const resource = URI.from(item.uri);
741
- const provider = await this.getTypeHierarchyServiceForUri(resource);
742
- const items = await provider?.provideSuperTypes(
743
- item._sessionId,
744
- item._itemId,
745
- new CancellationTokenSource().token
746
- );
747
- return (items ? items : []).map(typeItem => fromItemHierarchyDefinition(typeItem));
748
- }
749
- }
750
- );
751
- commands.registerCommand(
752
- {
753
- id: 'vscode.provideSubtypes'
754
- },
755
- {
756
- execute: async (item: TypeHierarchyItem): Promise<TypeHierarchyItem[]> => {
757
- if (!item._sessionId || !item._itemId) {
758
- return [];
759
- }
760
- const resource = URI.from(item.uri);
761
- const provider = await this.getTypeHierarchyServiceForUri(resource);
762
- const items = await provider?.provideSubTypes(
763
- item._sessionId, item._itemId,
764
- new CancellationTokenSource().token
765
- );
766
- return (items ? items : []).map(typeItem => fromItemHierarchyDefinition(typeItem));
767
-
768
- }
769
- }
770
- );
771
-
772
- commands.registerCommand({
773
- id: 'workbench.action.openRecent'
774
- }, {
775
- execute: () => this.quickOpenWorkspace.select()
776
- });
777
- commands.registerCommand({
778
- id: 'explorer.newFolder'
779
- }, {
780
- execute: () => commands.executeCommand(WorkspaceCommands.NEW_FOLDER.id)
781
- });
782
- commands.registerCommand({
783
- id: 'workbench.action.terminal.sendSequence'
784
- }, {
785
- execute: (args?: { text?: string }) => {
786
- if (args === undefined || args.text === undefined) {
787
- return;
788
- }
789
-
790
- const currentTerminal = this.terminalService.currentTerminal;
791
-
792
- if (currentTerminal === undefined) {
793
- return;
794
- }
795
-
796
- currentTerminal.sendText(args.text);
797
- }
798
- });
799
- commands.registerCommand({
800
- id: 'workbench.action.terminal.kill'
801
- }, {
802
- execute: () => {
803
- const currentTerminal = this.terminalService.currentTerminal;
804
-
805
- if (currentTerminal === undefined) {
806
- return;
807
- }
808
-
809
- currentTerminal.dispose();
810
- }
811
- });
812
- commands.registerCommand({
813
- id: 'workbench.view.explorer'
814
- }, {
815
- execute: () => commands.executeCommand(FileNavigatorCommands.FOCUS.id)
816
- });
817
- commands.registerCommand({
818
- id: 'copyFilePath'
819
- }, {
820
- execute: () => commands.executeCommand(CommonCommands.COPY_PATH.id)
821
- });
822
- commands.registerCommand({
823
- id: 'copyRelativeFilePath'
824
- }, {
825
- execute: () => commands.executeCommand(WorkspaceCommands.COPY_RELATIVE_FILE_PATH.id)
826
- });
827
- commands.registerCommand({
828
- id: 'revealInExplorer'
829
- }, {
830
- execute: async (resource: URI | object) => {
831
- if (!URI.isUri(resource)) {
832
- return;
833
- }
834
- let navigator = await this.shell.revealWidget(FILE_NAVIGATOR_ID);
835
- if (!navigator) {
836
- await this.commandService.executeCommand(FILE_NAVIGATOR_TOGGLE_COMMAND_ID);
837
- navigator = await this.shell.revealWidget(FILE_NAVIGATOR_ID);
838
- }
839
- if (navigator instanceof FileNavigatorWidget) {
840
- const model = navigator.model;
841
- const node = await model.revealFile(new TheiaURI(resource));
842
- if (SelectableTreeNode.is(node)) {
843
- model.selectNode(node);
844
- }
845
- }
846
- }
847
- });
848
-
849
- commands.registerCommand({
850
- id: 'workbench.experimental.requestUsbDevice'
851
- }, {
852
- execute: async (options?: { filters?: unknown[] }): Promise<UsbDeviceData | undefined> => {
853
- const usb = (navigator as any).usb;
854
- if (!usb) {
855
- return undefined;
856
- }
857
-
858
- const device = await usb.requestDevice({ filters: options?.filters ?? [] });
859
- if (!device) {
860
- return undefined;
861
- }
862
-
863
- return {
864
- deviceClass: device.deviceClass,
865
- deviceProtocol: device.deviceProtocol,
866
- deviceSubclass: device.deviceSubclass,
867
- deviceVersionMajor: device.deviceVersionMajor,
868
- deviceVersionMinor: device.deviceVersionMinor,
869
- deviceVersionSubminor: device.deviceVersionSubminor,
870
- manufacturerName: device.manufacturerName,
871
- productId: device.productId,
872
- productName: device.productName,
873
- serialNumber: device.serialNumber,
874
- usbVersionMajor: device.usbVersionMajor,
875
- usbVersionMinor: device.usbVersionMinor,
876
- usbVersionSubminor: device.usbVersionSubminor,
877
- vendorId: device.vendorId,
878
- };
879
- }
880
- });
881
-
882
- commands.registerCommand({
883
- id: 'workbench.experimental.requestSerialPort'
884
- }, {
885
- execute: async (options?: { filters?: unknown[] }): Promise<SerialPortData | undefined> => {
886
- const serial = (navigator as any).serial;
887
- if (!serial) {
888
- return undefined;
889
- }
890
-
891
- const port = await serial.requestPort({ filters: options?.filters ?? [] });
892
- if (!port) {
893
- return undefined;
894
- }
895
-
896
- const info = port.getInfo();
897
- return {
898
- usbVendorId: info.usbVendorId,
899
- usbProductId: info.usbProductId
900
- };
901
- }
902
- });
903
-
904
- commands.registerCommand({
905
- id: 'workbench.experimental.requestHidDevice'
906
- }, {
907
- execute: async (options?: { filters?: unknown[] }): Promise<HidDeviceData | undefined> => {
908
- const hid = (navigator as any).hid;
909
- if (!hid) {
910
- return undefined;
911
- }
912
-
913
- const devices = await hid.requestDevice({ filters: options?.filters ?? [] });
914
- if (!devices.length) {
915
- return undefined;
916
- }
917
-
918
- const device = devices[0];
919
- return {
920
- opened: device.opened,
921
- vendorId: device.vendorId,
922
- productId: device.productId,
923
- productName: device.productName,
924
- collections: device.collections
925
- };
926
- }
927
- });
928
-
929
- // required by Jupyter for the show table of contents action
930
- commands.registerCommand({ id: 'outline.focus' }, {
931
- execute: () => this.outlineViewContribution.openView({ activate: true })
932
- });
933
- }
934
-
935
- private async resolveLanguageId(resource: URI): Promise<string> {
936
- const reference = await this.textModelService.createModelReference(resource);
937
- const languageId = reference.object.languageId;
938
- reference.dispose();
939
- return languageId;
940
- }
941
-
942
- protected async getCallHierarchyServiceForUri(resource: URI): Promise<CallHierarchyService | undefined> {
943
- const languageId = await this.resolveLanguageId(resource);
944
- return this.callHierarchyProvider.get(languageId, new TheiaURI(resource));
945
- }
946
-
947
- protected async getTypeHierarchyServiceForUri(resource: URI): Promise<TypeHierarchyService | undefined> {
948
- const languageId = await this.resolveLanguageId(resource);
949
- return this.typeHierarchyProvider.get(languageId, new TheiaURI(resource));
950
- }
951
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2018 Red Hat, Inc. and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { Command, CommandContribution, CommandRegistry, environment, isOSX, CancellationTokenSource, MessageService } from '@theia/core';
18
+ import {
19
+ ApplicationShell,
20
+ CommonCommands,
21
+ NavigatableWidget,
22
+ OpenerService, OpenHandler,
23
+ QuickInputService,
24
+ Saveable,
25
+ TabBar,
26
+ Title,
27
+ Widget
28
+ } from '@theia/core/lib/browser';
29
+ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
30
+ import { ApplicationShellMouseTracker } from '@theia/core/lib/browser/shell/application-shell-mouse-tracker';
31
+ import { CommandService } from '@theia/core/lib/common/command';
32
+ import TheiaURI from '@theia/core/lib/common/uri';
33
+ import { EditorManager, EditorCommands } from '@theia/editor/lib/browser';
34
+ import {
35
+ TextDocumentShowOptions,
36
+ Location,
37
+ CallHierarchyItem,
38
+ CallHierarchyIncomingCall,
39
+ CallHierarchyOutgoingCall,
40
+ TypeHierarchyItem,
41
+ Hover,
42
+ TextEdit,
43
+ FormattingOptions,
44
+ DocumentHighlight
45
+ } from '@theia/plugin-ext/lib/common/plugin-api-rpc-model';
46
+ import { DocumentsMainImpl } from '@theia/plugin-ext/lib/main/browser/documents-main';
47
+ import { isUriComponents, toMergedSymbol, toPosition } from '@theia/plugin-ext/lib/plugin/type-converters';
48
+ import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl';
49
+ import { WorkspaceCommands } from '@theia/workspace/lib/browser';
50
+ import { WorkspaceService, WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
51
+ import { DiffService } from '@theia/workspace/lib/browser/diff-service';
52
+ import { inject, injectable, optional } from '@theia/core/shared/inversify';
53
+ import { Position } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
54
+ import { URI } from '@theia/core/shared/vscode-uri';
55
+ import { PluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
56
+ import { TerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
57
+ import { QuickOpenWorkspace } from '@theia/workspace/lib/browser/quick-open-workspace';
58
+ import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
59
+ import {
60
+ FileNavigatorCommands,
61
+ FILE_NAVIGATOR_TOGGLE_COMMAND_ID
62
+ } from '@theia/navigator/lib/browser/navigator-contribution';
63
+ import { FILE_NAVIGATOR_ID, FileNavigatorWidget } from '@theia/navigator/lib/browser';
64
+ import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection';
65
+ import { UriComponents } from '@theia/plugin-ext/lib/common/uri-components';
66
+ import { FileService } from '@theia/filesystem/lib/browser/file-service';
67
+ import { CallHierarchyServiceProvider, CallHierarchyService } from '@theia/callhierarchy/lib/browser';
68
+ import { TypeHierarchyServiceProvider, TypeHierarchyService } from '@theia/typehierarchy/lib/browser';
69
+ import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
70
+ import {
71
+ fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall,
72
+ fromCallHierarchyCallerToModelCallHierarchyIncomingCall,
73
+ fromItemHierarchyDefinition,
74
+ toItemHierarchyDefinition
75
+ } from '@theia/plugin-ext/lib/main/browser/hierarchy/hierarchy-types-converters';
76
+ import { CustomEditorOpener } from '@theia/plugin-ext/lib/main/browser/custom-editors/custom-editor-opener';
77
+ import { nls } from '@theia/core/lib/common/nls';
78
+ import { WindowService } from '@theia/core/lib/browser/window/window-service';
79
+ import * as monaco from '@theia/monaco-editor-core';
80
+ import { VSCodeExtensionUri } from '../common/plugin-vscode-uri';
81
+ import { CodeEditorWidgetUtil } from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings';
82
+ import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
83
+ import { Range } from '@theia/plugin';
84
+ import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages';
85
+
86
+ export namespace VscodeCommands {
87
+
88
+ export const GET_CODE_EXCHANGE_ENDPOINTS: Command = {
89
+ id: 'workbench.getCodeExchangeProxyEndpoints' // this command is used in the github auth built-in
90
+ // see: https://github.com/microsoft/vscode/blob/191be39e5ac872e03f9d79cc859d9917f40ad935/extensions/github-authentication/src/githubServer.ts#L60
91
+ };
92
+
93
+ export const OPEN: Command = {
94
+ id: 'vscode.open'
95
+ };
96
+
97
+ export const OPEN_WITH: Command = {
98
+ id: 'vscode.openWith'
99
+ };
100
+
101
+ export const OPEN_FOLDER: Command = {
102
+ id: 'vscode.openFolder'
103
+ };
104
+
105
+ export const DIFF: Command = {
106
+ id: 'vscode.diff'
107
+ };
108
+
109
+ export const INSTALL_FROM_VSIX: Command = {
110
+ id: 'workbench.extensions.installExtension'
111
+ };
112
+ }
113
+
114
+ // https://wicg.github.io/webusb/
115
+
116
+ export interface UsbDeviceData {
117
+ readonly deviceClass: number;
118
+ readonly deviceProtocol: number;
119
+ readonly deviceSubclass: number;
120
+ readonly deviceVersionMajor: number;
121
+ readonly deviceVersionMinor: number;
122
+ readonly deviceVersionSubminor: number;
123
+ readonly manufacturerName?: string;
124
+ readonly productId: number;
125
+ readonly productName?: string;
126
+ readonly serialNumber?: string;
127
+ readonly usbVersionMajor: number;
128
+ readonly usbVersionMinor: number;
129
+ readonly usbVersionSubminor: number;
130
+ readonly vendorId: number;
131
+ }
132
+
133
+ // https://wicg.github.io/serial/
134
+
135
+ export interface SerialPortData {
136
+ readonly usbVendorId?: number | undefined;
137
+ readonly usbProductId?: number | undefined;
138
+ }
139
+
140
+ // https://wicg.github.io/webhid/
141
+
142
+ export interface HidDeviceData {
143
+ readonly opened: boolean;
144
+ readonly vendorId: number;
145
+ readonly productId: number;
146
+ readonly productName: string;
147
+ readonly collections: [];
148
+ }
149
+
150
+ @injectable()
151
+ export class PluginVscodeCommandsContribution implements CommandContribution {
152
+ @inject(CommandService)
153
+ protected readonly commandService: CommandService;
154
+ @inject(ContextKeyService)
155
+ protected readonly contextKeyService: ContextKeyService;
156
+ @inject(EditorManager)
157
+ protected readonly editorManager: EditorManager;
158
+ @inject(ApplicationShell)
159
+ protected readonly shell: ApplicationShell;
160
+ @inject(DiffService)
161
+ protected readonly diffService: DiffService;
162
+ @inject(OpenerService)
163
+ protected readonly openerService: OpenerService;
164
+ @inject(ApplicationShellMouseTracker)
165
+ protected readonly mouseTracker: ApplicationShellMouseTracker;
166
+ @inject(QuickInputService) @optional()
167
+ protected readonly quickInput: QuickInputService;
168
+ @inject(WorkspaceService)
169
+ protected readonly workspaceService: WorkspaceService;
170
+ @inject(TerminalFrontendContribution)
171
+ protected readonly terminalContribution: TerminalFrontendContribution;
172
+ @inject(QuickOpenWorkspace)
173
+ protected readonly quickOpenWorkspace: QuickOpenWorkspace;
174
+ @inject(TerminalService)
175
+ protected readonly terminalService: TerminalService;
176
+ @inject(CodeEditorWidgetUtil)
177
+ protected readonly codeEditorWidgetUtil: CodeEditorWidgetUtil;
178
+ @inject(PluginServer)
179
+ protected readonly pluginServer: PluginServer;
180
+ @inject(FileService)
181
+ protected readonly fileService: FileService;
182
+ @inject(CallHierarchyServiceProvider)
183
+ protected readonly callHierarchyProvider: CallHierarchyServiceProvider;
184
+ @inject(TypeHierarchyServiceProvider)
185
+ protected readonly typeHierarchyProvider: TypeHierarchyServiceProvider;
186
+ @inject(MonacoTextModelService)
187
+ protected readonly textModelService: MonacoTextModelService;
188
+ @inject(WindowService)
189
+ protected readonly windowService: WindowService;
190
+ @inject(MessageService)
191
+ protected readonly messageService: MessageService;
192
+ @inject(OutlineViewContribution)
193
+ protected outlineViewContribution: OutlineViewContribution;
194
+ @inject(MonacoLanguages)
195
+ protected monacoLanguages: MonacoLanguages;
196
+
197
+ private async openWith(commandId: string, resource: URI, columnOrOptions?: ViewColumn | TextDocumentShowOptions, openerId?: string): Promise<boolean> {
198
+ if (!resource) {
199
+ throw new Error(`${commandId} command requires at least URI argument.`);
200
+ }
201
+ if (!URI.isUri(resource)) {
202
+ throw new Error(`Invalid argument for ${commandId} command with URI argument. Found ${resource}`);
203
+ }
204
+
205
+ let options: TextDocumentShowOptions | undefined;
206
+ if (typeof columnOrOptions === 'number') {
207
+ options = {
208
+ viewColumn: columnOrOptions
209
+ };
210
+ } else if (columnOrOptions) {
211
+ options = {
212
+ ...columnOrOptions
213
+ };
214
+ }
215
+
216
+ const uri = new TheiaURI(resource);
217
+ const editorOptions = DocumentsMainImpl.toEditorOpenerOptions(this.shell, options);
218
+
219
+ let openHandler: OpenHandler | undefined;
220
+ if (typeof openerId === 'string') {
221
+ const lowerViewType = openerId.toLowerCase();
222
+ const openers = await this.openerService.getOpeners();
223
+ for (const opener of openers) {
224
+ const idLowerCase = opener.id.toLowerCase();
225
+ if (lowerViewType === idLowerCase) {
226
+ openHandler = opener;
227
+ break;
228
+ }
229
+ }
230
+ } else {
231
+ openHandler = await this.openerService.getOpener(uri, editorOptions);
232
+ }
233
+
234
+ if (openHandler) {
235
+ await openHandler.open(uri, editorOptions);
236
+ return true;
237
+ }
238
+
239
+ return false;
240
+ }
241
+
242
+ registerCommands(commands: CommandRegistry): void {
243
+ commands.registerCommand(VscodeCommands.GET_CODE_EXCHANGE_ENDPOINTS, {
244
+ execute: () => undefined // this is a dummy implementation: only used in the case of web apps, which is not supported yet.
245
+ });
246
+
247
+ commands.registerCommand(VscodeCommands.OPEN, {
248
+ isVisible: () => false,
249
+ execute: async (resource: URI | string, columnOrOptions?: ViewColumn | TextDocumentShowOptions) => {
250
+ if (typeof resource === 'string') {
251
+ resource = URI.parse(resource);
252
+ }
253
+ try {
254
+ await this.openWith(VscodeCommands.OPEN.id, resource, columnOrOptions);
255
+ } catch (error) {
256
+ const message = nls.localizeByDefault("Unable to open '{0}'", resource.path);
257
+ const reason = nls.localizeByDefault('Error: {0}', error.message);
258
+ this.messageService.error(`${message}\n${reason}`);
259
+ console.warn(error);
260
+ }
261
+ }
262
+ });
263
+
264
+ commands.registerCommand(VscodeCommands.OPEN_WITH, {
265
+ isVisible: () => false,
266
+ execute: async (resource: URI, viewType: string, columnOrOptions?: ViewColumn | TextDocumentShowOptions) => {
267
+ if (!viewType) {
268
+ throw new Error(`Running the contributed command: ${VscodeCommands.OPEN_WITH} failed.`);
269
+ }
270
+
271
+ if (viewType.toLowerCase() === 'default') {
272
+ return commands.executeCommand(VscodeCommands.OPEN.id, resource, columnOrOptions);
273
+ }
274
+
275
+ let result = await this.openWith(VscodeCommands.OPEN_WITH.id, resource, columnOrOptions, viewType);
276
+ if (!result) {
277
+ result = await this.openWith(VscodeCommands.OPEN_WITH.id, resource, columnOrOptions, CustomEditorOpener.toCustomEditorId(viewType));
278
+ }
279
+
280
+ if (!result) {
281
+ throw new Error(`Could not find an editor for '${viewType}'`);
282
+ }
283
+ }
284
+ });
285
+
286
+ interface IOpenFolderAPICommandOptions {
287
+ forceNewWindow?: boolean;
288
+ forceReuseWindow?: boolean;
289
+ noRecentEntry?: boolean;
290
+ }
291
+
292
+ commands.registerCommand(VscodeCommands.OPEN_FOLDER, {
293
+ isVisible: () => false,
294
+ execute: async (resource?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}) => {
295
+ if (!resource) {
296
+ return commands.executeCommand(WorkspaceCommands.OPEN_WORKSPACE.id);
297
+ }
298
+ if (!URI.isUri(resource)) {
299
+ throw new Error(`Invalid argument for ${VscodeCommands.OPEN_FOLDER.id} command with URI argument. Found ${resource}`);
300
+ }
301
+ let options: WorkspaceInput | undefined;
302
+ if (typeof arg === 'boolean') {
303
+ options = { preserveWindow: !arg };
304
+ } else {
305
+ options = { preserveWindow: !arg.forceNewWindow };
306
+ }
307
+ this.workspaceService.open(new TheiaURI(resource), options);
308
+ }
309
+ });
310
+
311
+ commands.registerCommand(VscodeCommands.DIFF, {
312
+ isVisible: () => false,
313
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
314
+ execute: async (left: URI, right: URI, label?: string, options?: TextDocumentShowOptions) => {
315
+ if (!left || !right) {
316
+ throw new Error(`${VscodeCommands.DIFF} command requires at least two URI arguments. Found left=${left}, right=${right} as arguments`);
317
+ }
318
+ if (!URI.isUri(left)) {
319
+ throw new Error(`Invalid argument for ${VscodeCommands.DIFF.id} command with left argument. Expecting URI left type but found ${left}`);
320
+ }
321
+ if (!URI.isUri(right)) {
322
+ throw new Error(`Invalid argument for ${VscodeCommands.DIFF.id} command with right argument. Expecting URI right type but found ${right}`);
323
+ }
324
+
325
+ const leftURI = new TheiaURI(left);
326
+ const editorOptions = DocumentsMainImpl.toEditorOpenerOptions(this.shell, options);
327
+ await this.diffService.openDiffEditor(leftURI, new TheiaURI(right), label, editorOptions);
328
+ }
329
+ });
330
+
331
+ // https://code.visualstudio.com/docs/getstarted/keybindings#_navigation
332
+ /*
333
+ * internally, in VS Code, any widget opened in the main area is represented as an editor
334
+ * operations below apply to them, but not to side-bar widgets, like the explorer
335
+ *
336
+ * in Theia, there are not such difference and any widget can be put in any area
337
+ * because of it we filter out editors from views based on `NavigatableWidget.is`
338
+ * and apply actions only to them
339
+ */
340
+ if (!environment.electron.is() || isOSX) {
341
+ commands.registerCommand({ id: 'workbench.action.files.openFileFolder' }, {
342
+ execute: () => commands.executeCommand(WorkspaceCommands.OPEN.id)
343
+ });
344
+ }
345
+
346
+ commands.registerCommand({ id: 'workbench.action.files.openFile' }, {
347
+ execute: () => commands.executeCommand(WorkspaceCommands.OPEN_FILE.id)
348
+ });
349
+ commands.registerCommand({ id: 'workbench.action.files.openFolder' }, {
350
+ execute: () => commands.executeCommand(WorkspaceCommands.OPEN_FOLDER.id)
351
+ });
352
+ commands.registerCommand({ id: 'workbench.action.addRootFolder' }, {
353
+ execute: () => commands.executeCommand(WorkspaceCommands.ADD_FOLDER.id)
354
+ });
355
+ commands.registerCommand({ id: 'workbench.action.saveWorkspaceAs' }, {
356
+ execute: () => commands.executeCommand(WorkspaceCommands.SAVE_WORKSPACE_AS.id)
357
+ });
358
+ commands.registerCommand({ id: 'workbench.action.gotoLine' }, {
359
+ execute: () => commands.executeCommand(EditorCommands.GOTO_LINE_COLUMN.id)
360
+ });
361
+ commands.registerCommand({ id: 'workbench.action.quickOpen' }, {
362
+ execute: (prefix?: unknown) => this.quickInput.open(typeof prefix === 'string' ? prefix : '')
363
+ });
364
+ commands.registerCommand({ id: 'workbench.action.openSettings' }, {
365
+ execute: (query?: string) => commands.executeCommand(CommonCommands.OPEN_PREFERENCES.id, query)
366
+ });
367
+ commands.registerCommand({ id: 'workbench.action.openWorkspaceConfigFile' }, {
368
+ execute: () => commands.executeCommand(WorkspaceCommands.OPEN_WORKSPACE_FILE.id)
369
+ });
370
+ commands.registerCommand({ id: 'workbench.files.action.refreshFilesExplorer' }, {
371
+ execute: () => commands.executeCommand(FileNavigatorCommands.REFRESH_NAVIGATOR.id)
372
+ });
373
+ commands.registerCommand({ id: VscodeCommands.INSTALL_FROM_VSIX.id }, {
374
+ execute: async (vsixUriOrExtensionId: TheiaURI | UriComponents | string) => {
375
+ if (typeof vsixUriOrExtensionId === 'string') {
376
+ await this.pluginServer.deploy(VSCodeExtensionUri.fromId(vsixUriOrExtensionId).toString());
377
+ } else {
378
+ const uriPath = isUriComponents(vsixUriOrExtensionId) ? URI.revive(vsixUriOrExtensionId).fsPath : await this.fileService.fsPath(vsixUriOrExtensionId);
379
+ await this.pluginServer.deploy(`local-file:${uriPath}`);
380
+ }
381
+ }
382
+ });
383
+ commands.registerCommand({ id: 'workbench.action.files.save', }, {
384
+ execute: (uri?: monaco.Uri) => {
385
+ if (uri) {
386
+ const uriString = uri.toString();
387
+ const widget = this.shell.widgets.find(w => {
388
+ const resourceUri = Saveable.is(w) && NavigatableWidget.is(w) && w.getResourceUri();
389
+ return (resourceUri && resourceUri.toString()) === uriString;
390
+ });
391
+ if (Saveable.is(widget)) {
392
+ Saveable.save(widget);
393
+ }
394
+ } else {
395
+ this.shell.save();
396
+ }
397
+ }
398
+ });
399
+ commands.registerCommand({ id: 'workbench.action.files.saveAll', }, {
400
+ execute: () => this.shell.saveAll()
401
+ });
402
+ commands.registerCommand({ id: 'workbench.action.closeActiveEditor' }, {
403
+ execute: () => commands.executeCommand(CommonCommands.CLOSE_MAIN_TAB.id)
404
+ });
405
+ commands.registerCommand({ id: 'workbench.action.closeOtherEditors' }, {
406
+ execute: async (uri?: monaco.Uri) => {
407
+ let editor = this.editorManager.currentEditor || this.shell.currentWidget;
408
+ if (uri) {
409
+ const uriString = uri.toString();
410
+ editor = this.editorManager.all.find(e => {
411
+ const resourceUri = e.getResourceUri();
412
+ return (resourceUri && resourceUri.toString()) === uriString;
413
+ });
414
+ }
415
+ const toClose = this.shell.widgets.filter(widget => widget !== editor && this.codeEditorWidgetUtil.is(widget));
416
+ await this.shell.closeMany(toClose);
417
+ }
418
+ });
419
+
420
+ const performActionOnGroup = (
421
+ cb: (
422
+ tabBarOrArea: TabBar<Widget> | ApplicationShell.Area,
423
+ filter?: ((title: Title<Widget>, index: number) => boolean) | undefined
424
+ ) => void,
425
+ uri?: monaco.Uri
426
+ ): void => {
427
+ let editor = this.editorManager.currentEditor || this.shell.currentWidget;
428
+ if (uri) {
429
+ const uriString = uri.toString();
430
+ editor = this.editorManager.all.find(e => {
431
+ const resourceUri = e.getResourceUri();
432
+ return (resourceUri && resourceUri.toString()) === uriString;
433
+ });
434
+ }
435
+ if (editor) {
436
+ const tabBar = this.shell.getTabBarFor(editor);
437
+ if (tabBar) {
438
+ cb(tabBar, ({ owner }) => this.codeEditorWidgetUtil.is(owner));
439
+ }
440
+ }
441
+ };
442
+
443
+ commands.registerCommand({
444
+ id: 'workbench.action.closeEditorsInGroup',
445
+ label: nls.localizeByDefault('Close All Editors in Group')
446
+ }, {
447
+ execute: (uri?: monaco.Uri) => performActionOnGroup(this.shell.closeTabs, uri)
448
+ });
449
+ commands.registerCommand({
450
+ id: 'workbench.files.saveAllInGroup',
451
+ label: nls.localizeByDefault('Save All in Group')
452
+ }, {
453
+ execute: (uri?: monaco.Uri) => performActionOnGroup(this.shell.saveTabs, uri)
454
+ });
455
+ commands.registerCommand({ id: 'workbench.action.closeEditorsInOtherGroups' }, {
456
+ execute: () => {
457
+ const editor = this.editorManager.currentEditor || this.shell.currentWidget;
458
+ if (editor) {
459
+ const editorTabBar = this.shell.getTabBarFor(editor);
460
+ for (const tabBar of this.shell.allTabBars) {
461
+ if (tabBar !== editorTabBar) {
462
+ this.shell.closeTabs(tabBar,
463
+ ({ owner }) => this.codeEditorWidgetUtil.is(owner)
464
+ );
465
+ }
466
+ }
467
+ }
468
+ }
469
+ });
470
+ commands.registerCommand({ id: 'workbench.action.closeEditorsToTheLeft' }, {
471
+ execute: () => {
472
+ const editor = this.editorManager.currentEditor || this.shell.currentWidget;
473
+ if (editor) {
474
+ const tabBar = this.shell.getTabBarFor(editor);
475
+ if (tabBar) {
476
+ let left = true;
477
+ this.shell.closeTabs(tabBar,
478
+ ({ owner }) => {
479
+ if (owner === editor) {
480
+ left = false;
481
+ return false;
482
+ }
483
+ return left && this.codeEditorWidgetUtil.is(owner);
484
+ }
485
+ );
486
+ }
487
+ }
488
+ }
489
+ });
490
+ commands.registerCommand({ id: 'workbench.action.closeEditorsToTheRight' }, {
491
+ execute: () => {
492
+ const editor = this.editorManager.currentEditor || this.shell.currentWidget;
493
+ if (editor) {
494
+ const tabBar = this.shell.getTabBarFor(editor);
495
+ if (tabBar) {
496
+ let left = true;
497
+ this.shell.closeTabs(tabBar,
498
+ ({ owner }) => {
499
+ if (owner === editor) {
500
+ left = false;
501
+ return false;
502
+ }
503
+ return !left && this.codeEditorWidgetUtil.is(owner);
504
+ }
505
+ );
506
+ }
507
+ }
508
+ }
509
+ });
510
+ commands.registerCommand({ id: 'workbench.action.closeAllEditors' }, {
511
+ execute: async () => {
512
+ const toClose = this.shell.widgets.filter(widget => this.codeEditorWidgetUtil.is(widget));
513
+ await this.shell.closeMany(toClose);
514
+ }
515
+ });
516
+ commands.registerCommand({ id: 'workbench.action.nextEditor' }, {
517
+ execute: () => this.shell.activateNextTab()
518
+ });
519
+ commands.registerCommand({ id: 'workbench.action.previousEditor' }, {
520
+ execute: () => this.shell.activatePreviousTab()
521
+ });
522
+ commands.registerCommand({ id: 'workbench.action.navigateBack' }, {
523
+ execute: () => commands.executeCommand(EditorCommands.GO_BACK.id)
524
+ });
525
+ commands.registerCommand({ id: 'workbench.action.navigateForward' }, {
526
+ execute: () => commands.executeCommand(EditorCommands.GO_FORWARD.id)
527
+ });
528
+ commands.registerCommand({ id: 'workbench.action.navigateToLastEditLocation' }, {
529
+ execute: () => commands.executeCommand(EditorCommands.GO_LAST_EDIT.id)
530
+ });
531
+
532
+ commands.registerCommand({ id: 'openInTerminal' }, {
533
+ execute: (resource: URI) => this.terminalContribution.openInTerminal(new TheiaURI(resource.toString()))
534
+ });
535
+
536
+ commands.registerCommand({ id: 'workbench.action.reloadWindow' }, {
537
+ execute: () => {
538
+ this.windowService.reload();
539
+ }
540
+ });
541
+
542
+ /**
543
+ * TODO:
544
+ * Open Next: workbench.action.openNextRecentlyUsedEditorInGroup
545
+ * Open Previous: workbench.action.openPreviousRecentlyUsedEditorInGroup
546
+ * Copy Path of Active File: workbench.action.files.copyPathOfActiveFile
547
+ * Reveal Active File in Windows: workbench.action.files.revealActiveFileInWindows
548
+ * Show Opened File in New Window: workbench.action.files.showOpenedFileInNewWindow
549
+ * Compare Opened File With: workbench.files.action.compareFileWith
550
+ */
551
+
552
+ // Register built-in language service commands
553
+ // see https://code.visualstudio.com/api/references/commands
554
+ /* eslint-disable @typescript-eslint/no-explicit-any */
555
+
556
+ // TODO register other `vscode.execute...` commands.
557
+ // see https://github.com/microsoft/vscode/blob/master/src/vs/workbench/api/common/extHostApiCommands.ts
558
+ commands.registerCommand(
559
+ {
560
+ id: 'vscode.executeDefinitionProvider'
561
+ },
562
+ {
563
+ execute: ((resource: URI, position: Position) =>
564
+ commands.executeCommand<Location[]>('_executeDefinitionProvider', monaco.Uri.from(resource), position))
565
+ }
566
+ );
567
+ commands.registerCommand(
568
+ {
569
+ id: 'vscode.executeDeclarationProvider'
570
+ },
571
+ {
572
+ execute: ((resource: URI, position: Position) =>
573
+ commands.executeCommand<Location[]>('_executeDeclarationProvider', monaco.Uri.from(resource), position))
574
+ }
575
+ );
576
+ commands.registerCommand(
577
+ {
578
+ id: 'vscode.executeTypeDefinitionProvider'
579
+ },
580
+ {
581
+ execute: ((resource: URI, position: Position) =>
582
+ commands.executeCommand<Location[]>('_executeTypeDefinitionProvider', monaco.Uri.from(resource), position))
583
+ }
584
+ );
585
+ commands.registerCommand(
586
+ {
587
+ id: 'vscode.executeImplementationProvider'
588
+ },
589
+ {
590
+ execute: ((resource: URI, position: Position) =>
591
+ commands.executeCommand<Location[]>('_executeImplementationProvider', monaco.Uri.from(resource), position))
592
+ }
593
+ );
594
+ commands.registerCommand(
595
+ {
596
+ id: 'vscode.executeHoverProvider'
597
+ },
598
+ {
599
+ execute: ((resource: URI, position: Position) =>
600
+ commands.executeCommand<Hover[]>('_executeHoverProvider', monaco.Uri.from(resource), position))
601
+ }
602
+ );
603
+ commands.registerCommand(
604
+ {
605
+ id: 'vscode.executeDocumentHighlights'
606
+ },
607
+ {
608
+ execute: ((resource: URI, position: Position) =>
609
+ commands.executeCommand<DocumentHighlight[]>('_executeDocumentHighlights', monaco.Uri.from(resource), position))
610
+ }
611
+ );
612
+ commands.registerCommand(
613
+ {
614
+ id: 'vscode.executeReferenceProvider'
615
+ },
616
+ {
617
+ execute: ((resource: URI, position: Position) => commands.executeCommand<Location[]>('_executeReferenceProvider', monaco.Uri.from(resource), position))
618
+ }
619
+ );
620
+ commands.registerCommand(
621
+ {
622
+ id: 'vscode.executeDocumentSymbolProvider'
623
+ },
624
+ {
625
+ execute: (resource: URI) => commands.executeCommand('_executeDocumentSymbolProvider',
626
+ monaco.Uri.parse(resource.toString())
627
+ ).then((value: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
628
+ if (!Array.isArray(value) || value === undefined) {
629
+ return undefined;
630
+ }
631
+ return value.map(loc => toMergedSymbol(resource, loc));
632
+ })
633
+ }
634
+ );
635
+ commands.registerCommand(
636
+ {
637
+ id: 'vscode.executeFormatDocumentProvider'
638
+ },
639
+ {
640
+ execute: ((resource: URI, options: FormattingOptions) =>
641
+ commands.executeCommand<TextEdit[]>('_executeFormatDocumentProvider', monaco.Uri.from(resource), options))
642
+ }
643
+ );
644
+ commands.registerCommand(
645
+ {
646
+ id: 'vscode.executeFormatRangeProvider'
647
+ },
648
+ {
649
+ execute: ((resource: URI, range: Range, options: FormattingOptions) =>
650
+ commands.executeCommand<TextEdit[]>('_executeFormatRangeProvider', monaco.Uri.from(resource), range, options))
651
+ }
652
+ );
653
+ commands.registerCommand(
654
+ {
655
+ id: 'vscode.executeFormatOnTypeProvider'
656
+ },
657
+ {
658
+ execute: ((resource: URI, position: Position, ch: string, options: FormattingOptions) =>
659
+ commands.executeCommand<TextEdit[]>('_executeFormatOnTypeProvider', monaco.Uri.from(resource), position, ch, options))
660
+ }
661
+ );
662
+ commands.registerCommand(
663
+ {
664
+ id: 'vscode.executeFoldingRangeProvider'
665
+ },
666
+ {
667
+ execute: ((resource: URI, position: Position) =>
668
+ commands.executeCommand<TextEdit[]>('_executeFoldingRangeProvider', monaco.Uri.from(resource), position))
669
+ }
670
+ );
671
+ commands.registerCommand(
672
+ {
673
+ id: 'vscode.executeCodeActionProvider'
674
+ },
675
+ {
676
+ execute: ((resource: URI, range: Range, kind?: string, itemResolveCount?: number) =>
677
+ commands.executeCommand<TextEdit[]>('_executeCodeActionProvider', monaco.Uri.from(resource), range, kind, itemResolveCount))
678
+ }
679
+ );
680
+ commands.registerCommand(
681
+ {
682
+ id: 'vscode.executeWorkspaceSymbolProvider'
683
+ },
684
+ {
685
+ execute: async (queryString: string) =>
686
+ (await Promise.all(
687
+ this.monacoLanguages.workspaceSymbolProviders
688
+ .map(async provider => provider.provideWorkspaceSymbols({ query: queryString }, new CancellationTokenSource().token))))
689
+ .flatMap(symbols => symbols)
690
+ .filter(symbols => !!symbols)
691
+ }
692
+ );
693
+
694
+ commands.registerCommand(
695
+ {
696
+ id: 'vscode.prepareCallHierarchy'
697
+ },
698
+ {
699
+ execute: async (resource: URI, position: Position): Promise<CallHierarchyItem[]> => {
700
+ const provider = await this.getCallHierarchyServiceForUri(resource);
701
+ const definition = await provider?.getRootDefinition(
702
+ resource.path,
703
+ toPosition(position),
704
+ new CancellationTokenSource().token
705
+ );
706
+ if (definition) {
707
+ return definition.items.map(item => fromItemHierarchyDefinition(item));
708
+ };
709
+ return [];
710
+ }
711
+ }
712
+ );
713
+ commands.registerCommand(
714
+ {
715
+ id: 'vscode.provideIncomingCalls'
716
+ },
717
+ {
718
+ execute: async (item: CallHierarchyItem): Promise<CallHierarchyIncomingCall[]> => {
719
+ const resource = URI.from(item.uri);
720
+ const provider = await this.getCallHierarchyServiceForUri(resource);
721
+ const incomingCalls = await provider?.getCallers(
722
+ toItemHierarchyDefinition(item),
723
+ new CancellationTokenSource().token,
724
+ );
725
+ if (incomingCalls) {
726
+ return incomingCalls.map(fromCallHierarchyCallerToModelCallHierarchyIncomingCall);
727
+ }
728
+ return [];
729
+ },
730
+ },
731
+ );
732
+ commands.registerCommand(
733
+ {
734
+ id: 'vscode.provideOutgoingCalls'
735
+ },
736
+ {
737
+ execute: async (item: CallHierarchyItem): Promise<CallHierarchyOutgoingCall[]> => {
738
+ const resource = URI.from(item.uri);
739
+ const provider = await this.getCallHierarchyServiceForUri(resource);
740
+ const outgoingCalls = await provider?.getCallees?.(
741
+ toItemHierarchyDefinition(item),
742
+ new CancellationTokenSource().token,
743
+ );
744
+ if (outgoingCalls) {
745
+ return outgoingCalls.map(fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall);
746
+ }
747
+ return [];
748
+ }
749
+ }
750
+ );
751
+ commands.registerCommand(
752
+ {
753
+ id: 'vscode.prepareTypeHierarchy'
754
+ },
755
+ {
756
+ execute: async (resource: URI, position: Position): Promise<TypeHierarchyItem[]> => {
757
+ const provider = await this.getTypeHierarchyServiceForUri(resource);
758
+ const session = await provider?.prepareSession(
759
+ resource.path,
760
+ toPosition(position),
761
+ new CancellationTokenSource().token
762
+ );
763
+ return session ? session.items.map(item => fromItemHierarchyDefinition(item)) : [];
764
+ }
765
+ }
766
+ );
767
+ commands.registerCommand(
768
+ {
769
+ id: 'vscode.provideSupertypes'
770
+ },
771
+ {
772
+ execute: async (item: TypeHierarchyItem): Promise<TypeHierarchyItem[]> => {
773
+ if (!item._sessionId || !item._itemId) {
774
+ return [];
775
+ }
776
+ const resource = URI.from(item.uri);
777
+ const provider = await this.getTypeHierarchyServiceForUri(resource);
778
+ const items = await provider?.provideSuperTypes(
779
+ item._sessionId,
780
+ item._itemId,
781
+ new CancellationTokenSource().token
782
+ );
783
+ return (items ? items : []).map(typeItem => fromItemHierarchyDefinition(typeItem));
784
+ }
785
+ }
786
+ );
787
+ commands.registerCommand(
788
+ {
789
+ id: 'vscode.provideSubtypes'
790
+ },
791
+ {
792
+ execute: async (item: TypeHierarchyItem): Promise<TypeHierarchyItem[]> => {
793
+ if (!item._sessionId || !item._itemId) {
794
+ return [];
795
+ }
796
+ const resource = URI.from(item.uri);
797
+ const provider = await this.getTypeHierarchyServiceForUri(resource);
798
+ const items = await provider?.provideSubTypes(
799
+ item._sessionId, item._itemId,
800
+ new CancellationTokenSource().token
801
+ );
802
+ return (items ? items : []).map(typeItem => fromItemHierarchyDefinition(typeItem));
803
+
804
+ }
805
+ }
806
+ );
807
+
808
+ commands.registerCommand({
809
+ id: 'workbench.action.openRecent'
810
+ }, {
811
+ execute: () => this.quickOpenWorkspace.select()
812
+ });
813
+ commands.registerCommand({
814
+ id: 'explorer.newFolder'
815
+ }, {
816
+ execute: () => commands.executeCommand(WorkspaceCommands.NEW_FOLDER.id)
817
+ });
818
+ commands.registerCommand({
819
+ id: 'workbench.action.terminal.sendSequence'
820
+ }, {
821
+ execute: (args?: { text?: string }) => {
822
+ if (args === undefined || args.text === undefined) {
823
+ return;
824
+ }
825
+
826
+ const currentTerminal = this.terminalService.currentTerminal;
827
+
828
+ if (currentTerminal === undefined) {
829
+ return;
830
+ }
831
+
832
+ currentTerminal.sendText(args.text);
833
+ }
834
+ });
835
+ commands.registerCommand({
836
+ id: 'workbench.action.terminal.kill'
837
+ }, {
838
+ execute: () => {
839
+ const currentTerminal = this.terminalService.currentTerminal;
840
+
841
+ if (currentTerminal === undefined) {
842
+ return;
843
+ }
844
+
845
+ currentTerminal.dispose();
846
+ }
847
+ });
848
+ commands.registerCommand({
849
+ id: 'workbench.view.explorer'
850
+ }, {
851
+ execute: () => commands.executeCommand(FileNavigatorCommands.FOCUS.id)
852
+ });
853
+ commands.registerCommand({
854
+ id: 'copyFilePath'
855
+ }, {
856
+ execute: () => commands.executeCommand(CommonCommands.COPY_PATH.id)
857
+ });
858
+ commands.registerCommand({
859
+ id: 'copyRelativeFilePath'
860
+ }, {
861
+ execute: () => commands.executeCommand(WorkspaceCommands.COPY_RELATIVE_FILE_PATH.id)
862
+ });
863
+ commands.registerCommand({
864
+ id: 'revealInExplorer'
865
+ }, {
866
+ execute: async (resource: URI | object) => {
867
+ if (!URI.isUri(resource)) {
868
+ return;
869
+ }
870
+ let navigator = await this.shell.revealWidget(FILE_NAVIGATOR_ID);
871
+ if (!navigator) {
872
+ await this.commandService.executeCommand(FILE_NAVIGATOR_TOGGLE_COMMAND_ID);
873
+ navigator = await this.shell.revealWidget(FILE_NAVIGATOR_ID);
874
+ }
875
+ if (navigator instanceof FileNavigatorWidget) {
876
+ const model = navigator.model;
877
+ const node = await model.revealFile(new TheiaURI(resource));
878
+ if (SelectableTreeNode.is(node)) {
879
+ model.selectNode(node);
880
+ }
881
+ }
882
+ }
883
+ });
884
+
885
+ commands.registerCommand({
886
+ id: 'workbench.experimental.requestUsbDevice'
887
+ }, {
888
+ execute: async (options?: { filters?: unknown[] }): Promise<UsbDeviceData | undefined> => {
889
+ const usb = (navigator as any).usb;
890
+ if (!usb) {
891
+ return undefined;
892
+ }
893
+
894
+ const device = await usb.requestDevice({ filters: options?.filters ?? [] });
895
+ if (!device) {
896
+ return undefined;
897
+ }
898
+
899
+ return {
900
+ deviceClass: device.deviceClass,
901
+ deviceProtocol: device.deviceProtocol,
902
+ deviceSubclass: device.deviceSubclass,
903
+ deviceVersionMajor: device.deviceVersionMajor,
904
+ deviceVersionMinor: device.deviceVersionMinor,
905
+ deviceVersionSubminor: device.deviceVersionSubminor,
906
+ manufacturerName: device.manufacturerName,
907
+ productId: device.productId,
908
+ productName: device.productName,
909
+ serialNumber: device.serialNumber,
910
+ usbVersionMajor: device.usbVersionMajor,
911
+ usbVersionMinor: device.usbVersionMinor,
912
+ usbVersionSubminor: device.usbVersionSubminor,
913
+ vendorId: device.vendorId,
914
+ };
915
+ }
916
+ });
917
+
918
+ commands.registerCommand({
919
+ id: 'workbench.experimental.requestSerialPort'
920
+ }, {
921
+ execute: async (options?: { filters?: unknown[] }): Promise<SerialPortData | undefined> => {
922
+ const serial = (navigator as any).serial;
923
+ if (!serial) {
924
+ return undefined;
925
+ }
926
+
927
+ const port = await serial.requestPort({ filters: options?.filters ?? [] });
928
+ if (!port) {
929
+ return undefined;
930
+ }
931
+
932
+ const info = port.getInfo();
933
+ return {
934
+ usbVendorId: info.usbVendorId,
935
+ usbProductId: info.usbProductId
936
+ };
937
+ }
938
+ });
939
+
940
+ commands.registerCommand({
941
+ id: 'workbench.experimental.requestHidDevice'
942
+ }, {
943
+ execute: async (options?: { filters?: unknown[] }): Promise<HidDeviceData | undefined> => {
944
+ const hid = (navigator as any).hid;
945
+ if (!hid) {
946
+ return undefined;
947
+ }
948
+
949
+ const devices = await hid.requestDevice({ filters: options?.filters ?? [] });
950
+ if (!devices.length) {
951
+ return undefined;
952
+ }
953
+
954
+ const device = devices[0];
955
+ return {
956
+ opened: device.opened,
957
+ vendorId: device.vendorId,
958
+ productId: device.productId,
959
+ productName: device.productName,
960
+ collections: device.collections
961
+ };
962
+ }
963
+ });
964
+
965
+ // required by Jupyter for the show table of contents action
966
+ commands.registerCommand({ id: 'outline.focus' }, {
967
+ execute: () => this.outlineViewContribution.openView({ activate: true })
968
+ });
969
+ }
970
+
971
+ private async resolveLanguageId(resource: URI): Promise<string> {
972
+ const reference = await this.textModelService.createModelReference(resource);
973
+ const languageId = reference.object.languageId;
974
+ reference.dispose();
975
+ return languageId;
976
+ }
977
+
978
+ protected async getCallHierarchyServiceForUri(resource: URI): Promise<CallHierarchyService | undefined> {
979
+ const languageId = await this.resolveLanguageId(resource);
980
+ return this.callHierarchyProvider.get(languageId, new TheiaURI(resource));
981
+ }
982
+
983
+ protected async getTypeHierarchyServiceForUri(resource: URI): Promise<TypeHierarchyService | undefined> {
984
+ const languageId = await this.resolveLanguageId(resource);
985
+ return this.typeHierarchyProvider.get(languageId, new TheiaURI(resource));
986
+ }
987
+ }