Path: blob/main/extensions/copilot/src/extension/onboardDebug/node/parseLaunchConfigFromResponse.ts
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 * as jsonc from 'jsonc-parser';6import type { DebugConfiguration } from 'vscode';7import { IExtensionsService } from '../../../platform/extensions/common/extensionsService';8import { IDebugConfigSchema, IPackageJson } from '../../../platform/extensions/common/packageJson';9import { extractCodeBlocks } from '../../../util/common/markdown';10import { ILaunchJSON, ITasksJSON } from '../common/launchConfigService';1112export type IStartDebuggingParsedResponse = ILaunchJSON & Partial<ITasksJSON>;1314/**15* Parses a launch configuration from the response or any code blocks it contains.16* Always returns a well-structured {@link ILaunchJSON}.17*/18export const parseLaunchConfigFromResponse = (response: string, extensionsService: IExtensionsService): IStartDebuggingParsedResponse | undefined => {1920const codeBlocks = extractCodeBlocks(response);21const attempts = codeBlocks ? codeBlocks.map(c => c.code) : [response];2223const config = tryGetJsonDataFromPart(attempts, (parsed): ILaunchJSON | undefined => {24if (parsed && 'configurations' in parsed && Array.isArray(parsed.configurations)) {25parsed.configurations = parsed.configurations.map((config: IDebugConfigSchema) => processSchemaProperties(config, extensionsService));26return parsed;27}28if (parsed && 'type' in parsed && 'request' in parsed) {29return { configurations: [processSchemaProperties(parsed, extensionsService)] } as ILaunchJSON;30}31});3233const tasks = tryGetJsonDataFromPart(attempts, (parsed): ITasksJSON | undefined => {34if (parsed && 'tasks' in parsed && Array.isArray(parsed.tasks)) {35return parsed;36}37if (parsed && 'type' in parsed && 'label' in parsed) {38return { tasks: [parsed] };39}40});4142return config && tasks ? { ...config, ...tasks } : config;43};4445function tryGetJsonDataFromPart<T>(attempts: readonly string[], process: (parsed: any) => T) {46for (const attempt of attempts) {47try {48const parsed = jsonc.parse(attempt);49const processed = process(parsed);50if (processed) {51return processed;52}53} catch {54// continue55}56}5758return undefined;59}6061const defaultSchema = ['name', 'type', 'request', 'debugServer', 'preLaunchTask', 'postDebugTask', 'presentation', 'internalConsoleOptions', 'suppressMultipleSessionWarning'];62function processSchemaProperties(parsed: any, extensionsService: IExtensionsService): DebugConfiguration {63// See #768464if ('type' in parsed && parsed['type'] === 'python') {65parsed['type'] = 'debugpy';66}67const schema = getSchemasForType(parsed.type, extensionsService);68if (!schema) {69return parsed;70}71for (const property of Object.keys(parsed)) {72if (defaultSchema.includes(property) || property in schema) {73continue;74}75delete parsed[property];76}77return parsed;78}7980export function getSchemasForTypeAsList(type: string, extensionsService: IExtensionsService): string[] | undefined {81for (const extension of extensionsService.allAcrossExtensionHosts) {82const debuggers = (extension.packageJSON as IPackageJson)?.contributes?.debuggers;83if (!debuggers) {84continue;85}86const debuggersForType = debuggers.filter(d => d.type === type && !d.deprecated);87if (!Array.isArray(debuggersForType) || debuggersForType.length === 0) {88continue;89}9091const schemas = debuggersForType.filter(d => !!d.configurationAttributes.launch || !!d.configurationAttributes.attach).map(d => {92const properties = [d.configurationAttributes.launch?.properties, d.configurationAttributes.attach?.properties].filter(p => p !== undefined).flat();93return Object.entries(properties).map(p => {94return Object.entries(p[1]).map(p => {95return `${p[0]}: ${p[1].description || p[1].markdownDescription}`;96}).flat();97}).flat();98}).flat();99if (schemas.length) {100return schemas;101}102}103return;104}105106107export function getSchemasForType(type: string, extensionsService: IExtensionsService): object | undefined {108for (const extension of extensionsService.allAcrossExtensionHosts) {109const debuggers = (extension.packageJSON as IPackageJson)?.contributes?.debuggers;110if (!debuggers) {111continue;112}113const debuggersForType = debuggers.filter(d => d.type === type && !d.deprecated);114if (!Array.isArray(debuggersForType) || debuggersForType.length === 0) {115continue;116}117118return debuggersForType.flatMap(d => [d.configurationAttributes.launch?.properties, d.configurationAttributes.attach?.properties]).filter(p => p !== undefined).reduce((accumulator, currentObject) => {119return { ...accumulator, ...currentObject };120}, {});121}122return;123}124125126