最近学习了一下如何写vscode插件,不得不感叹大神写的vscode框架就是厉害,简单通过配置文件加上事件处理代码就可以扩展编辑器前端的能力。膜拜之余,造了一个轮子,交互过程如下,右键json文件选择“json生成go结构体(JsonToGo)”就可以生成json文件对应的golang struct;选择“生成golang代码或者结构体->curl生成go代码(CurlToGo)”就可以从curl命令(从浏览器的debug tool直接copy过来)生成对应的golang客户端代码,简单修改即可发起http请求。
代码放在https://github.com/xiazemin/golangCodeHelper,由于没有自己的独立域名,暂时没有发布到vscode应用市场,感兴趣的小伙伴可以到github
下载下来,然后通过导入vsix文件的方式来安装插件,编译好的插件位于:https://github.com/xiazemin/golangCodeHelper/blob/main/golangCodeHelper-0.0.1.vsix,通过这个插件我们可以在本地做常用的代码生成,提升我们的开发效率。比如:
json文件:
代码语言:javascript复制{
"name": "golangCodeHelper",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "golangCodeHelper",
"version": "0.0.1",
"devDependencies": {
"@types/glob": "^8.0.1",
"@types/mocha": "^10.0.1",
可以生成
代码语言:javascript复制type AutoGenerated struct {
Name string `json:"name"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Version string `json:"version"`
Engines struct {
Vscode string `json:"vscode"`
} `json:"engines"`
Categories []string `json:"categories"`
ActivationEvents []string `json:"activationEvents"`
Main string `json:"main"`
Contributes struct {
Commands []struct {
Command string `json:"command"`
Title string `json:"title"`
Category string `json:"category,omitempty"`
} `json:"commands"`
在浏览器上复制下来的curl
代码语言:javascript复制curl 'https://ug.baidu.com/mcp/pc/pcsearch'
-H 'Accept: */*'
-H 'Accept-Language: zh-CN,zh;q=0.9'
-H 'Connection: keep-alive'
-H 'Content-Type: application/json'
-H 'Cookie: BIDUPSID=17AC3E8E8275F51ACFD6A4E37AC75966; PSTM=1676794732; BAIDUID=17AC3E8E8275F51AC7F6EA632EC051DD:FG=1; H_PS_PSSID=36555_38112_38092_38132_38116_38150_38175_38173_36802_37935_38088_26350_38119_38097_38008_37881; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BA_HECTOR=0485ag8g0g2k8ha1250g0hjm1hv49q51k; delPer=0; PSINO=5; BAIDUID_BFESS=17AC3E8E8275F51AC7F6EA632EC051DD:FG=1; ZFY=LzNgJDmgbcwFmDlSJY5RPCv4lgsx3Kzg2hlLesbqjdY:C'
-H 'Origin: https://www.baidu.com'
-H 'Referer: https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=content-type application/json&fenlei=256&oq=content-type%20application%2Fjson&rsv_pq=b58f78f900026b99&rsv_t=1445vFHiJ7o4mPEOrw4MQTAUMrxUQJFcddCoRqhH8Q3CjHgKQK8m4sCm2NQ&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_btype=t&inputT=95112&rsv_sug3=407&rsv_sug1=450&rsv_sug7=000&rsv_sug2=0&rsv_sug4=95250&rsv_sug=1'
-H 'Sec-Fetch-Dest: empty'
-H 'Sec-Fetch-Mode: cors'
-H 'Sec-Fetch-Site: same-site'
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
-H 'sec-ch-ua: "Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"'
-H 'sec-ch-ua-mobile: ?0'
-H 'sec-ch-ua-platform: "macOS"'
--data-raw '{"invoke_info":{"pos_1":[{}],"pos_2":[{}],"pos_3":[{}]}}'
--compressed
可以生成
代码语言:javascript复制
type Payload struct {
InvokeInfo InvokeInfo `json:"invoke_info"`
}
type Pos1 struct {
}
type Pos2 struct {
}
type Pos3 struct {
}
type InvokeInfo struct {
Pos1 []Pos1 `json:"pos_1"`
Pos2 []Pos2 `json:"pos_2"`
Pos3 []Pos3 `json:"pos_3"`
}
data := Payload {
// fill struct
}
payloadBytes, err := json.Marshal(data)
if err != nil {
// handle err
}
body := bytes.NewReader(payloadBytes)
req, err := http.NewRequest("POST", "https://ug.baidu.com/mcp/pc/pcsearch", body)
if err != nil {
// handle err
}
req.Header.Set("Accept", "*/*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Cookie", "BIDUPSID=17AC3E8E8275F51ACFD6A4E37AC75966; PSTM=1676794732; BAIDUID=17AC3E8E8275F51AC7F6EA632EC051DD:FG=1; H_PS_PSSID=36555_38112_38092_38132_38116_38150_38175_38173_36802_37935_38088_26350_38119_38097_38008_37881; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BA_HECTOR=0485ag8g0g2k8ha1250g0hjm1hv49q51k; delPer=0; PSINO=5; BAIDUID_BFESS=17AC3E8E8275F51AC7F6EA632EC051DD:FG=1; ZFY=LzNgJDmgbcwFmDlSJY5RPCv4lgsx3Kzg2hlLesbqjdY:C")
req.Header.Set("Origin", "https://www.baidu.com")
req.Header.Set("Referer", "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=content-type application/json&fenlei=256&oq=content-type%20application%2Fjson&rsv_pq=b58f78f900026b99&rsv_t=1445vFHiJ7o4mPEOrw4MQTAUMrxUQJFcddCoRqhH8Q3CjHgKQK8m4sCm2NQ&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_btype=t&inputT=95112&rsv_sug3=407&rsv_sug1=450&rsv_sug7=000&rsv_sug2=0&rsv_sug4=95250&rsv_sug=1")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-site")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
req.Header.Set("Sec-Ch-Ua", ""Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"")
req.Header.Set("Sec-Ch-Ua-Mobile", "?0")
req.Header.Set("Sec-Ch-Ua-Platform", ""macOS"")
resp, err := http.DefaultClient.Do(req)
if err != nil {
// handle err
}
defer resp.Body.Close()
下面详细介绍下插件开发的详细流程。首先安装node环境
代码语言:javascript复制% node -v
v18.0.0
% npm -v
8.6.0
然后安装脚手架和插件打包工具
代码语言:javascript复制npm install -g yo generator-code
npm install -g @vscode/vsce
通过脚手架生成项目框架
代码语言:javascript复制yo code
_-----_ ╭──────────────────────────╮
| | │ Welcome to the Visual │
|--(o)--| │ Studio Code Extension │
`---------´ │ generator! │
( _´U`_ ) ╰──────────────────────────╯
/___A___ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? What type of extension do you want to create? (Use arrow keys)
❯ New Extension (TypeScript)
New Extension (JavaScript)
New Color Theme
New Language Support
New Code Snippets
New Keymap
New Extension Pack
New Language Pack (Localization)
New Web Extension (TypeScript)
New Notebook Renderer (TypeScript)
? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? hello
? What's the identifier of your extension? hello
? What's the description of your extension? hello
? Initialize a git repository? No
? Bundle the source code with webpack? Yes
? Which package manager to use? npm
它生成的代码很简单,是一个最简单的模板,能够在vscode中输出vscode,它的核心代码是下面几行,首先看下package.json
代码语言:javascript复制 "activationEvents": [
"onCommand:hello.helloWorld"
],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "hello.helloWorld",
"title": "Hello World"
}
]
},
其中activationEvents,就是我们的插件和对应的命令,./dist/extension.js,是插件的入口文件,commands,后面就是对应的我们的命令。然后我们看下入口文件./dist/extension.js
代码语言:javascript复制// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "hello" is now active!');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
let disposable = vscode.commands.registerCommand('hello.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from hello1!');
});
context.subscriptions.push(disposable);
}
// This method is called when your extension is deactivated
export function deactivate() {}
它最核心的就是暴露了一个激活方法给vscode框架调用,在方法内部,通过vscode.commands.registerCommand,注册了需要执行的命令和对应的callback函数,并将命令发布到context, context.subscriptions.push(disposable);这样一个最简单的插件就制作完毕了。我们可以通过fn f5进行调试,没有问题后就可以通过
代码语言:javascript复制vsce package
进行打包生成对应的插件文件xxx.vsix,我们发布到应有市场然后搜索,或者直接导入插件文件,就可以使用我们的插件了。
熟悉完上述流程后,就可以开始制作自己的插件,首先在package.json里配置我们需要的命令和菜单。
代码语言:javascript复制"activationEvents": [
"onCommand:golangCodeHelper.helloWorld",
"onCommand:golangCodeHelper.JsonToGo",
"onCommand:golangCodeHelper.CurlToGo",
"onCommand:golangCodeHelper.CurlToGoStructs"
],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "golangCodeHelper.helloWorld",
"title": "golang编码辅助工具集"
},
{
"command": "golangCodeHelper.JsonToGo",
"title": "json生成go结构体(JsonToGo)",
"category": "golangCodeHelper"
},
{
"command": "golangCodeHelper.CurlToGo",
"title": "curl生成go代码(CurlToGo)",
"category": "生成golang代码或者结构体"
},
{
"command": "golangCodeHelper.CurlToGoStructs",
"title": "curl生成go结构体(CurlToGoStructs)",
"category": "生成golang代码或者结构体"
}
],
"menus": {
"explorer/context": [
{
"command": "golangCodeHelper.JsonToGo",
"when": "!explorerResourceIsFolder",
"group": "2_workspace@1"
},
{
"group": "2_workspace@2",
"when": "!explorerResourceIsFolder",
"submenu": "golangCodeHelper/submenu/generate"
}
],
"golangCodeHelper/submenu/generate": [
{
"command": "golangCodeHelper.CurlToGo",
"group": "1_generate@1"
},
{
"command": "golangCodeHelper.CurlToGoStructs",
"group": "1_generate@2"
}
]
},
"submenus": [
{
"id": "golangCodeHelper/submenu/generate",
"label": "生成golang代码或者结构体"
}
]
},
然后注册我们的命令执行函数并且发布
代码语言:javascript复制// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as nodepath from 'path';
const {jsonToGo} =require('./json-to-go/json-to-go.js');
const {curlToGo} =require('./curl-to-go/resources/js/curl-to-go.js');
const{curlToGoStruct}=require('./curl-to-gostruct/curl-to-gostruct.js')
/**
*
* @param path 写入的文件路径
* @param content 写入的文件内容
* @param fileName 写入的文件名
* @param fileNameExtra 当文件名存在于该文件夹下时的替代文件名
*/
const writeFile = (path: string, content: string, fileName?: string | undefined, fileNameExtra?: string | undefined) => {
let newfileName = fileName || 'json_to_go.go';
const opt = {
flag: 'wx' // 但是如果文件路径存在,则文件写入失败。覆盖写入:'w'
};
const exists: boolean = fs.existsSync(`${path}${nodepath.sep}${newfileName}`);
if (exists) {
newfileName = fileNameExtra || fileNameExtra '_副本.go';
}
console.log(`写入路径:${path}${nodepath.sep}${newfileName}`);
fs.writeFile(`${path}${nodepath.sep}${newfileName}`, content, opt, (err) => {
if (err) {
vscode.window.showErrorMessage(`写入${newfileName}失败,可能原因是,改文件夹下已存在${newfileName}`);
return;
}
vscode.window.showInformationMessage(`已生成一个示例${newfileName}`);
});
};
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "golangCodeHelper" is now active!');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
let disposable = vscode.commands.registerCommand('golangCodeHelper.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from golangCodeHelper!');
});
let JsonToGo = vscode.commands.registerCommand('golangCodeHelper.JsonToGo', (e) => {
vscode.window.showInformationMessage('JsonToGo from golangCodeHelper!');
const filename = e ? e?.fsPath?.split('/').pop() : vscode.window.activeTextEditor?.document.fileName.split('/').pop();
vscode.window.showInformationMessage(filename "->" e.fsPath);
var data=fs.readFileSync(e.fsPath,"utf-8");
vscode.window.showInformationMessage(data);
console.log(data)
console.log(jsonToGo)
const got = jsonToGo(data, null, null, false);
console.log(got)
vscode.window.showInformationMessage(got);
if (got.error) {
vscode.window.showInformationMessage(`JsonToGo from golangCodeHelper failed! ${got.error}`);
} else {
writeFile(nodepath.dirname(e.fsPath), got.go,'json-to-go.go');
}
});
let CurlToGo = vscode.commands.registerCommand('golangCodeHelper.CurlToGo', (e) => {
vscode.window.showInformationMessage('curlToGo from golangCodeHelper!');
const filename = e ? e?.fsPath?.split('/').pop() : vscode.window.activeTextEditor?.document.fileName.split('/').pop();
vscode.window.showInformationMessage(filename "->" e.fsPath);
var data=fs.readFileSync(e.fsPath,"utf-8");
vscode.window.showInformationMessage(data);
console.log(data)
console.log(curlToGo)
const got = curlToGo(data, null, null, false);
console.log(got)
vscode.window.showInformationMessage(got);
writeFile(nodepath.dirname(e.fsPath), got,"curl-to-go.go");
});
let CurlToGoStructs = vscode.commands.registerCommand('golangCodeHelper.CurlToGoStructs', (e) => {
vscode.window.showInformationMessage('CurlToGoStructs from golangCodeHelper!');
const filename = e ? e?.fsPath?.split('/').pop() : vscode.window.activeTextEditor?.document.fileName.split('/').pop();
vscode.window.showInformationMessage(filename "->" e.fsPath);
var data=fs.readFileSync(e.fsPath,"utf-8");
vscode.window.showInformationMessage(data);
console.log(data)
console.log(curlToGoStruct)
const got = curlToGoStruct(data, null, null, false);
console.log(got)
vscode.window.showInformationMessage(got);
writeFile(nodepath.dirname(e.fsPath), got,"curl-to-go-struct.go");
});
context.subscriptions.push(disposable);
context.subscriptions.push(JsonToGo);
context.subscriptions.push(CurlToGo);
context.subscriptions.push(CurlToGoStructs);
}
// This method is called when your extension is deactivated
export function deactivate() {}
至此,插件制作完毕,当然vscode还支持更为复杂的插件,可以参考其官方文档进行学习。