Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.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 * as nls from '../../../../nls.js';
7
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
8
import { URI } from '../../../../base/common/uri.js';
9
import { MenuId, MenuRegistry, IMenuItem } from '../../../../platform/actions/common/actions.js';
10
import { ITerminalGroupService, ITerminalService as IIntegratedTerminalService } from '../../terminal/browser/terminal.js';
11
import { ResourceContextKey } from '../../../common/contextkeys.js';
12
import { IFileService } from '../../../../platform/files/common/files.js';
13
import { getMultiSelectedResources, IExplorerService } from '../../files/browser/files.js';
14
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
15
import { Schemas } from '../../../../base/common/network.js';
16
import { distinct } from '../../../../base/common/arrays.js';
17
import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';
18
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
19
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js';
20
import { Disposable } from '../../../../base/common/lifecycle.js';
21
import { isWindows } from '../../../../base/common/platform.js';
22
import { dirname, basename } from '../../../../base/common/path.js';
23
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
24
import { Registry } from '../../../../platform/registry/common/platform.js';
25
import { IExternalTerminalConfiguration, IExternalTerminalService } from '../../../../platform/externalTerminal/common/externalTerminal.js';
26
import { TerminalLocation } from '../../../../platform/terminal/common/terminal.js';
27
import { IListService } from '../../../../platform/list/browser/listService.js';
28
import { IEditorService } from '../../../services/editor/common/editorService.js';
29
import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';
30
31
const OPEN_IN_TERMINAL_COMMAND_ID = 'openInTerminal';
32
const OPEN_IN_INTEGRATED_TERMINAL_COMMAND_ID = 'openInIntegratedTerminal';
33
34
function registerOpenTerminalCommand(id: string, explorerKind: 'integrated' | 'external') {
35
CommandsRegistry.registerCommand({
36
id: id,
37
handler: async (accessor, resource: URI) => {
38
39
const configurationService = accessor.get(IConfigurationService);
40
const fileService = accessor.get(IFileService);
41
const integratedTerminalService = accessor.get(IIntegratedTerminalService);
42
const remoteAgentService = accessor.get(IRemoteAgentService);
43
const terminalGroupService = accessor.get(ITerminalGroupService);
44
let externalTerminalService: IExternalTerminalService | undefined = undefined;
45
try {
46
externalTerminalService = accessor.get(IExternalTerminalService);
47
} catch { }
48
49
const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService), accessor.get(IEditorGroupsService), accessor.get(IExplorerService));
50
return fileService.resolveAll(resources.map(r => ({ resource: r }))).then(async stats => {
51
// Always use integrated terminal when using a remote
52
const config = configurationService.getValue<IExternalTerminalConfiguration>();
53
54
const useIntegratedTerminal = remoteAgentService.getConnection() || explorerKind === 'integrated';
55
const targets = distinct(stats.filter(data => data.success));
56
if (useIntegratedTerminal) {
57
// TODO: Use uri for cwd in createterminal
58
const opened: { [path: string]: boolean } = {};
59
const cwds = targets.map(({ stat }) => {
60
const resource = stat!.resource;
61
if (stat!.isDirectory) {
62
return resource;
63
}
64
return URI.from({
65
scheme: resource.scheme,
66
authority: resource.authority,
67
fragment: resource.fragment,
68
query: resource.query,
69
path: dirname(resource.path)
70
});
71
});
72
for (const cwd of cwds) {
73
if (opened[cwd.path]) {
74
return;
75
}
76
opened[cwd.path] = true;
77
const instance = await integratedTerminalService.createTerminal({ config: { cwd } });
78
if (instance && instance.target !== TerminalLocation.Editor && (resources.length === 1 || !resource || cwd.path === resource.path || cwd.path === dirname(resource.path))) {
79
integratedTerminalService.setActiveInstance(instance);
80
terminalGroupService.showPanel(true);
81
}
82
}
83
} else if (externalTerminalService) {
84
distinct(targets.map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : dirname(stat!.resource.fsPath))).forEach(cwd => {
85
externalTerminalService.openTerminal(config.terminal.external, cwd);
86
});
87
}
88
});
89
}
90
});
91
}
92
93
registerOpenTerminalCommand(OPEN_IN_TERMINAL_COMMAND_ID, 'external');
94
registerOpenTerminalCommand(OPEN_IN_INTEGRATED_TERMINAL_COMMAND_ID, 'integrated');
95
96
export class ExternalTerminalContribution extends Disposable implements IWorkbenchContribution {
97
private _openInIntegratedTerminalMenuItem: IMenuItem;
98
private _openInTerminalMenuItem: IMenuItem;
99
100
constructor(
101
@IConfigurationService private readonly _configurationService: IConfigurationService
102
) {
103
super();
104
105
const shouldShowIntegratedOnLocal = ContextKeyExpr.and(
106
ResourceContextKey.Scheme.isEqualTo(Schemas.file),
107
ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.explorerKind', 'integrated'), ContextKeyExpr.equals('config.terminal.explorerKind', 'both')));
108
109
110
const shouldShowExternalKindOnLocal = ContextKeyExpr.and(
111
ResourceContextKey.Scheme.isEqualTo(Schemas.file),
112
ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.explorerKind', 'external'), ContextKeyExpr.equals('config.terminal.explorerKind', 'both')));
113
114
this._openInIntegratedTerminalMenuItem = {
115
group: 'navigation',
116
order: 30,
117
command: {
118
id: OPEN_IN_INTEGRATED_TERMINAL_COMMAND_ID,
119
title: nls.localize('scopedConsoleAction.Integrated', "Open in Integrated Terminal")
120
},
121
when: ContextKeyExpr.or(shouldShowIntegratedOnLocal, ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote))
122
};
123
124
125
this._openInTerminalMenuItem = {
126
group: 'navigation',
127
order: 31,
128
command: {
129
id: OPEN_IN_TERMINAL_COMMAND_ID,
130
title: nls.localize('scopedConsoleAction.external', "Open in External Terminal")
131
},
132
when: shouldShowExternalKindOnLocal
133
};
134
135
136
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, this._openInTerminalMenuItem);
137
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, this._openInIntegratedTerminalMenuItem);
138
139
this._register(this._configurationService.onDidChangeConfiguration(e => {
140
if (e.affectsConfiguration('terminal.explorerKind') || e.affectsConfiguration('terminal.external')) {
141
this._refreshOpenInTerminalMenuItemTitle();
142
}
143
}));
144
145
this._refreshOpenInTerminalMenuItemTitle();
146
}
147
148
private isWindows(): boolean {
149
const config = this._configurationService.getValue<IExternalTerminalConfiguration>().terminal;
150
if (isWindows && config.external?.windowsExec) {
151
const file = basename(config.external.windowsExec);
152
if (file === 'wt' || file === 'wt.exe') {
153
return true;
154
}
155
}
156
return false;
157
}
158
159
private _refreshOpenInTerminalMenuItemTitle(): void {
160
if (this.isWindows()) {
161
this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.wt', "Open in Windows Terminal");
162
}
163
}
164
}
165
166
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExternalTerminalContribution, LifecyclePhase.Restored);
167
168