VSCode создавался с прицелом на возможность расширения функционала с помощью плагинов. От UI интерфейса до своих AI агентов — почти каждую часть VS Code можно настроить и улучшить с помощью VSCode API. Многие части самого VSCode фактически являются плагинами.

Есть подробная документация по созданию своего плагина, а большое количество "мини-плагинов" с демонстрацией возможностей можно найти в репозитории vscode-extension-samples. Сама dts-ка с описанием API есть в основной репе microsoft/vscode вот тут. Кроме src/vscode-dts/vscode.d.ts в директории еще много других файлов в формате vscode.proposed.*.d.ts. Это экспериментальное API. О нем и пойдет речь в данной статье.

Что и как?

Плагин github-copilot (для inline-подсказок) использует следующие proposed API

// package.json плагина github-copilot
{
    ...
    "enabledApiProposals": [
		"inlineCompletionsAdditions"
	],
    ...
}

Это почти все необходимое, чтобы ваш плагин использовал какое-нибудь proposed API. Еще к сорцам плагина необходимо добавить dts-ку соответствующего API. Это удобно делать с помощью пакета @vscode-dts.

Microsoft рекомендует использовать такие расширения только в VSCode Insiders - специальной dev версии VSCode. Это связано с тем, что proposed API не стабильно и может кардинально измениться в следующем релизе VSCode (в статье будет пример).

Использование в привычном VSCode тоже возможно, но есть одно ограничение - ваш VSCode, если его не попросить, не сможет использовать функционал плагина, основанный на proposed API (так не всегда, но об этом позже). Попросить можно так: отредактировать аргументы загрузки самого VSCode, т.е. выполнить команду Preferences: Configure Runtime Arguments и добавить туда

// file .vscode/argv.json
{
    ...
    "enable-proposed-api": ["<YOUR-EXTENSION-ID>"]
}

Ну и главное - расширение, которое использует proposed API, нельзя публиковать в официальный Extensions Marketplace. Придется устанавливать через команду Install from VSIX.

Генерация commit message

Рассмотрим пример того, как расширение github-copilot-chat генерит commit message. Для этого оно использует proposed API под названием contribSourceControlInputBoxMenu. Это API позволяет добавлять кнопку к input блоку в Source Control меню и что-то туда записывать. Выглядит это следующим образом

Фактически это API позволяет работать с scm/inputBox (который скоро будет полностью заменен на SourceControl интерфейс). Использование выглядит очень просто

const repo: git.Repository = ...; 
repo.inputBox.value = 'My awesome commit message';

В плагине задается следующим образом

// package.json плагина github-copilot-chat
{
  "enabledApiProposals": [
    "contribSourceControlInputBoxMenu"
  ],
  "contributes": {
    "commands": [
      {
		"command": "github.copilot.git.generateCommitMessage",
		"title": "%github.copilot.git.generateCommitMessage%",
		"icon": "$(sparkle)",
		"enablement": "!github.copilot.interactiveSession.disabled",
		"category": "GitHub Copilot"
      },
      ...
    ],
    ...
  },
  ...
}

Сама кнопка с иконкой $(sparkle) , а при нажатии просто вызывается команда github.copilot.git.generateCommitMessage.

Стабильность

Proposed API - штука не стабильная. Хороший пример - это proposed API под названием interactive. Proposal довольный старый и до версии 1.89.0 позволял добавлять в свой плагин inline чат. Выглядело так

Но теперь этот proposal (как и все чатовые proposals) доступны только с авторизованным github copilot и только как интеграция в github-copilot-chat.

Пример

Напишем игрушечный плагин, который показывает cwd shell-integration терминала при запуске команды. Для этого используем proposed API под названием terminalShellIntegration.

Создадим свой awesome sample проект через yo

Процесс создания структуры плагина
Процесс создания структуры плагина

Разбирать детально структуру не будем, разберем только package.json и src/extension.ts

// package.json
{
  "name": "awesomesample",
  "displayName": "awesomeSample",
  "publisher": "awesomePublisher",
  ...
  "activationEvents": [
    "onStartupFinished"
  ],
  "enabledApiProposals": [
    "terminalShellIntegration"
  ],
  ...
}

Чтобы использовать proposed API сначала установим @vscode/dts

> npm i @vscode/dts

, а затем запустим

> npx vscode-dts dev
Downloading vscode.proposed.terminalShellIntegration.d.ts
To:   /Users/d.artyushin/Documents/habr/github-copilot-proposed-api/awesomesample/vscode.proposed.terminalShellIntegration.d.ts
From: https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellIntegration.d.ts
Read more about proposed API at: https://code.visualstudio.com/api/advanced-topics/using-proposed-api

В корне проекта должен появиться файл vscode.proposed.terminalShellIntegration.d.ts с описанием доступного API. Используем его

// src/extension.ts
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
	// подписываемся на запуск команды в терминале
    const terminalCwd = vscode.window.onDidStartTerminalShellExecution((event) => {
		// event имеет тип vscode.TerminalShellExecutionStartEvent
        // если shellIntegration есть, то у него берем поле cwd
		const currentCwd = event.terminal.shellIntegration?.cwd;

		if (currentCwd === undefined) {
			vscode.window.showInformationMessage('No found cwd :(');	
		} else {
			vscode.window.showInformationMessage(`Cwd is ${currentCwd}`);
    	}
	});

	context.subscriptions.push(terminalCwd);
}

