Плагин для анализа планов PostgreSQL в VS Code, и его разработка

—

от автора

VisualStudio Code extension

VisualStudio Code extension

Анализ планов и форматирование запросов PostgreSQL можно выполнять в VS Code, используя explain.tensor.ru и плагин, о котором пойдет речь ниже.


Установка

установка расширений

установка расширений

Для выполнения запросов наш плагин использует другое популярное расширение — SQLTools , поэтому вначале необходимо настроить в нем подключение к PostgreSQL и затем установить Explain PostgreSQL из приложения или командной строки:

code --install-extension TensorCompanyLtd.explain-postgresql

Форматирование запроса

В контекстном меню редактора SQL или в списке Code Actions выбираем Format SQL

форматирование запроса

форматирование запроса

Анализ запроса

В контекстном меню редактора SQL или в списке Code Actions выбираем Explain Analyze , при этом запрос выполнится на сервере PG (настроенном в SQLTools) и полученный план отправится на анализ в сервис explain.tensor.ru через публичное API , результат откроется в новом окне

анализ запроса

анализ запроса

Настройка

В настройках можно указать адрес сайта и опции выполнения анализа:

настройка

настройка

Разработка

Для создания каркаса плагина установим утилиты по инструкции , зададим параметры и установим рекомендуемые расширения:

npx --package yo --package generator-code -- yo code  ? What type of extension do you want to create? New Extension (TypeScript) ? What's the name of your extension? Explain PostgreSQL ? What's the identifier of your extension? explain-postgresql ? What's the description of your extension? Analyzes EXPLAIN plan from PostgreSQL ? Initialize a git repository? Yes ? Bundle the source code with webpack? Yes ? Which package manager to use? npm  code explain-postgresql

Проверяем — нажимаем F5 и в новом окне набираем команду Ctrl-Shift-P > Hello World , должно появиться окно с сообщением:

Hello World

Hello World

Главным файлом плагина является его манифест — package.json, пропишем в нем зависимость от плагина SQLTools для его установки вместе с нашим плагином:

  "extensionDependencies": [     "mtxr.sqltools"   ]

в параметре activationEvents определяем условие запуска плагина, в нашем случае редактирование SQL файлов:

  "activationEvents": [     "onLanguage:sql"   ]

в параметре contributes мы расширяем функционал VSCode — добавляем команды, пункты меню, настройки и прочие. Например, добавим три команды — форматирование текста, анализ плана запроса и открытие сайта:

"commands": [   {     "command": "explain-postgresql.formatsql",     "title": "Format SQL",     "icon": "icons/EP.png"   },   {     "command": "explain-postgresql.explainanalyze",     "title": "Explain Analyze",     "icon": "icons/EP.png"   },   {     "command": "explain-postgresql.explainsite",     "title": "Explain PostgreSQL",     "icon": "icons/EP.png"   } ]

Добавим два пункта в контекстном меню редактора — Format SQL и Explain Analyze, а также кнопку открытия сайта на титульной панели, условием отображения поставим язык SQL, а группу — navigation для размещения в топе и 1_modification для размещения наших команд первыми в списке:

"menus": {   "editor/title": [     {       "when": "resourceLangId == sql",       "command": "explain-postgresql.explainsite",       "group": "navigation",       "icon": "./icons/EP.png"     }   ],   "editor/context": [     {       "when": "resourceLangId == sql",       "command": "explain-postgresql.formatsql",       "group": "1_modification",       "icon": "./icons/EP.png"     },     {       "when": "resourceLangId == sql",       "command": "explain-postgresql.explainanalyze",       "group": "1_modification",       "icon": "./icons/EP.png"     }   ] }

Настройки плагина храним в виде объекта, например URL сайта:

"configuration": {   "type": "object",   "title": "Explain PostgreSQL",   "properties": {     "explain-postgresql.url": {       "scope": "resource",       "type": "string",       "default": "https://explain.tensor.ru",       "description": "API site",       "order": 1     }   } }

для доступа к настройкам используем функцию:

const url = vscode.workspace.getConfiguration('explain-postgresql').get('url');

В параметре main манифеста задается точка входа в плагин — файл extension.js , в котором экспортируется входная функция activate. В качестве параметра она получает контекст , в котором хранятся приватные параметры плагина, например путь к его каталогу extensionUri , который мы используем для получения пути к иконке панели:

panel.iconPath = vscode.Uri.joinPath(Context.extensionUri, "icons/EP.png");

В этой функции регистрируем команды, которые задали в манифесте:

vscode.commands.registerCommand('explain-postgresql.formatsql', formatCommand); vscode.commands.registerCommand('explain-postgresql.explainanalyze', explainCommand); vscode.commands.registerCommand('explain-postgresql.explainsite', explainsiteCommand);

В коде команды форматирования мы берем выделенный участок или весь текст, форматируем, используя API explain.tensor.ru и заменяем текст:

