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