Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/context/node/resolvers/vscodeContext.ts
13405 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
import * as l10n from '@vscode/l10n';
6
import type { Command } from 'vscode';
7
import { IWorkbenchService } from '../../../../platform/workbench/common/workbenchService';
8
import { extractCodeBlocks } from '../../../../util/common/markdown';
9
10
export interface VSCodeParticipantMetadata {
11
commandToRun?: Command;
12
}
13
14
/**
15
* Parses a raw Markdown string containing a code block and extracts settings and commands to show as options to user.
16
* @param codeBlock Markdown string containing a single code block surrounded by "```"
17
*/
18
export async function parseSettingsAndCommands(workbenchService: IWorkbenchService, codeBlock: string): Promise<VSCodeParticipantMetadata[]> {
19
const parsedCodeBlock = extractCodeBlocks(codeBlock);
20
21
// parsedCodeBlock is expected to only have a single element.
22
for (const block of parsedCodeBlock) {
23
// Skip non-JSON blocks, only process JSON blocks for settings/commands
24
if (block.language !== 'json' && block.language !== '') {
25
return [];
26
}
27
28
let parsed: ParsedItem[] = [];
29
try {
30
const removeTrailingCommas = block.code.replace(/,\s*([\]}])/g, '$1');
31
parsed = JSON.parse(removeTrailingCommas);
32
} catch (error) {
33
return [];
34
}
35
36
if (!parsed.length) {
37
return [];
38
}
39
const parsedMetadata: VSCodeParticipantMetadata[] = [];
40
const hasSettings = parsed.some(item => item.type === 'setting');
41
const hasCommands = parsed.some(item => item.type === 'command');
42
43
if (hasSettings) {
44
const allSettings = await workbenchService.getAllSettings();
45
// skip settings which are not found
46
parsed = parsed.filter(item => {
47
if (item.details) {
48
return Object.keys(allSettings).includes(item.details.key);
49
}
50
return true;
51
});
52
53
const settingsQuery = parsed.reduce((acc: string, item: ParsedItem) => {
54
if (item.details) {
55
acc += `@id:${item.details.key} `;
56
}
57
return acc;
58
}, '');
59
60
parsedMetadata.push({
61
commandToRun: {
62
command: 'workbench.action.openSettings',
63
arguments: [settingsQuery],
64
title: l10n.t("Show in Settings Editor"),
65
}
66
});
67
68
return parsedMetadata;
69
}
70
71
if (hasCommands) {
72
const item = parsed[0];
73
if (item.details?.key === 'workbench.extensions.search' || item.details?.key === 'workbench.extensions.installExtension') {
74
const args = (Array.isArray(item.details.value) ? item.details.value : [item.details.value]).filter(
75
(arg): arg is string => typeof arg === 'string'
76
);
77
78
// We only know how to handle 1 arguments
79
if (args.length === 1) {
80
const KNOWN_QUERIES = [
81
'featured',
82
'popular',
83
'recentlyPublished',
84
'recommended',
85
'updates',
86
'builtin',
87
'enabled',
88
'disabled',
89
'installed',
90
'workspaceUnsupported',
91
];
92
// If the arg contains a colon, assume it is a tag
93
if (args[0].includes(':') && !args[0].startsWith('@')) {
94
args[0] = `@${args[0]}`;
95
}
96
// If the arg is a known query, use it
97
else if (KNOWN_QUERIES.includes(args[0])) {
98
args[0] = `@${args[0]}`;
99
}
100
}
101
102
parsedMetadata.push({
103
commandToRun: {
104
command: 'workbench.extensions.search',
105
arguments: args,
106
title: l10n.t("Search Extension Marketplace"),
107
}
108
});
109
return parsedMetadata;
110
}
111
else {
112
// Get all commands regardless of preconditions, because there are some commands that a user may meet the preconditions for,
113
// but Copilot not, so we still will show the command in the palette for the user to run.
114
const allCommandsNoFilter = (await workbenchService.getAllCommands(/* filterByPreCondition */false));
115
const commandItem = allCommandsNoFilter.find(commandItem => commandItem.command === item.details?.key);
116
if (!commandItem) {
117
// If we can't find the command on the list, just open the command palette without any pre-filled filter
118
parsedMetadata.push({
119
commandToRun: {
120
command: 'workbench.action.quickOpen',
121
arguments: [`>`],
122
title: l10n.t("Open Command Palette"),
123
}
124
});
125
return parsedMetadata;
126
}
127
parsedMetadata.push({
128
commandToRun: {
129
command: 'workbench.action.quickOpen',
130
arguments: [`>${commandItem.label ?? ''}`],
131
title: parsedMetadata.length > 1 ? l10n.t('Show "{0}"', commandItem.label ?? '') : l10n.t("Show in Command Palette"),
132
}
133
});
134
return parsedMetadata;
135
}
136
}
137
}
138
return [];
139
140
}
141
142
type ParsedItem = {
143
type: 'command' | 'setting';
144
details: {
145
key: string;
146
value?: string;
147
};
148
};
149
150