Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/contrib/chat/electron-browser/openInVSCode.contribution.ts
13401 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 { Codicon } from '../../../../base/common/codicons.js';
7
import { Schemas } from '../../../../base/common/network.js';
8
import { URI } from '../../../../base/common/uri.js';
9
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
10
import { localize2 } from '../../../../nls.js';
11
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
12
import { AGENT_HOST_SCHEME, fromAgentHostUri } from '../../../../platform/agentHost/common/agentHostUri.js';
13
import { IRemoteAgentHostService } from '../../../../platform/agentHost/common/remoteAgentHostService.js';
14
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
15
import { INativeHostService } from '../../../../platform/native/common/native.js';
16
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
17
import { IProductService } from '../../../../platform/product/common/productService.js';
18
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
19
import { IsAuxiliaryWindowContext } from '../../../../workbench/common/contextkeys.js';
20
import { IsPhoneLayoutContext, SessionsWelcomeVisibleContext } from '../../../common/contextkeys.js';
21
import { logSessionsInteraction } from '../../../common/sessionsTelemetry.js';
22
import { Menus } from '../../../browser/menus.js';
23
import { isWorkspaceAgentSessionType } from '../../../services/sessions/common/session.js';
24
import { ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js';
25
import { ISessionsProvidersService } from '../../../services/sessions/browser/sessionsProvidersService.js';
26
import { resolveRemoteAuthority } from '../browser/openInVSCodeUtils.js';
27
import { DebugAgentHostInDevToolsAction } from '../../../../workbench/contrib/chat/electron-browser/actions/debugAgentHostAction.js';
28
import { isLinux } from '../../../../base/common/platform.js';
29
import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
30
31
/**
32
* Desktop version of the "Open in VS Code" action.
33
*
34
* In built builds with a sibling app configured, launches the host VS Code app
35
* via {@link INativeHostService.launchSiblingApp} (child_process.spawn) with
36
* direct CLI arguments, bypassing protocol handlers and their OS security
37
* prompts. In dev builds (no sibling app), falls back to the protocol handler
38
* approach via {@link IOpenerService}.
39
*/
40
registerAction2(class OpenSessionWorktreeInVSCodeAction extends Action2 {
41
static readonly ID = 'chat.openSessionWorktreeInVSCode';
42
43
constructor() {
44
super({
45
id: OpenSessionWorktreeInVSCodeAction.ID,
46
title: localize2('openInVSCode', 'Open in VS Code'),
47
icon: Codicon.vscodeInsiders,
48
precondition: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated()),
49
menu: [{
50
id: Menus.TitleBarSessionMenu,
51
group: 'navigation',
52
order: 7,
53
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated(), IsPhoneLayoutContext.negate()),
54
}]
55
});
56
}
57
58
override async run(accessor: ServicesAccessor): Promise<void> {
59
const telemetryService = accessor.get(ITelemetryService);
60
logSessionsInteraction(telemetryService, 'openInVSCode');
61
62
const productService = accessor.get(IProductService);
63
const environmentService = accessor.get(IEnvironmentService);
64
const sessionsManagementService = accessor.get(ISessionsManagementService);
65
const sessionsProvidersService = accessor.get(ISessionsProvidersService);
66
const remoteAgentHostService = accessor.get(IRemoteAgentHostService);
67
68
const activeSession = sessionsManagementService.activeSession.get();
69
const workspace = activeSession?.workspace.get();
70
const repo = workspace?.repositories[0];
71
const rawFolderUri = isWorkspaceAgentSessionType(activeSession?.sessionType) ? repo?.workingDirectory ?? repo?.uri : undefined;
72
const folderUri = rawFolderUri?.scheme === AGENT_HOST_SCHEME ? fromAgentHostUri(rawFolderUri) : rawFolderUri;
73
const remoteAuthority = activeSession
74
? resolveRemoteAuthority(activeSession.providerId, sessionsProvidersService, remoteAgentHostService)
75
: undefined;
76
77
if (environmentService.isBuilt && !isLinux) {
78
await this.launchViaSiblingApp(accessor, activeSession, folderUri, remoteAuthority);
79
} else {
80
await this.launchViaProtocolHandler(accessor, productService, activeSession, folderUri, remoteAuthority);
81
}
82
}
83
84
private async launchViaSiblingApp(
85
accessor: ServicesAccessor,
86
activeSession: ReturnType<ISessionsManagementService['activeSession']['get']>,
87
folderUri: URI | undefined,
88
remoteAuthority: string | undefined,
89
): Promise<void> {
90
const nativeHostService = accessor.get(INativeHostService);
91
92
const args: string[] = ['--new-window'];
93
94
if (folderUri) {
95
if (remoteAuthority) {
96
args.push('--folder-uri', URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: folderUri.path }).toString());
97
} else {
98
args.push('--folder-uri', folderUri.toString());
99
}
100
}
101
102
if (activeSession) {
103
args.push('--open-chat-session', activeSession.resource.toString());
104
}
105
106
await nativeHostService.launchSiblingApp(args);
107
}
108
109
private async launchViaProtocolHandler(
110
accessor: ServicesAccessor,
111
productService: IProductService,
112
activeSession: ReturnType<ISessionsManagementService['activeSession']['get']>,
113
folderUri: URI | undefined,
114
remoteAuthority: string | undefined,
115
): Promise<void> {
116
const openerService = accessor.get(IOpenerService);
117
118
const scheme = productService.quality === 'stable'
119
? 'vscode'
120
: productService.quality === 'exploration'
121
? 'vscode-exploration'
122
: productService.quality === 'insider'
123
? 'vscode-insiders'
124
: productService.urlProtocol;
125
126
const params = new URLSearchParams();
127
params.set('windowId', '_blank');
128
129
if (!activeSession || !folderUri) {
130
await openerService.open(URI.from({ scheme, query: params.toString() }), { openExternal: true });
131
return;
132
}
133
134
params.set('session', activeSession.resource.toString());
135
136
if (remoteAuthority) {
137
await openerService.open(URI.from({
138
scheme,
139
authority: Schemas.vscodeRemote,
140
path: `/${remoteAuthority}${folderUri.path}`,
141
query: params.toString(),
142
}), { openExternal: true });
143
} else {
144
await openerService.open(URI.from({
145
scheme,
146
authority: Schemas.file,
147
path: folderUri.path,
148
query: params.toString(),
149
}), { openExternal: true });
150
}
151
}
152
});
153
154
registerAction2(DebugAgentHostInDevToolsAction);
155
156