export function deactivate() {}

Нужно не забыть добавить в argv.json строку (попробуйте сначала запустить плагин без этого)

// .vscode/argv.json
{
  ...
  // Enable proposed API features.
  "enable-proposed-api": ["awesomePublisher.awesomesample"],
}

Наконец, запускаем плагин и пробуем команду ls в zsh и bash:

Запустили ls в zsh
Запустили ls в zsh
Запустили ls в bash
Запустили ls в bash

Proposed API в плагинах Copilot

Обычно github тестирует свои новые API на плагине github-copilot-chat. Вот пример

// package.json плагина github-copilot-chat
{
  ...
  "enabledApiProposals": [
    "interactive",
    "terminalDataWriteEvent",
	"terminalExecuteCommandEvent",
	"terminalSelection",
	"terminalQuickFixProvider",
	"chatParticipantAdditions",
	"defaultChatParticipant",
	"embeddings",
	"chatVariableResolver",
	"chatProvider",
	"mappedEditsProvider",
	"aiRelatedInformation",
	"codeActionAI",
	"findTextInFiles",
	"textSearchProvider",
	"commentReveal",
	"contribSourceControlInputBoxMenu",
	"contribCommentEditorActionsMenu",
	"contribCommentThreadAdditionalMenu",
	"contribCommentsViewThreadMenus",
	"newSymbolNamesProvider",
	"findFiles2",
	"extensionsAny",
	"authLearnMore",
	"testObserver",
	"aiTextSearchProvider",
	"documentFiltersExclusive",
	"chatParticipantPrivate",
	"lmTools"
  ],
  ...
}

Справедливый вопрос: "Как тогда github публикует свои плагины в Extension Marketplace?". В исходном коде VSCode можно найти следующий код

...
        // NEW world - product.json spells out what proposals each extension can use
		if (productService.extensionEnabledApiProposals) {
			for (const [k, value] of Object.entries(productService.extensionEnabledApiProposals)) {
				const key = ExtensionIdentifier.toKey(k);
				const proposalNames = value.filter(name => {
					if (!allApiProposals[<ApiProposalName>name]) {
						_logService.warn(`Via 'product.json#extensionEnabledApiProposals' extension '${key}' wants API proposal '${name}' but that proposal DOES NOT EXIST. Likely, the proposal has been finalized (check 'vscode.d.ts') or was abandoned.`);
						return false;
					}
					return true;
				});
				this._productEnabledExtensions.set(key, proposalNames);
			}
		}
	}

	updateEnabledApiProposals(extensions: IExtensionDescription[]): void {
		for (const extension of extensions) {
			this.doUpdateEnabledApiProposals(extension);
		}
	}

...   

Коммент в коде говорит сам за себя. Файл product.json после установки VSCode находится по пути (для MacOS) /Applications/Visual Studio Code.app/Contents/Resources/app/product.json . Там для "важных" расширений как раз указаны доступные proposal API

{
    ...
    "extensionEnabledApiProposals": {
        ...
        "GitHub.copilot": [
			"inlineCompletionsAdditions"
		],
		"GitHub.copilot-nightly": [
			"inlineCompletionsAdditions"
		],
		"GitHub.copilot-chat": [
			"interactive",
			"terminalDataWriteEvent",
			"terminalExecuteCommandEvent",
			"terminalSelection",
			"terminalQuickFixProvider",
			"chatParticipantAdditions",
			"defaultChatParticipant",
			"embeddings",
			"chatVariableResolver",
			"chatProvider",
			"mappedEditsProvider",
			"aiRelatedInformation",
			"codeActionAI",
			"findTextInFiles",
			"textSearchProvider",
			"commentReveal",
			"contribSourceControlInputBoxMenu",
			"contribCommentEditorActionsMenu",
			"contribCommentThreadAdditionalMenu",
			"contribCommentsViewThreadMenus",
			"newSymbolNamesProvider",
			"findFiles2",
			"extensionsAny",
			"authLearnMore",
			"testObserver",
			"aiTextSearchProvider",
			"documentFiltersExclusive",
			"chatParticipantPrivate",
			"lmTools"
		],
        ...
}

Себе (или пользователям) вы тоже можете обновлять этот файл, чтобы избегать манипуляций с argv.

Заключение

Заключение очень простое - учитывайте нюансы и используйте VSCode proposed API, там много клевого и полезного :) Так же призываю следить за Release Notes новых версий VSCode - там всегда есть раздел Proposed APIs.

Комментарии (1)


  1. gudvinr
    08.07.2024 05:44

    VSCode создавался с прицелом на возможность расширения функционала с помощью плагинов

    VSCode создавался с прицелом на внедрение проприетарных плагинов Microsoft под браваду о приверженности опенсорсу

    Pylance с опенсорсной сборкой не работает.
    OmniSharp почти закрыли, но поднялась шумиха.
    Практически все крупные плагины разрабатываются MS для MS практически без возможности внести изменения. Да и вносить изменения в код, написанный энтерпрайзом для энтерпрайза с отпратительным процессом принятия PR, уже итак не хочется