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