Path: blob/main/extensions/copilot/src/extension/onboardDebug/node/commandToConfigConverter.tsx
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes';6import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';7import { IExtensionsService } from '../../../platform/extensions/common/extensionsService';8import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';9import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';10import { createServiceIdentifier } from '../../../util/common/services';11import { CancellationToken } from '../../../util/vs/base/common/cancellation';12import { isAbsolute, join, relative } from '../../../util/vs/base/common/path';13import { count } from '../../../util/vs/base/common/strings';14import { URI } from '../../../util/vs/base/common/uri';15import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';16import { PromptRenderer } from '../../prompts/node/base/promptRenderer';17import { StartDebuggingPrompt, StartDebuggingType } from '../../prompts/node/panel/startDebugging';18import { IStartDebuggingParsedResponse, parseLaunchConfigFromResponse } from './parseLaunchConfigFromResponse';1920export interface IDebugConfigResult {21ok: boolean;22workspaceFolder: URI | undefined;23config: IStartDebuggingParsedResponse | undefined;24text: string;25}2627export interface IDebugCommandToConfigConverter {28readonly _serviceBrand: undefined;29convert(cwd: string, args: readonly string[], token: CancellationToken): Promise<IDebugConfigResult>;30}3132export const IDebugCommandToConfigConverter = createServiceIdentifier<IDebugCommandToConfigConverter>('IDebugCommandToConfigConverter');3334export class DebugCommandToConfigConverter implements IDebugCommandToConfigConverter {35declare readonly _serviceBrand: undefined;3637constructor(38@IEndpointProvider private readonly endpointProvider: IEndpointProvider,39@IInstantiationService private readonly instantiationService: IInstantiationService,40@IWorkspaceService private readonly workspace: IWorkspaceService,41@ITelemetryService private readonly telemetry: ITelemetryService,42@IExtensionsService private readonly extensionsService: IExtensionsService,43) {44}4546/**47* Converts a command run in the given working directory to a VS Code48* launch config.49*/50public async convert(cwd: string, args: readonly string[], token: CancellationToken): Promise<IDebugConfigResult> {51const relCwd = getPathRelativeToWorkspaceFolder(cwd, this.workspace);5253const endpoint = await this.endpointProvider.getChatEndpoint('copilot-base');54const promptRenderer = PromptRenderer.create(55this.instantiationService,56endpoint,57StartDebuggingPrompt,58{59input: {60type: StartDebuggingType.CommandLine,61relativeCwd: relCwd?.path,62absoluteCwd: cwd,63args,64},65history: [],66}67);6869const prompt = await promptRenderer.render(undefined, token);70const fetchResult = await endpoint.makeChatRequest(71'debugCommandToConfig',72prompt.messages,73undefined,74token,75ChatLocation.Other,76);7778if (fetchResult.type !== ChatFetchResponseType.Success) {79return { ok: false, config: undefined, text: fetchResult.reason, workspaceFolder: relCwd?.folder };80}8182const config = parseLaunchConfigFromResponse(fetchResult.value, this.extensionsService);8384/* __GDPR__85"onboardDebug.configGenerated" : {86"owner": "connor4312",87"comment": "Reports usages of the copilot-debug command",88"configGenerated": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether a config was generated", "isMeasurement": true },89"configType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "command": "launch.json config type generated, if any" }90}91*/92this.telemetry.sendMSFTTelemetryEvent('onboardDebug.configGenerated', {93configType: config?.configurations[0].type,94}, {95ok: config ? 1 : 0,96});9798return {99ok: true,100config,101text: fetchResult.value,102workspaceFolder: relCwd?.folder103};104}105}106107export function getPathRelativeToWorkspaceFolder(path: string, workspace: IWorkspaceService) {108let closest: { rel: string; distance: number; folder: URI } | undefined;109110for (const folder of workspace.getWorkspaceFolders()) {111const rel = relative(folder.fsPath, path);112const distance = isAbsolute(rel) ? Infinity : count(rel, '..');113if (!closest || distance < closest.distance || (distance === closest.distance && rel.length < closest.rel.length)) {114closest = { rel: join('${workspaceFolder}', rel).replaceAll('\\', '/'), distance, folder };115}116}117118return closest && { folder: closest.folder, path: closest.rel };119}120121122