diff --git a/README.md b/README.md index 710f159..fa62c50 100644 --- a/README.md +++ b/README.md @@ -3,68 +3,99 @@ [![Version](https://img.shields.io/visual-studio-marketplace/v/alibabacloud-openapi.vscode-alicloud-api)](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.vscode-alicloud-api) [![Installs](https://img.shields.io/visual-studio-marketplace/i/alibabacloud-openapi.vscode-alicloud-api)](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.vscode-alicloud-api) [![Ratings](https://img.shields.io/visual-studio-marketplace/r/alibabacloud-openapi.vscode-alicloud-api)](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.vscode-alicloud-api) -![beta](https://img.shields.io/badge/beta-version) -Alibaba Cloud API Toolkit 是一个轻量化的阿里云 API 工具,支持在 VS Code 中快速查阅阿里云产品的 API. +The Alibaba Cloud API Toolkit for VSCode makes it easier to access Alibaba Cloud services. -简体中文 | [English](./README.en-US.md) +## Features +* **Alicloud Product Subscription:** You can search for Alicloud products and subscribe to its APIs you want to use. -## 功能 +* **API Searching:** You can search the Open API of Alibaba Cloud products to which you are subscribed. -* **阿里云产品订阅:** 插件提供了一键订阅阿里云产品的功能,你可以通过 `ctrl+cmd+k` 搜索和订阅阿里云产品。 +* **API Document View:** Clicking on an API can navigate you to a new tab which displays the corresponding API document, +including descriptions, request parameters, response parameters and error codes. -* **API 搜索:** 你可以通过 `ctrl+cmd+l` 搜索并查看已订阅的 API 文档。 - -* **API 文档预览:** 点击 API 可以打开一个新的标签页,并显示对应的 API 文档,包括描述、请求参数、响应参数和错误码。在文档中点击调试按钮,可以链接到阿里云 OpenAPI 门户进行在线 API 试用。 - -* **API 调试:** 你可以在插件中使用表单的方式调试阿里云 API,并查看结果。 +* **Call the API:** You can use the form to call the Alibaba Cloud API and see the response. -* **SDK 代码示例:** 你可以在插件中获得 SDK 代码示例,并在编辑器中快速打开对应的 SDK 代码。 +* **SDK Code Sample:** You can get the SDK code samples and quickly open the corresponding SDK code in VS Code. -* 更多功能正在开发中,敬请期待... +* **Code Snippets:** You can use the code snippets to quickly generate the code. -## 插件 UI 导览 +* More features are under development, please stay tuned. -![VSCode Extension Guide](https://img.alicdn.com/imgextra/i1/O1CN01o9s4TT1GTq3oggW7K_!!6000000000624-0-tps-2456-1446.jpg) +## Extension UI Guide - * 产品搜索 `ctrl+cmd+k` +![VSCode Extension Guide](https://img.alicdn.com/imgextra/i1/O1CN01rOmqbK1pOoGtdWxFO_!!6000000005351-0-tps-2462-1440.jpg) +### Product Searching `ctrl+cmd+k` + ![Product Searching](https://img.alicdn.com/imgextra/i1/O1CN01bcJ5DM1RpmnlOjDHK_!!6000000002161-0-tps-1202-798.jpg) - - * API 搜索 `ctrl+cmd+l` + +### API Searching `ctrl+cmd+l` ![API Serching](https://img.alicdn.com/imgextra/i1/O1CN01KaWkBF1UfCUkY0N3v_!!6000000002544-0-tps-1286-518.jpg) -* API 调试 - -调试功能需要配置你的 AK/SK 信息,配置方法如下: -1. 安装 [Alibaba Cloud CLI Tools](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.aliyuncli) 插件, -2. 打开命令行安装 aliyun-cli `brew install aliyun-cli`, -3. 输入 `aliyun configure` 命令,按照提示进行配置, -4. 点击 VS Code 状态栏中的阿里云图标,管理你的 profiles, -5. 更多信息请参考 [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure)。 +### Call the API + +The feature requires you to configure your AK/SK information as follows: +1. Install [Alibaba Cloud CLI Tools](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.aliyuncli) extentions. +2. Install [aliyun-cli](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#installation). +3. You can run the `aliyun configure` command for quick configuration. +``` +$ aliyun configure +Configuring profile 'default' ... +Aliyun Access Key ID [None]: +Aliyun Access Key Secret [None]: +Default Region Id [None]: cn-hangzhou +Default output format [json]: json +Default Language [zh]: zh +``` +4. Click the Alibaba Cloud icon in VS Code status bar to manage your profiles. +![](https://img.alicdn.com/imgextra/i1/O1CN0144NU9N1L4G1cq89Uf_!!6000000001245-0-tps-248-46.jpg) +![](https://img.alicdn.com/imgextra/i2/O1CN01btLUkc1ldEHJQ0w4S_!!6000000004841-0-tps-1206-190.jpg) +5. More information please refer to the [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure). ![API debug](https://img.alicdn.com/imgextra/i4/O1CN01F1qI7S1BunIFJPiAt_!!6000000000006-0-tps-2618-2050.jpg) -* SDK 示例 - +### SDK Code Sample +You can generate the SDK Sample by using the debugger form and then open it in your editor. + ![sdk demo](https://img.alicdn.com/imgextra/i1/O1CN01GVhWTl1waRdYmCn7E_!!6000000006324-0-tps-2630-2038.jpg) +### Code Snippets +You can use the code snippets to quickly generate the code. + +Type your subscribed API into the editor and select the API snippet you want to generate. + +![code snippets](https://img.alicdn.com/imgextra/i3/O1CN01iKQA6u1KWMiVttyH0_!!6000000001171-1-tps-915-442.gif) +Or use the keyboard shortcuts `ctrl+cmd+l` to search the API and select Insert snippets. + +![code snippets](https://img.alicdn.com/imgextra/i3/O1CN01dmGwmX1ZyVHozyKx4_!!6000000003263-1-tps-842-468.gif) + +## Feedback +Submit bug reports and feature requests on [our Github repository](https://github.com/aliyun/alibabacloud-api-vscode-toolkit/issues) +. + ## Requirements - Need VS Code 1.75.0 or above. ## Release Notes +### 0.0.6 +- Surpport code snippets. + +### 0.0.5 +- Added aliyun-cli installation instructions. + +### 0.0.3 +- Support API calling. +- Support SDK code sample. + ### 0.0.1 Initial release - Support product searching and subscription. - Support API searching. - Support API Document View. -### 0.0.3 -- Support API calling. -- Support SDK code sample. - ## License See the [Apache License 2.0](./LICENSE). \ No newline at end of file diff --git a/README.zh_CN.md b/README.zh_CN.md new file mode 100644 index 0000000..bb863c0 --- /dev/null +++ b/README.zh_CN.md @@ -0,0 +1,104 @@ +# Alibaba Cloud API Toolkit + +[![Version](https://img.shields.io/visual-studio-marketplace/v/alibabacloud-openapi.vscode-alicloud-api)](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.vscode-alicloud-api) +[![Installs](https://img.shields.io/visual-studio-marketplace/i/alibabacloud-openapi.vscode-alicloud-api)](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.vscode-alicloud-api) +[![Ratings](https://img.shields.io/visual-studio-marketplace/r/alibabacloud-openapi.vscode-alicloud-api)](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.vscode-alicloud-api) +![beta](https://img.shields.io/badge/beta-version) + +Alibaba Cloud API Toolkit 是一个轻量化的阿里云 API 工具,支持在 VS Code 中快速查阅阿里云产品的 API. + +简体中文 | [English](./README.md) + +## 功能 + +* **阿里云产品订阅:** 插件提供了一键订阅阿里云产品的功能,你可以通过 `ctrl+cmd+k` 搜索和订阅阿里云产品。 + +* **API 搜索:** 你可以通过 `ctrl+cmd+l` 搜索并查看已订阅的 API 文档。 + +* **API 文档预览:** 点击 API 可以打开一个新的标签页,并显示对应的 API 文档,包括描述、请求参数、响应参数和错误码。 + +* **API 调试:** 你可以在插件中使用表单的方式调试阿里云 API,并查看结果。 + +* **SDK 代码示例:** 你可以在插件中获得 SDK 代码示例,并在编辑器中快速打开对应的 SDK 代码。 + +* **Code Snippets:** 支持代码片段功能,帮助你快速编写 SDK 代码。 + +* 更多功能正在开发中,敬请期待... + +## 插件 UI 导览 + +![VSCode Extension Guide](https://img.alicdn.com/imgextra/i1/O1CN01o9s4TT1GTq3oggW7K_!!6000000000624-0-tps-2456-1446.jpg) + +### 产品搜索 `ctrl+cmd+k` + +![Product Searching](https://img.alicdn.com/imgextra/i1/O1CN01bcJ5DM1RpmnlOjDHK_!!6000000002161-0-tps-1202-798.jpg) + +### API 搜索 `ctrl+cmd+l` + +![API Serching](https://img.alicdn.com/imgextra/i1/O1CN01KaWkBF1UfCUkY0N3v_!!6000000002544-0-tps-1286-518.jpg) + +### API 调试 + +调试功能需要配置你的 AK/SK 信息,配置方法如下: +1. 安装 [Alibaba Cloud CLI Tools](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.aliyuncli) 插件, +2. 打开命令行安装 aliyun-cli `brew install aliyun-cli`, +3. 输入 `aliyun configure` 命令,按照提示进行配置, +``` +$ aliyun configure +Configuring profile 'default' ... +Aliyun Access Key ID [None]: +Aliyun Access Key Secret [None]: +Default Region Id [None]: cn-hangzhou +Default output format [json]: json +Default Language [zh]: zh +``` +4. 点击 VS Code 状态栏中的阿里云图标,管理你的 profiles, +![](https://img.alicdn.com/imgextra/i1/O1CN0144NU9N1L4G1cq89Uf_!!6000000001245-0-tps-248-46.jpg) +![](https://img.alicdn.com/imgextra/i2/O1CN01btLUkc1ldEHJQ0w4S_!!6000000004841-0-tps-1206-190.jpg) +1. 更多信息请参考 [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure)。 + +![API debug](https://img.alicdn.com/imgextra/i4/O1CN01F1qI7S1BunIFJPiAt_!!6000000000006-0-tps-2618-2050.jpg) + +### SDK 示例 +你能够通过调试表单生成 SDK 代码示例,并在编辑器中打开对应的 SDK 代码。 + +![sdk demo](https://img.alicdn.com/imgextra/i1/O1CN01GVhWTl1waRdYmCn7E_!!6000000006324-0-tps-2630-2038.jpg) + +### Code Snippets +你能通过代码片段快速生成 SDK 代码示例。 + +在编辑器输入你已订阅的 API 即可搜索到对应 API 的代码段。 + +![code snippets](https://img.alicdn.com/imgextra/i3/O1CN01iKQA6u1KWMiVttyH0_!!6000000001171-1-tps-915-442.gif) + +或者你也可以通过快捷键 `ctrl+cmd+l` 搜索 API,然后选择插入代码段。 + +![code snippets](https://img.alicdn.com/imgextra/i3/O1CN01dmGwmX1ZyVHozyKx4_!!6000000003263-1-tps-842-468.gif) + +## 反馈 +欢迎在我们的 [Github repository](https://github.com/aliyun/alibabacloud-api-vscode-toolkit/issues) 上提交你的问题和建议。 + +## Requirements +- Need VS Code 1.75.0 or above. + +## Release Notes + +### 0.0.6 +- Surpport code snippets. + +### 0.0.5 +- Added aliyun-cli installation instructions. + +### 0.0.3 +- Support API calling. +- Support SDK code sample. + +### 0.0.1 +Initial release +- Support product searching and subscription. +- Support API searching. +- Support API Document View. + +## License + +See the [Apache License 2.0](./LICENSE). \ No newline at end of file diff --git a/package.json b/package.json index 0339229..b003d1d 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Alibaba Cloud API Toolkit", "description": "The Alibaba Cloud API Toolkit for VSCode makes it easier to access Alibaba Cloud services.", "author": "Alibaba Cloud SDK Team", - "version": "0.0.4", + "version": "0.0.5", "private": true, "publisher": "alibabacloud-openapi", "license": "Apache-2.0", @@ -20,6 +20,10 @@ "main": "./dist/extension", "contributes": { "commands": [ + { + "command": "alicloud.api.autoImport", + "title": "导入依赖" + }, { "command": "alicloud.api.githubIssue", "title": "feedback", @@ -59,6 +63,16 @@ "icon": "$(add)", "title": "订阅更多产品" }, + { + "command": "alicloud.api.searchProducts", + "icon": "$(search)", + "title": "搜索产品并订阅" + }, + { + "command": "alicloud.api.addSubscription", + "icon": "$(add)", + "title": "订阅产品" + }, { "command": "alicloud.api.removeSubscriptions", "icon": "$(notebook-delete-cell)", @@ -163,6 +177,16 @@ } ], "view/item/context": [ + { + "command": "alicloud.api.addSubscription", + "when": "view == alicloudApiExplorer && viewItem == PRODUCT", + "group": "inline" + }, + { + "command": "alicloud.api.searchProducts", + "when": "view == alicloudApiExplorer && viewItem == alicloudProducts", + "group": "inline" + }, { "command": "alicloud.api.addSubscriptions", "when": "view == alicloudApiExplorer && viewItem == alicloudAPISubscriptions", diff --git a/resources/product.svg b/resources/product.svg new file mode 100644 index 0000000..397e497 --- /dev/null +++ b/resources/product.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/commands.ts b/src/commands.ts index c1e021a..570c55a 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -7,15 +7,17 @@ import { alicloudAPIMessageService } from "./Service"; import { PontSpec } from "pontx-spec"; import * as fs from "fs-extra"; import { Product } from "./types"; +import { codeSampleProvider } from "./plugins/generate"; +import { generateImport } from "./common/generateImport"; const path = require("path"); -export const insertCode = (code: string) => { +export const insertCode = (code: string, insertPosition?) => { const editor = vscode.window.activeTextEditor; if (editor) { editor.edit((builder) => { if (editor.selection.isEmpty) { - const position = editor.selection.active; + const position = insertPosition ? insertPosition : editor.selection.active; builder.insert(position, code); } else { @@ -52,7 +54,7 @@ export class AlicloudApiCommands { description: `${product.description || ""}`, versions: product.versions?.map((version) => { return { - label: version, + label: `选择 API 版本: ${version}`, key: version, description: version === product.defaultVersion ? "推荐版本" : "", } as vscode.QuickPickItem; @@ -64,7 +66,7 @@ export class AlicloudApiCommands { const service = alicloudAPIMessageService; vscode.commands.registerCommand("alicloud.api.githubIssue", async () => { - vscode.env.openExternal(vscode.Uri.parse('https://github.com/aliyun/alibabacloud-api-vscode-toolkit/issues')); + vscode.env.openExternal(vscode.Uri.parse("https://github.com/aliyun/alibabacloud-api-vscode-toolkit/issues")); }); vscode.commands.registerCommand("alicloud.api.findInterface", () => { @@ -98,16 +100,56 @@ export class AlicloudApiCommands { const apiMeta = pontSpec?.apis[apiName]; - Promise.resolve(service.pontManager.innerManagerConfig.plugins.generate?.instance).then((generatePlugin) => { - vscode.commands.executeCommand("alicloud.api.openDocument", { - specName, - modName, - name: apiName, - spec: apiMeta, - pageType: "document", - schemaType: "api", - }); - }); + Promise.resolve(service.pontManager.innerManagerConfig.plugins.generate?.instance).then( + async (generatePlugin) => { + const snippets = await codeSampleProvider({ + language: vscode.window.activeTextEditor?.document.languageId || "typescript", + product: specName?.split("::")[0], + version: specName?.split("::")[1], + apiName: apiName, + simplify: true, + }); + const VIEW_API_DOC_ID = "VSCODE_PONTX_SHOW_PICK_ITEM_VIEW_API_DOC"; + let pickItems = [ + { + label: "查看文档", + id: VIEW_API_DOC_ID, + }, + ]; + if (snippets?.length && vscode.window.activeTextEditor) { + pickItems = [ + ...pickItems, + ...snippets.map((snippet) => { + return { + label: "插入代码段: " + snippet.name, + id: snippet.name, + description: snippet.description, + }; + }), + ]; + } + return vscode.window + .showQuickPick(pickItems, { + matchOnDescription: true, + matchOnDetail: true, + }) + .then((snippet) => { + const foundSnippet = snippets.find((inst) => inst.name === snippet?.id); + if (foundSnippet) { + insertCode(foundSnippet.code); + } else if (snippet.id === VIEW_API_DOC_ID) { + vscode.commands.executeCommand("alicloud.api.openDocument", { + specName, + modName, + name: apiName, + spec: apiMeta, + pageType: "document", + schemaType: "api", + }); + } + }); + }, + ); }); }); @@ -169,6 +211,15 @@ export class AlicloudApiCommands { // }); // } // }); + vscode.commands.registerCommand("alicloud.api.autoImport", (...argus) => { + const diagnostic = argus[0]; + const missingDep = argus[1]; + const range = argus[2]; + if (diagnostic.message === "importLists") { + const importStr = generateImport(missingDep, range); + insertCode(importStr, new vscode.Position(0, 0)); + } + }); vscode.commands.registerCommand("alicloud.api.fetchRemote", (config) => { const pontManager = service.pontManager; diff --git a/src/common/generateImport.ts b/src/common/generateImport.ts new file mode 100644 index 0000000..9d67a5b --- /dev/null +++ b/src/common/generateImport.ts @@ -0,0 +1,69 @@ +import * as vscode from "vscode"; + +export const makeJavaImportList = () => { + return { + "java":`import com.aliyun.tea.*;` + }; +}; + +export const makeJavaAsyncImportList = () => { + const importParts = { + Credential: `import com.aliyun.auth.credentials.Credential;\n`, + StaticCredentialProvider: `import com.aliyun.auth.credentials.provider.StaticCredentialProvider;\n`, + Gson: `import com.google.gson.Gson;\n`, + ClientOverrideConfiguration: `import darabonba.core.client.ClientOverrideConfiguration;\n`, + CompletableFuture: `import java.util.concurrent.CompletableFuture;\n`, + }; + return importParts; + }; + +export const makeTSImportList = () => { + const importParts = { + OpenApi: `import OpenApi, * as $OpenApi from '@alicloud/openapi-client';\n`, + $OpenApi: `import OpenApi, * as $OpenApi from '@alicloud/openapi-client';\n`, + Util: `import Util, * as $Util from '@alicloud/tea-util';\n`, + $Util: `import Util, * as $Util from '@alicloud/tea-util';\n`, + $tea: `import * as $tea from '@alicloud/tea-typescript';\n`, + }; + return importParts; +}; + +export const makePyImportList = () => { + const importParts = { + os: `import os\n`, + sys: `import sys\n`, + open_api_models: `from alibabacloud_tea_openapi import models as open_api_models\n`, + util_models: `from alibabacloud_tea_util import models as util_models\n`, + UtilClient: `from alibabacloud_tea_util.client import Client as UtilClient\n`, + }; + return importParts; +}; + +export const generateImport = (missingDep: string, rang: vscode.Range): string => { + const language = vscode.window.activeTextEditor?.document.languageId; + let dep = missingDep; + if (missingDep?.includes("$")) { + dep = dep.replace("$", ""); + } + switch (language) { + case "typescript": + return makeTSImportList()[dep] || `import ${dep}, * as $${dep} from '@alicloud/${dep}';\n`; + case "python": + return makePyImportList()[dep]; + default: + return ""; + } +}; + + +export const getDepsByLanguage = (dep, rang: vscode.Range) => { + const language = vscode.window.activeTextEditor?.document.languageId; + switch (language) { + case "typescript": + return Object.keys(makeTSImportList() || []); + case "python": + return Object.keys(makePyImportList() || []); + default: + return []; + } +}; diff --git a/src/explorer.ts b/src/explorer.ts index ad55208..4b3e725 100644 --- a/src/explorer.ts +++ b/src/explorer.ts @@ -8,6 +8,7 @@ import { findAlicloudAPIConfig, plugins, VSCodeLogger } from "./utils"; import { AlicloudApiCommands } from "./commands"; import { getProductRequestInstance } from "./productExplorer"; import { Product } from "./types"; +import _ from "lodash"; type DiffResult = T; @@ -20,6 +21,30 @@ export class PontAPITreeItem extends vscode.TreeItem { } export class PontAPIExplorer { + static getProductItems(products:Array, element:PontAPITreeItem = null){ + return products?.map(product=>{ + return { + specName: product.code, + modName: "", + contextValue: "PRODUCT", + label: `${product.name}`, + iconPath: vscode.Uri.joinPath(alicloudAPIMessageService.context.extensionUri, `resources/product.svg`), + description: product.code, + collapsibleState: vscode.TreeItemCollapsibleState.None, + command: { + command: "alicloud.api.addSubscription", + title: "subscribe", + arguments: [ + { + specName: product.code, + modName: "clickItem", + label: product.name + }, + ], + }, + }; + }) + } static getDirItems(spec: PontSpec, element = null) { const dirs = element?.children || spec?.ext?.directories || []; @@ -211,6 +236,19 @@ export class AlicloudApiExplorer implements vscode.TreeDataProvider { + if(element.contextValue === "alicloudProducts"){ + const productExplorer = getProductRequestInstance(); + const productGroups = _.groupBy(productExplorer?.products, (product) => product.group) + return (Object.keys(productGroups || {}))?.map((group) => { + return { + specName: group, + contextValue: 'productGroup', + label: `${group}`, + modName: group, + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + }; + }); + } if (element.contextValue === "alicloudAPISubscriptions") { const hasSingleSpec = this.pontManager.localPontSpecs?.length <= 1 && !this.pontManager.localPontSpecs[0]?.name; if (hasSingleSpec) { @@ -227,7 +265,7 @@ export class AlicloudApiExplorer implements vscode.TreeDataProvider product.group) + return PontAPIExplorer.getProductItems(productGroups[element?.modName?.toString()],element) + } + else { return PontAPIExplorer.getDirItems(spec); } } @@ -366,6 +409,30 @@ export class AlicloudApiExplorer implements vscode.TreeDataProvider{ + if (element.modName === 'clickItem') { + // 取消订阅 + let result = await vscode.window.showInformationMessage(`是否订阅${element.label}?`, "Yes", "No"); + if (result === "No") { + return + } + } + const productExplorer = getProductRequestInstance() + const selectedProduct = productExplorer?.products?.find((item) => item?.code === element?.specName); + const pickItem = AlicloudApiCommands.getPickProductItems(selectedProduct) + if (pickItem.versions?.length > 1) { + vscode.window + .showQuickPick(pickItem.versions, { + matchOnDescription: true, + matchOnDetail: true, + }) + .then(async (version: any) => { + this.subscribeProduct(pickItem.code, version.key); + }); + } else { + this.subscribeProduct(pickItem.code, (pickItem.versions[0] as any).key); + } + }) vscode.commands.registerCommand("alicloud.api.addSubscriptions", async () => { // 搜索+订阅功能 const productExplorer = getProductRequestInstance() @@ -397,6 +464,38 @@ export class AlicloudApiExplorer implements vscode.TreeDataProvider { + // 搜索+订阅功能 + const productExplorer = getProductRequestInstance() + const items = productExplorer?.products?.map((item) => { + return AlicloudApiCommands.getPickProductItems(item); + }) + .reduce((pre, next) => pre.concat(next), []); + + return vscode.window + .showQuickPick(items, { + matchOnDescription: true, + matchOnDetail: true, + }) + .then(async (item: any) => { + if (!item) { + return; + } + if (item.versions?.length > 1) { + vscode.window + .showQuickPick(item.versions, { + matchOnDescription: true, + matchOnDetail: true, + }) + .then(async (version: any) => { + this.subscribeProduct(item.code, version.key); + }); + } else { + this.subscribeProduct(item.code, item.versions[0].key); + } + }); + }); vscode.commands.registerCommand("alicloud.api.removeSubscriptions", async (meta) => { if (meta.specName) { @@ -565,6 +664,12 @@ export class AlicloudApiExplorer implements vscode.TreeDataProvider { if (!element) { return [ + { + label: "阿里云产品", + contextValue: "alicloudProducts", + resourceUri: vscode.Uri.parse(`pontx-manager://manager`), + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + }, { label: "我的订阅", contextValue: "alicloudAPISubscriptions", diff --git a/src/extension.ts b/src/extension.ts index e32737f..947c9ec 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,14 +5,15 @@ import * as vscode from "vscode"; import { PontManager } from "pontx-manager"; import PontMetaFetchPlugin from "pontx-meta-fetch-plugin"; import { alicloudAPIMessageService } from "./Service"; -import { findAlicloudAPIConfig, pontConsole, registerConfigSchema, VSCodeLogger } from "./utils"; +import { findAlicloudAPIConfig, pontConsole, VSCodeLogger } from "./utils"; import { AlicloudAPISerializer, AlicloudAPIWebview } from "./webview"; import { PontFileDecoration } from "./explorer"; -import * as path from "path"; import { AlicloudAPIPontParserPlugin } from "./plugins/parser"; import { AlicloudApiMetaGeneratePlugin } from "./plugins/generate"; import { getProductRequestInstance } from "./productExplorer"; +import autoCompletion from './provider/autoCompletion' +import autofix from "./provider/autofix"; export async function activate(context: vscode.ExtensionContext) { // if (!vscode.workspace.rootPath) { @@ -25,7 +26,7 @@ export async function activate(context: vscode.ExtensionContext) { return; } - // 需要先拿一次产品列表 + // 获取产品列表 await getProductRequestInstance(); alicloudAPIMessageService.context = context; @@ -61,6 +62,10 @@ export async function activate(context: vscode.ExtensionContext) { ); // 将命令注册到执行上下文中 context.subscriptions.push(new PontFileDecoration()); + // 自动补全 + autoCompletion(context); + // 自动修复 + autofix(context); } } catch (e) { vscode.window.showErrorMessage(e.message); diff --git a/src/plugins/generate.ts b/src/plugins/generate.ts index 69d7f18..4a0d64c 100644 --- a/src/plugins/generate.ts +++ b/src/plugins/generate.ts @@ -2,6 +2,7 @@ import { getFilesBySpecs, snippetsProvider } from "pontx-sdk-plugin-core"; import { createPontxGeneratePlugin, SnippetsProvider, PontxGeneratorPlugin, GetFilesBySpecs } from "pontx-generate"; import { PontSpec } from "pontx-spec"; import { InnerOriginConfig, PontManager } from "pontx-manager"; +import { getRequiredParamsValue } from "../utils"; const mySnippetsProvider: SnippetsProvider = (info) => { return []; @@ -20,8 +21,43 @@ const myGetFilesBySpecs: GetFilesBySpecs = async (origins) => { return fileStructure; }; +const codeSampleProvider = async (info:{language:string,product:string,version:string,apiName:string,params?:any, simplify?:boolean}):Promise => { + const body = { + apiName: info?.apiName, + apiVersion: info?.version, + product: info?.product, + sdkType: "dara", + params: info?.params || getRequiredParamsValue(info?.product, info?.version, info?.apiName), + regionId: "regionId", + endpoint: "endpoint", + useCommon: false, + simplify: info?.simplify, + // language: item.label.toString().includes("(java-async)") ? "java-async" : language, + }; + + // 补全的 code 来源 + const makeCodeStr = await fetch(`https://api.aliyun.com/api/product/makeCode`, { + method: "post", + body: JSON.stringify(body), + headers: { "Content-Type": "application/json" }, + }).then((res) => res.text()); + const sdkDemos = JSON.parse(makeCodeStr); + + const asyncFetchedCodes = (Object.keys(sdkDemos?.data?.demoSdk || {}))?.map( + (key)=>{ + return { + name: key, + language: key, + code: sdkDemos?.data?.demoSdk[key]?.codeSample, + importList: sdkDemos?.data?.demoSdk[key]?.importList + } + } + ) + return asyncFetchedCodes || []; +} + export const AlicloudApiMetaGeneratePlugin: any = createPontxGeneratePlugin({ snippetsProvider: mySnippetsProvider, getFilesBySpecs: myGetFilesBySpecs, }); -export { getFilesBySpecs, snippetsProvider }; +export { getFilesBySpecs, snippetsProvider,codeSampleProvider }; diff --git a/src/provider/autoCompletion.ts b/src/provider/autoCompletion.ts new file mode 100644 index 0000000..2bebdd7 --- /dev/null +++ b/src/provider/autoCompletion.ts @@ -0,0 +1,105 @@ +import * as vscode from "vscode"; +import { alicloudAPIMessageService } from "../Service"; +import { AlicloudApiCommands } from "../commands"; +import _ from "lodash"; +import { fileSel } from "../utils"; +import { codeSampleProvider } from "../plugins/generate"; + +class CompletionItemProvider { + async provideCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + context: vscode.CompletionContext, + ) { + const language = vscode.window.activeTextEditor?.document.languageId; + const service = alicloudAPIMessageService; + const items = service.pontManager.localPontSpecs + .map((pontSpec) => { + return AlicloudApiCommands.getPickItems(pontSpec); + }) + .reduce((pre, next) => pre.concat(next), []); + + // 支持换行 代码从起始位置到输入位置 + const text = document.getText( + new vscode.Range(new vscode.Position(position.line, position.character - 1), position), + ); + + let completionItems = []; + + // 支持已订阅的API搜索 + if (items?.find((item) => item?.label?.toLocaleLowerCase()?.includes(text?.toLocaleLowerCase()))) { + const searchAPIs = items?.filter((item) => item?.label?.toLocaleLowerCase().includes(text?.toLocaleLowerCase())); + completionItems = searchAPIs?.map((api) => { + let completionItem = new vscode.CompletionItem(api.label, vscode.CompletionItemKind["Interface"]); + completionItem.detail = api.detail; + completionItem.documentation = api.description; + + // 代码替换位置,查找位置会同步应用 + completionItem.range = new vscode.Range( + new vscode.Position(position.line, position.character - text.length), + new vscode.Position(position.line, position.character), + ); + return completionItem; + }); + if (language === "java") { + const javaAsyncCompletionItems = searchAPIs?.map((api) => { + let javaAsyncCompletionItem = new vscode.CompletionItem( + api.label + "(java-async)", + vscode.CompletionItemKind["Interface"], + ); + javaAsyncCompletionItem.detail = api.detail; + javaAsyncCompletionItem.documentation = api.description; + + // 代码替换位置,查找位置会同步应用 + javaAsyncCompletionItem.range = new vscode.Range( + new vscode.Position(position.line, position.character - text.length), + new vscode.Position(position.line, position.character), + ); + return javaAsyncCompletionItem; + }); + completionItems = [...completionItems, ...javaAsyncCompletionItems]; + } + return completionItems; + } + } + + resolveCompletionItem?( + item: vscode.CompletionItem, + token: vscode.CancellationToken, + ): vscode.ProviderResult { + const language = vscode.window.activeTextEditor?.document.languageId; + const [product, versionAPI] = item?.detail?.split("::"); + const [version, apiName] = versionAPI?.split("/"); + let asyncFetchedCode = ""; + if (product && apiName && version) { + return new Promise(async (resolve) => { + const snippets = await codeSampleProvider({ + language: language, + product: product, + version: version, + apiName: apiName, + simplify:true + }); + + if (item.label.toString().includes("(java-async)")) { + asyncFetchedCode = snippets?.find((item) => item.language === "java-async")?.code; + } else if(snippets?.find((item) => item.language === language)){ + asyncFetchedCode = snippets?.find((item) => item.language === language)?.code; + }else{ + item.documentation = "暂不支持该语言的 SDK" + } + // 设置补全项的实际插入文本 + item.insertText = asyncFetchedCode; + resolve(item); + }); + } + return item; + } +} + +export default function autoCompletion(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.languages.registerCompletionItemProvider(fileSel, new CompletionItemProvider(), "", "."), + ); +} diff --git a/src/provider/autofix.ts b/src/provider/autofix.ts new file mode 100644 index 0000000..a932c14 --- /dev/null +++ b/src/provider/autofix.ts @@ -0,0 +1,58 @@ +/** + * @description: Quick fix need to be used with a Linter + */ +import * as vscode from "vscode"; +import { containsAnySubstring, fileSel } from "../utils"; +import { getDepsByLanguage } from "../common/generateImport"; +import { alicloudAPIMessageService } from "../Service"; + +class CodeActionProvider { + provideCodeActions( + document: vscode.TextDocument, + _range: vscode.Range | vscode.Selection, + _context: vscode.CodeActionContext, + _token: vscode.CancellationToken, + ): vscode.ProviderResult { + // 拿到当前文档全部诊断信息 + const diagnostic: readonly vscode.Diagnostic[] = _context.diagnostics; + + let result: vscode.CodeAction[] = diagnostic?.map((item) => { + const editor = vscode.window.activeTextEditor; + const document = editor.document; + const errorText = document.getText(item?.range); + const service = alicloudAPIMessageService; + const products = service.pontManager.localPontSpecs + .map((pontSpec) => { + return pontSpec?.name?.split("::")[0]; + }) + + if (getDepsByLanguage(errorText, item.range)?.includes(errorText) || containsAnySubstring(errorText, products)) { + const autoImportAction = new vscode.CodeAction( + item.message + "导入依赖", + vscode.CodeActionKind.QuickFix, + ); + const newDiagnostic = new vscode.Diagnostic(item.range, "importLists", vscode.DiagnosticSeverity.Error); + // 自动修复命令注册 + autoImportAction.command = { + title: "导入依赖", + command: "alicloud.api.autoImport", + arguments: [newDiagnostic, errorText, item.range], + }; + return autoImportAction; + } + }); + + return result; + } +} + +class CodeActionProviderMetadata { + providedCodeActionKinds = [vscode.CodeActionKind.QuickFix]; +} + +export default function autofix(context: vscode.ExtensionContext) { + // 自动修复命令 + context.subscriptions.push( + vscode.languages.registerCodeActionsProvider(fileSel, new CodeActionProvider(), new CodeActionProviderMetadata()), + ); +} diff --git a/src/utils.ts b/src/utils.ts index 1c1b922..0045a0a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,6 +7,9 @@ import PontMetaFetchPlugin from "pontx-meta-fetch-plugin"; import { AlicloudAPIPontParserPlugin } from "./plugins/parser"; import { AlicloudApiMetaGeneratePlugin } from "./plugins/generate"; import _ from "lodash"; +import { alicloudAPIMessageService } from "./Service"; +import { AlicloudApiCommands } from "./commands"; +import { AlicloudApiExplorer } from "./explorer"; const configSchema = require("pontx-spec/configSchema.json"); const { createServerContent } = require("../media/lib/index"); @@ -430,3 +433,53 @@ export const getFormatValues = (paramValues: any, apiParams, purpose?: string) = travelObj(newAPIParamValues); return newAPIParamValues; }; + +export const getDefaultValue = (schema) => { + switch(schema?.type){ + case 'string' : return ""; + case 'number' : return 0; + case 'boolean': return false; + case 'array': return []; + case 'object': return {}; + default: return ""; + } +} + +export const getRequiredParamsValue = (product:string,version:string, api:string) => { + const service = alicloudAPIMessageService; + let apis = []; + const selectSpec = service.pontManager.localPontSpecs?.find((spec) => spec.name === `${product}::${version}`); + const selectAPI = _.find(selectSpec?.apis || {}, item=>item.name === api); + + const paramsValue = {} + const requiredParams = selectAPI?.parameters?.map(param=>{ + if(param?.schema?.required){ + paramsValue[param.name] = param.schema?.example || getDefaultValue(param.schema) + return param + } + }) + + return paramsValue +} + +export const fileSel: vscode.DocumentSelector = [ + { scheme: "file", language: "typescript" }, + { scheme: "file", language: "go" }, + { scheme: "file", language: "java" }, + { scheme: "file", language: "csharp" }, + { scheme: "file", language: "python" }, + { scheme: "file", language: "php" }, +]; + + export const containsAnySubstring = (targetStr, substrings) => { + const language = vscode.window.activeTextEditor?.document.languageId; + if(language !== "typescript"){ + return false + } + for (let i = 0; i < substrings.length; i++) { + if (targetStr.includes(substrings[i])) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/webview.ts b/src/webview.ts index f4bb43a..fb96ad9 100644 --- a/src/webview.ts +++ b/src/webview.ts @@ -46,7 +46,15 @@ export class AlicloudAPIWebview { openTab(extensionUri: vscode.Uri, panelConfig: PanelConfig) { const panelKey = getPanelKey(panelConfig); - const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; + const activeEditor = vscode.window.activeTextEditor; + const getViewColumn = () =>{ + if (activeEditor && vscode.window.visibleTextEditors.length > 1) { + return activeEditor.viewColumn; + } else { + return vscode.ViewColumn.Two; + } + } + const column = getViewColumn(); let webview = AlicloudAPIWebview.webviewPanels[panelKey]; // If we already have a panel, show it.