VSCode提供了丰富的[API](https://code.visualstudio.com/api/references/vscode-api),可以借助编辑器扩展许多定制功能。
  本次研发了一款名为[Search Method](https://github.com/pwstrick/search-method)的插件,在此记录整个研发过程。
## 一、准备工作
**1)安装环境**
  首先是全局安装 yo 和 generator-code 两个库,我本地全局安装了[cnpm](https://npmmirror.com/),所以用它来安装。
~~~
npm install yo generator-code -g
~~~
:-: ![](https://img.kancloud.cn/e4/3c/e43c1a3723e0bf60d852b376b7848677_2174x856.png =800x)
  然后使用 yo 命令初始化插件项目。
~~~
yo code
~~~
  回答些问题,但如果在回答 Initialize a git repository 时选择 yes,那么就会出现报错。
:-: ![](https://img.kancloud.cn/4f/71/4f71d7d7af77f5bad7cb80422123147b_2474x858.png =800x)
  选择 no 之后,才会提示安装成功,这个莫名其妙的问题就处理了好久。
:-: ![](https://img.kancloud.cn/a1/30/a130361f1a0a100fbd5fdb1462fe0ea2_1060x1314.png =400x)
  初始化成功后,就会得到一个项目结构(大致如下),建议单独打开个窗口调试插件。
~~~
|-- test
|-- extension.js // 插件入口文件,插件的逻辑在此完成
|-- CHANGELOG.md
|-- package-lock.json
|-- package.json
|-- README.md // 插件说明 README,发布后会展示
|-- jsconfig.json
|-- .eslintrc.json
|-- vsc-extension-quickstart.md
~~~
  最重要的就是 extension.js 和 package.json,前者会实现插件的核心功能,后者包括插件的配置信息。
**2)调试**
  选择 run =》 Start Debugging 后,就会自动弹出另一个 VSCode 窗口。
![](https://img.kancloud.cn/dd/61/dd619df8f77fb0bec732c2a36519f4b7_2118x840.png =800x)
  在这个窗口中,会默认安装上正在调试的插件,其实我本来起的插件名字叫 Search Function。
  但是在调试时,按下 Command + Shift + P 打开命令面板,却无法在此处找到默认的 Hello World 命令。
:-: ![](https://img.kancloud.cn/57/36/5736917f941c8ff38a9fb4852ab428fe_698x236.png =500x)
  直到我换了名字后,才能在调试时找到 Hello World,这个坑也花了我好几个小时。
  还有个问题,就是在在编辑器的 Debug Console 标签内无法看到打印信息,相当于是在盲调,电脑重启后,就能看到了,还是重启大法好。
:-: ![](https://img.kancloud.cn/b3/7e/b37e55bb6b2c93fec7e5b4823c9c15a4_1198x530.png =500x)
  至此,完成了整个准备工作。
## 二、开发过程
  公司有个 Node 项目,现在有个问题,就是 router 层的代码无法自动关联到 service 层的方法声明。
  下面这段代码存在于 router 层,common 存在于service 层,它有一个 aggregation() 方法。
  原先鼠标拖到方法处,按住 command 键,就能跳转到声明处,查看方法实现逻辑,但是现在无法跳转。
~~~
const data = await services.common.aggregation({ tableName });
~~~
  因为项目为了不用每次初始化 service 中的类,一下子全部都初始化好了,赋到一个对象中,如下所示。
~~~
Object.keys(dir).forEach((item) => {
services[item] = new dir[item](models);
});
~~~
  这次要开发的插件,其实就是为了解决此问题,提升大家的开发效率。
**1)最终效果**
  在经过多轮深思熟虑的设计之后,确定了要达到的效果,那就是先选中要查看的方法以及文件名称,然后右键找到 Search Services File 菜单,此时就能直接跳转过去了。
:-: ![](https://img.kancloud.cn/9c/a5/9ca5813f8acb7d23f2913e37e2b29df7_1217x765.png =500x)
**2)菜单配置**
  要想在右键显示这个自定义的菜单,需要在 package.json 中做些配置。
  commands 是默认就存在的,主要是 menus 字段,注册菜单。
~~~
"contributes": {
"commands": [
{
"command": "search-method.services",
"title": "Search Services File"
}
],
"menus": {
"editor/context": [
{
"command": "search-method.services",
"group": "navigation",
"when": "editorHasSelection"
}
]
}
},
~~~
  editor/context 是指编辑器上下文菜单,在[contributes.menus](https://code.visualstudio.com/api/references/contribution-points#contributes.menus)一栏中,还可以找到其余 menus 的关键字,可以都尝试下。
  editor/context 的值是一个数组,可以配置多个菜单,菜单中的 group 就是菜单所处的位置。
  navigation 就是最上面,还有 1\_modification,9\_cutcopypaste 和 z\_commands[参考下图](https://code.visualstudio.com/api/references/contribution-points#Sorting-of-groups)。
:-: ![](https://img.kancloud.cn/24/72/24727a7e9a6ae20899435159946e5038_624x271.png =400x)
  when 就是触发条件,editorHasSelection 就是指在编辑器中选中时触发。
  可选的关键字还可以是 editorFocus、inputFocus、editorHasMultipleSelections 等,参考[available contexts](https://code.visualstudio.com/api/references/when-clause-contexts#available-contexts)。
**3)核心逻辑**
  下面这段就是最精简的 extension.js 代码了,注册一个名为 search-method.services 的命令,核心功能会在此回调函数中实现。
~~~
const vscode = require('vscode');
function activate(context) {
const disposable = vscode.commands.registerCommand(
'search-method.services', (uri) => {
}
context.subscriptions.push(disposable);
}
function deactivate() {}
module.exports = {
activate,
deactivate
}
~~~
  在研发时,以为像下面这样就能直接得到 webMonitor.js 绝对目录,但其实此处读的是插件的根目录,而不是项目的。
~~~
path.resolve(__dirname, '../services/webMonitor.js')
~~~
  通过回调函数的参数 uri.fsPath 才能得到当前选中的代码所处的绝对位置。下面是完整的插件逻辑。
~~~
1 const vscode = require('vscode');
2 const path = require('path');
3 const fs = require('fs');
4 const { Uri, window, Position, Range, Selection } = vscode;
5 const disposable = vscode.commands.registerCommand(
6 "search-method.services",
7 (uri) => {
8 // 获取编辑器对象
9 const editor = window.activeTextEditor;
10 if (!editor) {
11 return;
12 }
13 // 当前选中的代码所处的绝对位置
14 const dirPath = uri.fsPath;
15 // services的绝对目录
16 const serviceDir = path.resolve(dirPath, "../../services");
17 // 获取选中文本
18 const doc = editor.document;
19 const selection = editor.selection;
20 const words = doc.getText(selection).split(".");
21 const serviceName = words[0];
22 const methodName = words.length > 1 ? words[1] : "";
23 // 列出目录中所有的文件
24 const files = fs.readdirSync(serviceDir);
25 for (const item of files) {
26 // 读取文件名称
27 const name = item.split(".")[0];
28 // 文件匹配
29 if (serviceName === name) {
30 const file = Uri.file(path.resolve(serviceDir, item));
31 // 根据换行符分隔字符串
32 const fileContentArr = fs
33 .readFileSync(path.resolve(serviceDir, item), "utf8")
34 .split(/\r?\n/);
35 // 声明的方法会有 async 关键字,或者通过空格和括号匹配
36 const index = fileContentArr.findIndex(
37 (element) =>
38 element.indexOf(`async ${methodName}`) >= 0 ||
39 element.indexOf(` ${methodName}(`) >= 0
40 );
41 // 跳转到指定行数的文件
42 window.showTextDocument(file).then((editor) => {
43 // 开始位置
44 const start = new Position(index, 0);
45 // 结束位置加了 20 行,为了便于查看
46 const end = new Position(index + 20, 0);
47 // 光标聚焦的位置
48 editor.selections = [new Selection(start, start)];
49 // 可见范围
50 const range = new Range(start, end);
51 editor.revealRange(range);
52 });
53 break;
54 }
55 }
56 }
57 );
~~~
  上面这段代码比较长,核心思路:
* 第13行,读取当前项目 services 的绝对路径。
* 第17行,获取选中的文本,例如 common.aggregation,然后分隔得到文件名和方法名。
* 第23行,列出 services 的所有文件,并与选中的文件名匹配。
* 第28行,匹配成功时读取文件内容并通过换行符分隔,查找方法所在的行。
* 第41行,通过[window.showTextDocument](https://code.visualstudio.com/api/references/vscode-api#window)打开[Uri.file()](https://code.visualstudio.com/api/references/vscode-api#Uri)处理过的文件,初始化[Position](https://code.visualstudio.com/api/references/vscode-api#Position)类,配置光标聚焦的位置([Selection](https://code.visualstudio.com/api/references/vscode-api#Selection))和可见范围([Range](https://code.visualstudio.com/api/references/vscode-api#Range))。
  虽然只有40多行代码,但花费了我一天的时间才完成,中间走了不少弯路,最麻烦的是跳转文件,showTextDocument() 方法也是偶然间才发现的。
  还有个小技巧,可以通过看 window、Uri 这些类的声明,就能了解到它们提供的功能。
## 三、对外发布
  为了能在 VSCode 的 Extensions 中被搜索到,还需要几个步骤。
**1)注册账号**
  首先到[Azure DevOps](https://dev.azure.com/pwstrick/) 创建管理账号,根据提示来就行了。
  然后选中 Personal access tokens,去创建 token。
:-: ![](https://img.kancloud.cn/cc/c7/ccc7416829c93c5a78b75e9b5ca56936_296x427.png =200x)
  接着在创建时,有些选项要注意,Organization 和 Scopes,网上说不能乱选,否则发布会不成功。创建后,记得自己将 token 保存一下,后面就无法查看了。
:-: ![](https://img.kancloud.cn/32/ed/32ed2febbd2ced35069a0bccd9a81418_619x372.png =400x)
  最后创建[发布账号](https://marketplace.visualstudio.com/manage),填些信息,下一步登录时使用。
**2)vsce**
  vsce 用于上传插件,首先全局安装。
~~~
npm i vsce -g
~~~
  然后是登录刚刚注册的发布账号,例如 vsce login pwstrick。
~~~
vsce login <publisher name>
~~~
  选好后会要求你输入之前申请的 token,登录成功后就会有下面的一段提示。
~~~
Personal Access Token for publisher 'pwstrick': ************************
The Personal Access Token verification succeeded for the publisher 'pwstrick'.
~~~
  此时,就可以输入发布命令了,成功的话,就会出现 DONE 的提示。
~~~
vsce publish
INFO Publishing 'pwstrick.search-method v0.0.1'...
INFO Extension URL (might take a few minutes): https://marketplace.visualstudio.com/items?itemName=pwstrick.search-method
INFO Hub URL: https://marketplace.visualstudio.com/manage/publishers/pwstrick/extensions/search-method/hub
DONE Published pwstrick.search-method v0.0.1.
~~~
  上传成功后,不会马上就能搜索到。
  在[插件管理](https://marketplace.visualstudio.com/manage/publishers/)中,当出现绿色的勾时,才表示插件发布完成,现在可以在应用市场搜索到了。
:-: ![](https://img.kancloud.cn/f5/3a/f53a5bf6cc480cd0b0772e3c962cb360_908x141.png =600x)
  可以看到并不是在第一行,需要往下翻一翻。
:-: ![](https://img.kancloud.cn/9f/d9/9fd94eb749b69654ce82ef2c590ca742_1996x1220.png =800x)
  在给组员使用时,发现他们不能安装,因为我设置的最低版本是 1.7.0,这个在开发的时候也需要注意。
~~~
"engines": {
"vscode": "^1.70.0"
},
~~~
参考:
[vscode插件开发指南(一)-理论篇](https://juejin.cn/post/6960626872791072798)
[vscode插件编写体验-右键菜单](https://segmentfault.com/a/1190000041672434)
[vscode window.showTextDocument示例](https://javascript.hotexamples.com/zh/examples/vscode/window/showTextDocument/javascript-window-showtextdocument-method-examples.html)
[编写一个VSCode插件](https://juejin.cn/post/7119095066810908679)
*****
> 原文出处:
[博客园-前端利器躬行记](https://www.cnblogs.com/strick/category/1472499.html)
[知乎专栏-前端利器躬行记](https://zhuanlan.zhihu.com/pwtool)
已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。
![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、扩展运算符和剩余参数
- 3、解构
- 4、模板字面量
- 5、对象字面量的扩展
- 6、Symbol
- 7、代码模块化
- 8、数字
- 9、字符串
- 10、正则表达式
- 11、对象
- 12、数组
- 13、类型化数组
- 14、函数
- 15、箭头函数和尾调用优化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、类
- 21、类的继承
- 22、Promise
- 23、Promise的静态方法和应用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基础实践
- 3、WebRTC视频通话
- 4、Web音视频基础
- CSS进阶
- 1、CSS基础拾遗
- 2、伪类和伪元素
- 3、CSS属性拾遗
- 4、浮动形状
- 5、渐变
- 6、滤镜
- 7、合成
- 8、裁剪和遮罩
- 9、网格布局
- 10、CSS方法论
- 11、管理后台响应式改造
- React
- 1、函数式编程
- 2、JSX
- 3、组件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表单
- 8、样式
- 9、组件通信
- 10、高阶组件
- 11、Redux基础
- 12、Redux中间件
- 13、React Router
- 14、测试框架
- 15、React Hooks
- 16、React源码分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基础
- 4、webpack进阶
- 5、Git
- 6、Fiddler
- 7、自制脚手架
- 8、VSCode插件研发
- 9、WebView中的页面调试方法
- Vue.js
- 1、数据绑定
- 2、指令
- 3、样式和表单
- 4、组件
- 5、组件通信
- 6、内容分发
- 7、渲染函数和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、数据类型
- 2、接口
- 3、类
- 4、泛型
- 5、类型兼容性
- 6、高级类型
- 7、命名空间
- 8、装饰器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系统和网络
- 3、命令行工具
- 4、自建前端监控系统
- 5、定时任务的调试
- 6、自制短链系统
- 7、定时任务的进化史
- 8、通用接口
- 9、微前端实践
- 10、接口日志查询
- 11、E2E测试
- 12、BFF
- 13、MySQL归档
- 14、压力测试
- 15、活动规则引擎
- 16、活动配置化
- 17、UmiJS版本升级
- 18、半吊子的可视化搭建系统
- 19、KOA源码分析(上)
- 20、KOA源码分析(下)
- 21、花10分钟入门Node.js
- 22、Node环境升级日志
- 23、Worker threads
- 24、低代码
- 25、Web自动化测试
- 26、接口拦截和页面回放实验
- 27、接口管理
- 28、Cypress自动化测试实践
- 29、基于Electron的开播助手
- Node.js精进
- 1、模块化
- 2、异步编程
- 3、流
- 4、事件触发器
- 5、HTTP
- 6、文件
- 7、日志
- 8、错误处理
- 9、性能监控(上)
- 10、性能监控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 监控系统
- 1、SDK
- 2、存储和分析
- 3、性能监控
- 4、内存泄漏
- 5、小程序
- 6、较长的白屏时间
- 7、页面奔溃
- 8、shin-monitor源码分析
- 前端性能精进
- 1、优化方法论之测量
- 2、优化方法论之分析
- 3、浏览器之图像
- 4、浏览器之呈现
- 5、浏览器之JavaScript
- 6、网络
- 7、构建
- 前端体验优化
- 1、概述
- 2、基建
- 3、后端
- 4、数据
- 5、后台
- Web优化
- 1、CSS优化
- 2、JavaScript优化
- 3、图像和网络
- 4、用户体验和工具
- 5、网站优化
- 6、优化闭环实践
- 数据结构与算法
- 1、链表
- 2、栈、队列、散列表和位运算
- 3、二叉树
- 4、二分查找
- 5、回溯算法
- 6、贪心算法
- 7、分治算法
- 8、动态规划
- 程序员之路
- 大学
- 2011年
- 2012年
- 2013年
- 2014年
- 项目反思
- 前端基础学习分享
- 2015年
- 再一次项目反思
- 然并卵
- PC网站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端学习之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 日志
- 2020