Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/onboardDebug/node/commandToConfigConverter.tsx
13399 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 { ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes';
7
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
8
import { IExtensionsService } from '../../../platform/extensions/common/extensionsService';
9
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';
10
import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
11
import { createServiceIdentifier } from '../../../util/common/services';
12
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
13
import { isAbsolute, join, relative } from '../../../util/vs/base/common/path';
14
import { count } from '../../../util/vs/base/common/strings';
15
import { URI } from '../../../util/vs/base/common/uri';
16
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
17
import { PromptRenderer } from '../../prompts/node/base/promptRenderer';
18
import { StartDebuggingPrompt, StartDebuggingType } from '../../prompts/node/panel/startDebugging';
19
import { IStartDebuggingParsedResponse, parseLaunchConfigFromResponse } from './parseLaunchConfigFromResponse';
20
21
export interface IDebugConfigResult {
22
ok: boolean;
23
workspaceFolder: URI | undefined;
24
config: IStartDebuggingParsedResponse | undefined;
25
text: string;
26
}
27
28
export interface IDebugCommandToConfigConverter {
29
readonly _serviceBrand: undefined;
30
convert(cwd: string, args: readonly string[], token: CancellationToken): Promise<IDebugConfigResult>;
31
}
32
33
export const IDebugCommandToConfigConverter = createServiceIdentifier<IDebugCommandToConfigConverter>('IDebugCommandToConfigConverter');
34
35
export class DebugCommandToConfigConverter implements IDebugCommandToConfigConverter {
36
declare readonly _serviceBrand: undefined;
37
38
constructor(
39
@IEndpointProvider private readonly endpointProvider: IEndpointProvider,
40
@IInstantiationService private readonly instantiationService: IInstantiationService,
41
@IWorkspaceService private readonly workspace: IWorkspaceService,
42
@ITelemetryService private readonly telemetry: ITelemetryService,
43
@IExtensionsService private readonly extensionsService: IExtensionsService,
44
) {
45
}
46
47
/**
48
* Converts a command run in the given working directory to a VS Code
49
* launch config.
50
*/
51
public async convert(cwd: string, args: readonly string[], token: CancellationToken): Promise<IDebugConfigResult> {
52
const relCwd = getPathRelativeToWorkspaceFolder(cwd, this.workspace);
53
54
const endpoint = await this.endpointProvider.getChatEndpoint('copilot-base');
55
const promptRenderer = PromptRenderer.create(
56
this.instantiationService,
57
endpoint,
58
StartDebuggingPrompt,
59
{
60
input: {
61
type: StartDebuggingType.CommandLine,
62
relativeCwd: relCwd?.path,
63
absoluteCwd: cwd,
64
args,
65
},
66
history: [],
67
}
68
);
69
70
const prompt = await promptRenderer.render(undefined, token);
71
const fetchResult = await endpoint.makeChatRequest(
72
'debugCommandToConfig',
73
prompt.messages,
74
undefined,
75
token,
76
ChatLocation.Other,
77
);
78
79
if (fetchResult.type !== ChatFetchResponseType.Success) {
80
return { ok: false, config: undefined, text: fetchResult.reason, workspaceFolder: relCwd?.folder };
81
}
82
83
const config = parseLaunchConfigFromResponse(fetchResult.value, this.extensionsService);
84
85
/* __GDPR__
86
"onboardDebug.configGenerated" : {
87
"owner": "connor4312",
88
"comment": "Reports usages of the copilot-debug command",
89
"configGenerated": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether a config was generated", "isMeasurement": true },
90
"configType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "command": "launch.json config type generated, if any" }
91
}
92
*/
93
this.telemetry.sendMSFTTelemetryEvent('onboardDebug.configGenerated', {
94
configType: config?.configurations[0].type,
95
}, {
96
ok: config ? 1 : 0,
97
});
98
99
return {
100
ok: true,
101
config,
102
text: fetchResult.value,
103
workspaceFolder: relCwd?.folder
104
};
105
}
106
}
107
108
export function getPathRelativeToWorkspaceFolder(path: string, workspace: IWorkspaceService) {
109
let closest: { rel: string; distance: number; folder: URI } | undefined;
110
111
for (const folder of workspace.getWorkspaceFolders()) {
112
const rel = relative(folder.fsPath, path);
113
const distance = isAbsolute(rel) ? Infinity : count(rel, '..');
114
if (!closest || distance < closest.distance || (distance === closest.distance && rel.length < closest.rel.length)) {
115
closest = { rel: join('${workspaceFolder}', rel).replaceAll('\\', '/'), distance, folder };
116
}
117
}
118
119
return closest && { folder: closest.folder, path: closest.rel };
120
}
121
122