export async function formatCommand(): Promise<void> {     let editor = vscode.window.activeTextEditor;     if (editor) {         let validFullRange: vscode.Range;         let selection = editor.selection;         let text = editor.document.getText(selection);         if (!text) {             text = editor.document.getText();             let invalidRange = new vscode.Range(0, 0, editor.document.lineCount, 0);             validFullRange = editor.document.validateRange(invalidRange);         }         try {             let formatted = await beautifier(text); // получаем форматированный текст             editor.edit(editBuilder => {                 editBuilder.replace(validFullRange || selection, formatted);             })         } catch (e) {             vscode.window.showErrorMessage(`${e}`, {modal: true});         }     } } 

В команде анализа запроса мы также берем текст запроса из текущего редактора и добавляем к нему EXPLAIN со списком опций, заданных в настройках плагина.
Затем запускаем команду executeQuery из плагина SQLTools, которая выполнит запрос на подключенном сервере и вернет план запроса.
Текст запроса вместе с его планом выполнения отправляем на анализ в API explain.tensor.ru и открываем результат в новой панели:

export async function explainCommand(): Promise<void> {     let editor = vscode.window.activeTextEditor;     let selection = editor.selection;     let query = editor.document.getText(selection);     if (!query) {         query = editor.document.getText();     }      const explainSettings = vscode.workspace.getConfiguration('explain-postgresql').get('explain');     let options = Object.keys(explainSettings).filter(key => explainSettings[key]).join(',');     let plan = await vscode.commands.executeCommand(`sqltools.executeQuery`, `EXPLAIN (${options}) ${query}`);      try {         let url = await explain(plan, query); // отправляем план на анализ в API explain         const apiUrl = vscode.workspace.getConfiguration('explain-postgresql').get('url');         await createView(`${apiUrl}${url}`);     } catch(e) {         vscode.window.showErrorMessage(`${e}`, {modal: true});     } }

Для создания панели с результатами анализа используем Webview, которая фактически является элементом iframe , таким образом мы можем размещать в нем свой HTML-код.
Добавим в панель свой iframe, в атрибуте src которого укажем URL документа с результатом анализа:

async function getHtml (url: string) {     return `<!DOCTYPE html><html><head></head> <body style="margin:0px;padding:0px;overflow:hidden"> <div>             <iframe sandbox="allow-scripts allow-forms allow-same-origin allow-downloads" src=${url} style="width:100vw;height:100vh;border: none;display: block;"></iframe> </div> </body></html>`; }  export default async function createView(url: string) {     let panel = vscode.window.createWebviewPanel(         'explainAnalyze',         'Explain Analyze',         vscode.ViewColumn.Active,             {             enableScripts: true,             retainContextWhenHidden: true,         }     );     panel.webview.html = getHtml(url);        panel.iconPath = vscode.Uri.joinPath(Context.extensionUri, "icons/EP.png"); }

Для создания Code Actions используем CodeActionProvider :

export class Actions implements vscode.CodeActionProvider { public static readonly providedCodeActionKinds = [ vscode.CodeActionKind.Refactor, ];  provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> { const commandFormatSQL = this.createCommand('explain-postgresql.formatsql', "Format SQL", "Formatting SQL text"); const commandExplainAnalyze = this.createCommand('explain-postgresql.explainanalyze', "Explain Analyze", "Explain query plan"); return [ commandFormatSQL, commandExplainAnalyze, ]; }  private createCommand(command: string, title: string, tooltip: string): vscode.CodeAction { const action = new vscode.CodeAction(title, vscode.CodeActionKind.Refactor); action.command = {command, title, tooltip}; return action; } } 

регистрируем его в функции activate вместе с командами:

vscode.languages.registerCodeActionsProvider('sql', new Actions(), {     providedCodeActionKinds: Actions.providedCodeActionKinds })

Для создания кнопки на панели статусов создаем новый StatusBarItem также в activate :

const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10); statusBarItem.command = 'explain-postgresql.explainanalyze'; statusBarItem.text = 'Explain Analyze'; statusBarItem.show();

Отладка

Для отладки панелей удобно пользоваться встроенным WebView Developer Tools:

запускаем devtools

запускаем devtools
отладка панели

отладка панели

Сборка и публикация плагина

Для сборки и публикации плагина установим утилиту vsce :

npm install -g @vscode/vsce

Указываем версию в параметре version в package.json и запускаем сборку:

vsce package  Packaged: explain-postgresql/explain-postgresql-1.0.5.vsix (12 files, 319.64KB)

VS Code Extension Marketplace использует Azure DevOps , а утилита vsce публикует только с использованием персональных токенов этого сервиса, поэтому для его получения регистрируемся на dev.azure.com и создаем новую организацию:

создаем организацию

создаем организацию

В настройках организации создаем новый токен:

создаем токен

создаем токен

Копируем созданный токен, заходим на сайт Visual Studio Marketplace publisher management page с тем же логином, которым создавали токен, и создаем публикацию:

create publisher

create publisher

При создании потребуется указать уникальный ID, который пропишем в package.json в параметре publisher:

"publisher": "TensorCompanyLtd"

Проверяем публикацию из утилиты vsce , используя созданный токен:

vsce login TensorCompanyLtd  https://marketplace.visualstudio.com/manage/publishers/  Personal Access Token for publisher 'TensorCompanyLtd': ****************************************************  The Personal Access Token verification succeeded for the publisher 'TensorCompanyLtd'.

И публикуем наш плагин:

vsce publish

Проверяем — плагин должен появиться на сайте  Visual Studio Marketplace publisher management page и через несколько минут в списке расширений в VSCode

управление плагинами

управление плагинами

Код плагина опубликован под лицензией MIT.


ссылка на оригинал статьи https://habr.com/ru/articles/801439/