{"id":371260,"date":"2024-05-21T04:44:30","date_gmt":"2024-05-21T04:44:30","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=371260"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-05-21T04:44:30","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=371260","title":{"rendered":"<span>\u041f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u043f\u043b\u0430\u043d\u043e\u0432 PostgreSQL \u0432 VS Code, \u0438 \u0435\u0433\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6f9\/7f1\/17a\/6f97f117a9328d51d91835d6d834f935.png\" alt=\"VisualStudio Code extension\" title=\"VisualStudio Code extension\" width=\"785\" height=\"363\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6f9\/7f1\/17a\/6f97f117a9328d51d91835d6d834f935.png\"\/><\/p>\n<div><figcaption>VisualStudio Code extension<\/figcaption><\/div>\n<\/figure>\n<p>\u0410\u043d\u0430\u043b\u0438\u0437 \u043f\u043b\u0430\u043d\u043e\u0432 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 PostgreSQL \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0432 VS Code, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/explain.tensor.ru\">explain.tensor.ru<\/a> \u0438 \u043f\u043b\u0430\u0433\u0438\u043d, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u0439\u0434\u0435\u0442 \u0440\u0435\u0447\u044c \u043d\u0438\u0436\u0435.<\/p>\n<hr\/>\n<h4>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/h4>\n<figure class=\"float\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c9b\/ec8\/88c\/c9bec888c111e3cef0c6290c783c5585.png\" alt=\"\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439\" title=\"\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439\" width=\"407\" height=\"197\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c9b\/ec8\/88c\/c9bec888c111e3cef0c6290c783c5585.png\"\/><\/p>\n<div><figcaption>\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439<\/figcaption><\/div>\n<\/figure>\n<p>\u0414\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 &#8212; <a href=\"https:\/\/vscode-sqltools.mteixeira.dev\/\">SQLTools<\/a> , \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432 \u043d\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a PostgreSQL \u0438 \u0437\u0430\u0442\u0435\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=TensorCompanyLtd.explain-postgresql\">Explain PostgreSQL<\/a> \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438:<\/p>\n<pre><code class=\"bash\">code --install-extension TensorCompanyLtd.explain-postgresql<\/code><\/pre>\n<h4>\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/h4>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 SQL \u0438\u043b\u0438 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 <code>Code Actions<\/code> \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c <code>Format SQL<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/189\/82f\/b3b\/18982fb3b27116bf9e14e2632c4efda5.png\" alt=\"\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\" title=\"\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\" width=\"925\" height=\"603\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/189\/82f\/b3b\/18982fb3b27116bf9e14e2632c4efda5.png\"\/><\/p>\n<div><figcaption>\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/figcaption><\/div>\n<\/figure>\n<h4>\u0410\u043d\u0430\u043b\u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/h4>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 SQL \u0438\u043b\u0438 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 <code>Code Actions<\/code> \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c <code>Explain Analyze<\/code> , \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 PG (\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u043c \u0432 SQLTools) \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043f\u043b\u0430\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u043d\u0430 \u0430\u043d\u0430\u043b\u0438\u0437 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u00a0<a href=\"https:\/\/explain.tensor.ru\/\">explain.tensor.ru<\/a>\u00a0\u0447\u0435\u0440\u0435\u0437\u00a0<a href=\"https:\/\/explain.tensor.ru\/api-description\/\">\u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0435 API<\/a>\u00a0, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0442\u043a\u0440\u043e\u0435\u0442\u0441\u044f \u0432 \u043d\u043e\u0432\u043e\u043c \u043e\u043a\u043d\u0435<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b12\/2d3\/7cf\/b122d37cf55dd5c7b912c3c924233e23.png\" alt=\"\u0430\u043d\u0430\u043b\u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\" title=\"\u0430\u043d\u0430\u043b\u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\" width=\"1570\" height=\"665\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b12\/2d3\/7cf\/b122d37cf55dd5c7b912c3c924233e23.png\"\/><\/p>\n<div><figcaption>\u0430\u043d\u0430\u043b\u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/figcaption><\/div>\n<\/figure>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430<\/h4>\n<p>\u0412 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0430\u0439\u0442\u0430 \u0438 \u043e\u043f\u0446\u0438\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b5c\/fff\/c99\/b5cfffc9960c1dc3791ad8d2f1a5e54c.png\" alt=\"\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\" title=\"\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\" width=\"518\" height=\"549\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b5c\/fff\/c99\/b5cfffc9960c1dc3791ad8d2f1a5e54c.png\"\/><\/p>\n<div><figcaption>\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430<\/figcaption><\/div>\n<\/figure>\n<h4>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430<\/h4>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u0430\u0440\u043a\u0430\u0441\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u043f\u043e <a href=\"https:\/\/code.visualstudio.com\/api\/get-started\/your-first-extension\">\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438<\/a> , \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"bash\">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<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c &#8212; \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c F5 \u0438 \u0432 \u043d\u043e\u0432\u043e\u043c \u043e\u043a\u043d\u0435 \u043d\u0430\u0431\u0438\u0440\u0430\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>Ctrl-Shift-P<\/code> > <code>Hello World<\/code> , \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u043a\u043d\u043e \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dfd\/f4a\/336\/dfdf4a336fad2d59663026b247dcf12d.png\" alt=\"Hello World\" title=\"Hello World\" width=\"1021\" height=\"378\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dfd\/f4a\/336\/dfdf4a336fad2d59663026b247dcf12d.png\"\/><\/p>\n<div><figcaption>Hello World<\/figcaption><\/div>\n<\/figure>\n<p>\u0413\u043b\u0430\u0432\u043d\u044b\u043c \u0444\u0430\u0439\u043b\u043e\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0435\u0433\u043e <a href=\"https:\/\/code.visualstudio.com\/api\/get-started\/extension-anatomy#extension-manifest\">\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442<\/a> &#8212; package.json, \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0432 \u043d\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0442 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 SQLTools \u0434\u043b\u044f \u0435\u0433\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043d\u0430\u0448\u0438\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u043c:<\/p>\n<pre><code class=\"json\">  \"extensionDependencies\": [     \"mtxr.sqltools\"   ]<\/code><\/pre>\n<p>\u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/activation-events\">activationEvents<\/a> \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u0430, \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 SQL \u0444\u0430\u0439\u043b\u043e\u0432:<\/p>\n<pre><code class=\"json\">  \"activationEvents\": [     \"onLanguage:sql\"   ]<\/code><\/pre>\n<p>\u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/contribution-points\">contributes<\/a> \u043c\u044b \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b VSCode &#8212; \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043f\u0443\u043d\u043a\u0442\u044b \u043c\u0435\u043d\u044e, \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438 \u043f\u0440\u043e\u0447\u0438\u0435. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0440\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b &#8212; \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u043a\u0441\u0442\u0430, \u0430\u043d\u0430\u043b\u0438\u0437 \u043f\u043b\u0430\u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0441\u0430\u0439\u0442\u0430:<\/p>\n<pre><code class=\"json\">\"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\"   } ]<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u0432\u0430 \u043f\u0443\u043d\u043a\u0442\u0430 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 &#8212; <code>Format SQL<\/code> \u0438 <code>Explain Analyze<\/code>,  \u0430 \u0442\u0430\u043a\u0436\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u0430\u0439\u0442\u0430 \u043d\u0430 \u0442\u0438\u0442\u0443\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438, \u0443\u0441\u043b\u043e\u0432\u0438\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u044f\u0437\u044b\u043a SQL, \u0430 \u0433\u0440\u0443\u043f\u043f\u0443 &#8212; <code>navigation<\/code> \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0432 \u0442\u043e\u043f\u0435 \u0438 <code>1_modification<\/code> \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u0435\u0440\u0432\u044b\u043c\u0438 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435:<\/p>\n<pre><code class=\"json\">\"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\"     }   ] }<\/code><\/pre>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0445\u0440\u0430\u043d\u0438\u043c \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 URL \u0441\u0430\u0439\u0442\u0430:<\/p>\n<pre><code class=\"json\">\"configuration\": {   \"type\": \"object\",   \"title\": \"Explain PostgreSQL\",   \"properties\": {     \"explain-postgresql.url\": {       \"scope\": \"resource\",       \"type\": \"string\",       \"default\": \"https:\/\/explain.tensor.ru\",       \"description\": \"API site\",       \"order\": 1     }   } }<\/code><\/pre>\n<p>\u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e:<\/p>\n<pre><code class=\"typescript\">const url = vscode.workspace.getConfiguration('explain-postgresql').get('url');<\/code><\/pre>\n<p>\u0412 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>main<\/code>  \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0437\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u0432 \u043f\u043b\u0430\u0433\u0438\u043d &#8212; \u0444\u0430\u0439\u043b <em>extension.js<\/em> , \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432\u0445\u043e\u0434\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>activate<\/code>. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043e\u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/vscode-api#ExtensionContext\">\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/a> , \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043b\u0430\u0433\u0438\u043d\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0443\u0442\u044c \u043a \u0435\u0433\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0443 <em>extensionUri<\/em> , \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0438 \u043a \u0438\u043a\u043e\u043d\u043a\u0435 \u043f\u0430\u043d\u0435\u043b\u0438:<\/p>\n<pre><code class=\"typescript\">panel.iconPath = vscode.Uri.joinPath(Context.extensionUri, \"icons\/EP.png\");<\/code><\/pre>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0434\u0430\u043b\u0438 \u0432 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435:<\/p>\n<pre><code class=\"typescript\">vscode.commands.registerCommand('explain-postgresql.formatsql', formatCommand); vscode.commands.registerCommand('explain-postgresql.explainanalyze', explainCommand); vscode.commands.registerCommand('explain-postgresql.explainsite', explainsiteCommand);<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u044b \u0431\u0435\u0440\u0435\u043c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u0443\u0447\u0430\u0441\u0442\u043e\u043a \u0438\u043b\u0438 \u0432\u0435\u0441\u044c \u0442\u0435\u043a\u0441\u0442, \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/explain.tensor.ru\/api-description\/\">API explain.tensor.ru<\/a> \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u0442\u0435\u043a\u0441\u0442:<\/p>\n<pre><code class=\"typescript\">export async function formatCommand(): Promise&lt;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); \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442             editor.edit(editBuilder => {                 editBuilder.replace(validFullRange || selection, formatted);             })         } catch (e) {             vscode.window.showErrorMessage(`${e}`, {modal: true});         }     } } <\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0431\u0435\u0440\u0435\u043c \u0442\u0435\u043a\u0441\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a \u043d\u0435\u043c\u0443 EXPLAIN \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c \u043e\u043f\u0446\u0438\u0439, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u0430. <br \/>\u0417\u0430\u0442\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>executeQuery<\/code> \u0438\u0437 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=mtxr.sqltools\">SQLTools<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u043b\u0430\u043d \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<br \/>\u0422\u0435\u043a\u0441\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0435\u0433\u043e \u043f\u043b\u0430\u043d\u043e\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0430 \u0430\u043d\u0430\u043b\u0438\u0437 \u0432 API explain.tensor.ru \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 \u043d\u043e\u0432\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438:<\/p>\n<pre><code class=\"typescript\">export async function explainCommand(): Promise&lt;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); \/\/ \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043b\u0430\u043d \u043d\u0430 \u0430\u043d\u0430\u043b\u0438\u0437 \u0432 API explain         const apiUrl = vscode.workspace.getConfiguration('explain-postgresql').get('url');         await createView(`${apiUrl}${url}`);     } catch(e) {         vscode.window.showErrorMessage(`${e}`, {modal: true});     } }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0430\u043d\u0435\u043b\u0438 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438 \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/code.visualstudio.com\/api\/extension-guides\/webview\">Webview<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c iframe , \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0442\u044c \u0432 \u043d\u0435\u043c \u0441\u0432\u043e\u0439 HTML-\u043a\u043e\u0434. <br \/>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043f\u0430\u043d\u0435\u043b\u044c \u0441\u0432\u043e\u0439 iframe, \u0432 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0435 src \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0443\u043a\u0430\u0436\u0435\u043c URL \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0430\u043d\u0430\u043b\u0438\u0437\u0430:<\/p>\n<pre><code class=\"typescript\">async function getHtml (url: string) {     return `&lt;!DOCTYPE html>&lt;html>&lt;head>&lt;\/head> &lt;body style=\"margin:0px;padding:0px;overflow:hidden\"> &lt;div>             &lt;iframe sandbox=\"allow-scripts allow-forms allow-same-origin allow-downloads\" src=${url} style=\"width:100vw;height:100vh;border: none;display: block;\">&lt;\/iframe> &lt;\/div> &lt;\/body>&lt;\/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><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f <code>Code Actions<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/code.visualstudio.com\/api\/references\/vscode-api#CodeActionProvider\">CodeActionProvider<\/a> :<\/p>\n<pre><code class=\"typescript\">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&lt;(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; } } <\/code><\/pre>\n<p>\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>activate<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438:<\/p>\n<pre><code class=\"typescript\">vscode.languages.registerCodeActionsProvider('sql', new Actions(), {     providedCodeActionKinds: Actions.providedCodeActionKinds })<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/vscode-api#StatusBarItem\">StatusBarItem<\/a> \u0442\u0430\u043a\u0436\u0435 \u0432 <code>activate<\/code> :<\/p>\n<pre><code class=\"typescript\">const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10); statusBarItem.command = 'explain-postgresql.explainanalyze'; statusBarItem.text = 'Explain Analyze'; statusBarItem.show();<\/code><\/pre>\n<h4>\u041e\u0442\u043b\u0430\u0434\u043a\u0430<\/h4>\n<p>\u0414\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u043f\u0430\u043d\u0435\u043b\u0435\u0439 \u0443\u0434\u043e\u0431\u043d\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c WebView Developer Tools:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/12b\/5a0\/4bc\/12b5a04bc33e68e8b056c53978620feb.png\" alt=\"\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c devtools\" title=\"\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c devtools\" width=\"600\" height=\"197\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/12b\/5a0\/4bc\/12b5a04bc33e68e8b056c53978620feb.png\"\/><\/p>\n<div><figcaption>\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c devtools<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b4b\/fee\/d04\/b4bfeed04cf7bb93fa5f965f7e49f121.png\" alt=\"\u043e\u0442\u043b\u0430\u0434\u043a\u0430 \u043f\u0430\u043d\u0435\u043b\u0438\" title=\"\u043e\u0442\u043b\u0430\u0434\u043a\u0430 \u043f\u0430\u043d\u0435\u043b\u0438\" width=\"1618\" height=\"284\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b4b\/fee\/d04\/b4bfeed04cf7bb93fa5f965f7e49f121.png\"\/><\/p>\n<div><figcaption>\u043e\u0442\u043b\u0430\u0434\u043a\u0430 \u043f\u0430\u043d\u0435\u043b\u0438<\/figcaption><\/div>\n<\/figure>\n<h4>\u0421\u0431\u043e\u0440\u043a\u0430 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u0430<\/h4>\n<p>\u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u0443 <code>vsce<\/code> :<\/p>\n<pre><code class=\"bash\">npm install -g @vscode\/vsce<\/code><\/pre>\n<p>\u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0432\u0435\u0440\u0441\u0438\u044e \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>version<\/code> \u0432 <em>package.json<\/em> \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0431\u043e\u0440\u043a\u0443:<\/p>\n<pre><code class=\"bash\">vsce package  Packaged: explain-postgresql\/explain-postgresql-1.0.5.vsix (12 files, 319.64KB)<\/code><\/pre>\n<p><a href=\"https:\/\/marketplace.visualstudio.com\/vscode\">VS Code Extension Marketplace<\/a> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/azure.microsoft.com\/services\/devops\/\">Azure DevOps<\/a> , \u0430 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>vsce<\/code> \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u044d\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 <a href=\"https:\/\/dev.azure.com\">dev.azure.com<\/a> \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u0443\u044e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044e:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b37\/005\/d94\/b37005d94fe7420b6988fca191a47058.png\" alt=\"\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044e\" title=\"\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044e\" width=\"397\" height=\"291\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b37\/005\/d94\/b37005d94fe7420b6988fca191a47058.png\"\/><\/p>\n<div><figcaption>\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044e<\/figcaption><\/div>\n<\/figure>\n<p>\u0412 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6aa\/066\/08d\/6aa06608d1e84618edee02ff07595cd6.png\" alt=\"\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d\" title=\"\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d\" width=\"1901\" height=\"582\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6aa\/066\/08d\/6aa06608d1e84618edee02ff07595cd6.png\"\/><\/p>\n<div><figcaption>\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d<\/figcaption><\/div>\n<\/figure>\n<p>\u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d, \u0437\u0430\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0441\u0430\u0439\u0442 <a href=\"https:\/\/marketplace.visualstudio.com\/manage\">Visual Studio Marketplace publisher management page<\/a> \u0441 \u0442\u0435\u043c \u0436\u0435 \u043b\u043e\u0433\u0438\u043d\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b\u0438 \u0442\u043e\u043a\u0435\u043d, \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044e:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b0f\/2d3\/b2c\/b0f2d3b2c33bc055cc28e82b28594cd2.png\" alt=\"create publisher\" title=\"create publisher\" width=\"657\" height=\"327\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b0f\/2d3\/b2c\/b0f2d3b2c33bc055cc28e82b28594cd2.png\"\/><\/p>\n<div><figcaption>create publisher<\/figcaption><\/div>\n<\/figure>\n<p>\u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 ID, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0432 package.json \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>publisher<\/code>:<\/p>\n<pre><code class=\"json\">\"publisher\": \"TensorCompanyLtd\"<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044e \u0438\u0437 \u0443\u0442\u0438\u043b\u0438\u0442\u044b <code>vsce<\/code> , \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d:<\/p>\n<pre><code class=\"bash\">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'.<\/code><\/pre>\n<p>\u0418 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d:<\/p>\n<pre><code class=\"bash\">vsce publish<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c &#8212; \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 \u00a0<a href=\"https:\/\/marketplace.visualstudio.com\/manage\">Visual Studio Marketplace publisher management page<\/a>\u00a0\u0438 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439 \u0432 VSCode<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/360\/603\/41f\/36060341f800db15229d77ba2e3ad82e.png\" alt=\"\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c\u0438\" title=\"\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c\u0438\" width=\"1192\" height=\"293\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/360\/603\/41f\/36060341f800db15229d77ba2e3ad82e.png\"\/><\/p>\n<div><figcaption>\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c\u0438<\/figcaption><\/div>\n<\/figure>\n<p><a href=\"https:\/\/github.com\/MGorkov\/explain-postgresql-vscode\">\u041a\u043e\u0434 \u043f\u043b\u0430\u0433\u0438\u043d\u0430<\/a> \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043f\u043e\u0434 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0435\u0439 MIT.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/801439\/\"> https:\/\/habr.com\/ru\/articles\/801439\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\">\n<div><figcaption>VisualStudio Code extension<\/figcaption><\/div>\n<\/figure>\n<p>\u0410\u043d\u0430\u043b\u0438\u0437 \u043f\u043b\u0430\u043d\u043e\u0432 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 PostgreSQL \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0432 VS Code, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/explain.tensor.ru\">explain.tensor.ru<\/a> \u0438 \u043f\u043b\u0430\u0433\u0438\u043d, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u0439\u0434\u0435\u0442 \u0440\u0435\u0447\u044c \u043d\u0438\u0436\u0435.<\/p>\n<hr\/>\n<h4>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/h4>\n<figure class=\"float\">\n<div><figcaption>\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439<\/figcaption><\/div>\n<\/figure>\n<p>\u0414\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 &#8212; <a href=\"https:\/\/vscode-sqltools.mteixeira.dev\/\">SQLTools<\/a> , \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432 \u043d\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a PostgreSQL \u0438 \u0437\u0430\u0442\u0435\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=TensorCompanyLtd.explain-postgresql\">Explain PostgreSQL<\/a> \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438:<\/p>\n<pre><code class=\"bash\">code --install-extension TensorCompanyLtd.explain-postgresql<\/code><\/pre>\n<h4>\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/h4>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 SQL \u0438\u043b\u0438 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 <code>Code Actions<\/code> \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c <code>Format SQL<\/code><\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/figcaption><\/div>\n<\/figure>\n<h4>\u0410\u043d\u0430\u043b\u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/h4>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 SQL \u0438\u043b\u0438 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 <code>Code Actions<\/code> \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c <code>Explain Analyze<\/code> , \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 PG (\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u043c \u0432 SQLTools) \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043f\u043b\u0430\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u043d\u0430 \u0430\u043d\u0430\u043b\u0438\u0437 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u00a0<a href=\"https:\/\/explain.tensor.ru\/\">explain.tensor.ru<\/a>\u00a0\u0447\u0435\u0440\u0435\u0437\u00a0<a href=\"https:\/\/explain.tensor.ru\/api-description\/\">\u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0435 API<\/a>\u00a0, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0442\u043a\u0440\u043e\u0435\u0442\u0441\u044f \u0432 \u043d\u043e\u0432\u043e\u043c \u043e\u043a\u043d\u0435<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u0430\u043d\u0430\u043b\u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/figcaption><\/div>\n<\/figure>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430<\/h4>\n<p>\u0412 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0430\u0439\u0442\u0430 \u0438 \u043e\u043f\u0446\u0438\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430:<\/p>\n<figure class=\"\">\n<div><figcaption>\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430<\/figcaption><\/div>\n<\/figure>\n<h4>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430<\/h4>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u0430\u0440\u043a\u0430\u0441\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u043f\u043e <a href=\"https:\/\/code.visualstudio.com\/api\/get-started\/your-first-extension\">\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438<\/a> , \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"bash\">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<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c &#8212; \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c F5 \u0438 \u0432 \u043d\u043e\u0432\u043e\u043c \u043e\u043a\u043d\u0435 \u043d\u0430\u0431\u0438\u0440\u0430\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>Ctrl-Shift-P<\/code> > <code>Hello World<\/code> , \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u043a\u043d\u043e \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c:<\/p>\n<figure class=\"full-width\">\n<div><figcaption>Hello World<\/figcaption><\/div>\n<\/figure>\n<p>\u0413\u043b\u0430\u0432\u043d\u044b\u043c \u0444\u0430\u0439\u043b\u043e\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0435\u0433\u043e <a href=\"https:\/\/code.visualstudio.com\/api\/get-started\/extension-anatomy#extension-manifest\">\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442<\/a> &#8212; package.json, \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0432 \u043d\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0442 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 SQLTools \u0434\u043b\u044f \u0435\u0433\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043d\u0430\u0448\u0438\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u043c:<\/p>\n<pre><code class=\"json\">  \"extensionDependencies\": [     \"mtxr.sqltools\"   ]<\/code><\/pre>\n<p>\u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/activation-events\">activationEvents<\/a> \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u0430, \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 SQL \u0444\u0430\u0439\u043b\u043e\u0432:<\/p>\n<pre><code class=\"json\">  \"activationEvents\": [     \"onLanguage:sql\"   ]<\/code><\/pre>\n<p>\u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/contribution-points\">contributes<\/a> \u043c\u044b \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b VSCode &#8212; \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043f\u0443\u043d\u043a\u0442\u044b \u043c\u0435\u043d\u044e, \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438 \u043f\u0440\u043e\u0447\u0438\u0435. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0440\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b &#8212; \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u043a\u0441\u0442\u0430, \u0430\u043d\u0430\u043b\u0438\u0437 \u043f\u043b\u0430\u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0441\u0430\u0439\u0442\u0430:<\/p>\n<pre><code class=\"json\">\"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\"   } ]<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u0432\u0430 \u043f\u0443\u043d\u043a\u0442\u0430 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 &#8212; <code>Format SQL<\/code> \u0438 <code>Explain Analyze<\/code>,  \u0430 \u0442\u0430\u043a\u0436\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u0430\u0439\u0442\u0430 \u043d\u0430 \u0442\u0438\u0442\u0443\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438, \u0443\u0441\u043b\u043e\u0432\u0438\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u044f\u0437\u044b\u043a SQL, \u0430 \u0433\u0440\u0443\u043f\u043f\u0443 &#8212; <code>navigation<\/code> \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0432 \u0442\u043e\u043f\u0435 \u0438 <code>1_modification<\/code> \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u0435\u0440\u0432\u044b\u043c\u0438 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435:<\/p>\n<pre><code class=\"json\">\"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\"     }   ] }<\/code><\/pre>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0445\u0440\u0430\u043d\u0438\u043c \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 URL \u0441\u0430\u0439\u0442\u0430:<\/p>\n<pre><code class=\"json\">\"configuration\": {   \"type\": \"object\",   \"title\": \"Explain PostgreSQL\",   \"properties\": {     \"explain-postgresql.url\": {       \"scope\": \"resource\",       \"type\": \"string\",       \"default\": \"https:\/\/explain.tensor.ru\",       \"description\": \"API site\",       \"order\": 1     }   } }<\/code><\/pre>\n<p>\u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e:<\/p>\n<pre><code class=\"typescript\">const url = vscode.workspace.getConfiguration('explain-postgresql').get('url');<\/code><\/pre>\n<p>\u0412 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>main<\/code>  \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0437\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u0432 \u043f\u043b\u0430\u0433\u0438\u043d &#8212; \u0444\u0430\u0439\u043b <em>extension.js<\/em> , \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432\u0445\u043e\u0434\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>activate<\/code>. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043e\u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/vscode-api#ExtensionContext\">\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/a> , \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043b\u0430\u0433\u0438\u043d\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0443\u0442\u044c \u043a \u0435\u0433\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0443 <em>extensionUri<\/em> , \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0438 \u043a \u0438\u043a\u043e\u043d\u043a\u0435 \u043f\u0430\u043d\u0435\u043b\u0438:<\/p>\n<pre><code class=\"typescript\">panel.iconPath = vscode.Uri.joinPath(Context.extensionUri, \"icons\/EP.png\");<\/code><\/pre>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0434\u0430\u043b\u0438 \u0432 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435:<\/p>\n<pre><code class=\"typescript\">vscode.commands.registerCommand('explain-postgresql.formatsql', formatCommand); vscode.commands.registerCommand('explain-postgresql.explainanalyze', explainCommand); vscode.commands.registerCommand('explain-postgresql.explainsite', explainsiteCommand);<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u044b \u0431\u0435\u0440\u0435\u043c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u0443\u0447\u0430\u0441\u0442\u043e\u043a \u0438\u043b\u0438 \u0432\u0435\u0441\u044c \u0442\u0435\u043a\u0441\u0442, \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/explain.tensor.ru\/api-description\/\">API explain.tensor.ru<\/a> \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u0442\u0435\u043a\u0441\u0442:<\/p>\n<pre><code class=\"typescript\">export async function formatCommand(): Promise&lt;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); \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442             editor.edit(editBuilder => {                 editBuilder.replace(validFullRange || selection, formatted);             })         } catch (e) {             vscode.window.showErrorMessage(`${e}`, {modal: true});         }     } } <\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0431\u0435\u0440\u0435\u043c \u0442\u0435\u043a\u0441\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a \u043d\u0435\u043c\u0443 EXPLAIN \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c \u043e\u043f\u0446\u0438\u0439, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u0430. <br \/>\u0417\u0430\u0442\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>executeQuery<\/code> \u0438\u0437 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=mtxr.sqltools\">SQLTools<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u043b\u0430\u043d \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<br \/>\u0422\u0435\u043a\u0441\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0435\u0433\u043e \u043f\u043b\u0430\u043d\u043e\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0430 \u0430\u043d\u0430\u043b\u0438\u0437 \u0432 API explain.tensor.ru \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 \u043d\u043e\u0432\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438:<\/p>\n<pre><code class=\"typescript\">export async function explainCommand(): Promise&lt;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); \/\/ \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043b\u0430\u043d \u043d\u0430 \u0430\u043d\u0430\u043b\u0438\u0437 \u0432 API explain         const apiUrl = vscode.workspace.getConfiguration('explain-postgresql').get('url');         await createView(`${apiUrl}${url}`);     } catch(e) {         vscode.window.showErrorMessage(`${e}`, {modal: true});     } }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0430\u043d\u0435\u043b\u0438 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438 \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/code.visualstudio.com\/api\/extension-guides\/webview\">Webview<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c iframe , \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0442\u044c \u0432 \u043d\u0435\u043c \u0441\u0432\u043e\u0439 HTML-\u043a\u043e\u0434. <br \/>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043f\u0430\u043d\u0435\u043b\u044c \u0441\u0432\u043e\u0439 iframe, \u0432 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0435 src \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0443\u043a\u0430\u0436\u0435\u043c URL \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0430\u043d\u0430\u043b\u0438\u0437\u0430:<\/p>\n<pre><code class=\"typescript\">async function getHtml (url: string) {     return `&lt;!DOCTYPE html>&lt;html>&lt;head>&lt;\/head> &lt;body style=\"margin:0px;padding:0px;overflow:hidden\"> &lt;div>             &lt;iframe sandbox=\"allow-scripts allow-forms allow-same-origin allow-downloads\" src=${url} style=\"width:100vw;height:100vh;border: none;display: block;\">&lt;\/iframe> &lt;\/div> &lt;\/body>&lt;\/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><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f <code>Code Actions<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/code.visualstudio.com\/api\/references\/vscode-api#CodeActionProvider\">CodeActionProvider<\/a> :<\/p>\n<pre><code class=\"typescript\">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&lt;(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; } } <\/code><\/pre>\n<p>\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>activate<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438:<\/p>\n<pre><code class=\"typescript\">vscode.languages.registerCodeActionsProvider('sql', new Actions(), {     providedCodeActionKinds: Actions.providedCodeActionKinds })<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 <a href=\"https:\/\/code.visualstudio.com\/api\/references\/vscode-api#StatusBarItem\">StatusBarItem<\/a> \u0442\u0430\u043a\u0436\u0435 \u0432 <code>activate<\/code> :<\/p>\n<pre><code class=\"typescript\">const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10); statusBarItem.command = 'explain-postgresql.explainanalyze'; statusBarItem.text = 'Explain Analyze'; statusBarItem.show();<\/code><\/pre>\n<h4>\u041e\u0442\u043b\u0430\u0434\u043a\u0430<\/h4>\n<p>\u0414\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u043f\u0430\u043d\u0435\u043b\u0435\u0439 \u0443\u0434\u043e\u0431\u043d\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c WebView Developer Tools:<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c devtools<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\">\n<div><figcaption>\u043e\u0442\u043b\u0430\u0434\u043a\u0430 \u043f\u0430\u043d\u0435\u043b\u0438<\/figcaption><\/div>\n<\/figure>\n<h4>\u0421\u0431\u043e\u0440\u043a\u0430 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u0430<\/h4>\n<p>\u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u0443 <code>vsce<\/code> :<\/p>\n<pre><code class=\"bash\">npm install -g @vscode\/vsce<\/code><\/pre>\n<p>\u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0432\u0435\u0440\u0441\u0438\u044e \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>version<\/code> \u0432 <em>package.json<\/em> \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0431\u043e\u0440\u043a\u0443:<\/p>\n<pre><code class=\"bash\">vsce package  Packaged: explain-postgresql\/explain-postgresql-1.0.5.vsix (12 files, 319.64KB)<\/code><\/pre>\n<p><a href=\"https:\/\/marketplace.visualstudio.com\/vscode\">VS Code Extension Marketplace<\/a> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/azure.microsoft.com\/services\/devops\/\">Azure DevOps<\/a> , \u0430 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>vsce<\/code> \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u044d\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 <a href=\"https:\/\/dev.azure.com\">dev.azu<\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-371260","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/371260","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=371260"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/371260\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=371260"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=371260"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=371260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}