这篇文章主要介绍“如何实现编写插件来改造VS Code编辑器”,在日常操作中,相信很多人在如何实现编写插件来改造VS Code编辑器问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何实现编写插件来改造VS Code编辑器”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
作为一个靠代码作为“生计”的开发者,bug 写的好不好,编辑器真的很重要!那么 Visual Studio Code 这个大名你肯定不会陌生。作为一个老厉害的编辑器,它的过人之处简单讲讲来说有这么几点:
首先,它的设计者是一个很有名的工程师:Eric Gamma。20年前,他是《设计模式:可复用面向对象软件的基础》的作者之一,这本书在开发社区的地位被视为面向对象软件开发的指路明灯(瞻望大佬)。
其次,对于写 JavaScript 的人来说,虽然市面上有很多大大小小不同的编辑器,比如 sublime、atom、webstorm 等等,VS Code 与他们区别在于他比 sublime 开源,比 atom 更快,比 webstorm 更轻。
VS Code 全名 Visual Studio Code 是微软开源的一款编辑器,GitHub 上标星 115k(11 万)。它是基于 TypeScript 编写,总计代码数量在 30 万以上,大型知名开源项目。
> 项目地址:https://github.com/microsoft/vscode
我们先来简单看一下它的产品定位吧~可以看到,项目作者对它的定位属于轻量级的编辑器,所以要求它轻量、响应速度快,适用于多种语言等等。VS Code 的编辑能力来自于一款同样来自微软叫做 monaco 的开源 Web 编辑器,同时为了实现跨平台的需求又引入了 Electron:一个使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序。
正因为有了清楚的定位和方向,才会有了更加清晰的边界。或许你很疑惑,他是怎么支持多种语言的又做到轻量级的?那我们不得不来看一下它的多进程架构。
VS Code 有一个主进程入口,负责一些窗口管理、进程间通信、自动更新等全局任务;
渲染进程,顾名思义负责一个 Web 页面的渲染;
插件宿主进程,每个插件的代码都运行在一个独立且隔离的 Node 环境的宿主进程中,插件无法访问 UI;
Debug 进程,用于调试;
语言服务,是一种重要的特殊拓展,可以为许多编程语言提供编辑体验,还可以实现 VS Code 支持的自动补充,错误检查(诊断),跳转到定义以及许多其他的语言功能。
最核心的部分就是它的插件系统,为编辑器的拓展带来了更加个性化的开源定制。只要你能找到强大的 VS Code 插件组合在一起,那你的编辑器一定是一个高级且高效率的工作好帮手。
先来看一下,VS Code 大致有哪些可供我们拓展的能力。
有没有心痒痒地想自己动手搞一个 VS Code 插件?下面就带大家做一个入门级的 VS Code 插件。
首先你搞个 Node.js 和 Git。
其次「全局(-g)」安装 Yeoman(现代 Web 应用程序脚手架工具)和 VS Code Extension Generator 这两个官方指定的工具脚手架(生成 VS Code 插件项目的工具)。
npm install -g yo generator-code
当你看到下面的信息就说明安装成功了:
依赖环境搞好了,接下来就要用到 Yeoman 这个工具来帮我们快速创建项目结构啦!可能有很多人对这个脚手架不熟悉,简单的说,Yeoman 是一个通用的脚手架系统,它允许创建任何类型的应用程序。你可以用它快速开始新项目。所以不仅仅是 JavaScript 这个语言,Java,Python,C#等都可以用它来生成项目,只要有对应的生成器就可以。那我们进行下一步,在命令行中输入 yo code
。
让我们来分析一下这几个选项的意思,其实和字面意思一样,从上到下:
新的插件(Typescript)
新的插件(JavaScript)
新的主题颜色
新的语言支持
新的代码片段
新的键值绑定
新的插件包
新的语言包(本土化)
你可以看到这个工具支持创建多种类型的项目,我们今天先从插件(Extension)入手,所以第一个和第二个的区别就是,你要是会用 TypeScript 就选第一个,也是官方推荐的一个;要是不想写各种麻烦的类型定义和类型校验,就选第二个 JavaScript。 这一次我们选 JavaScript 来做一个简单的入门, 随后你会需要填写一系列初始化的信息如下:
注释如下:
What type of extension do you want to create?(创建哪一种类型的扩展?)
What's the name of your extension?(扩展的名称?应该全部为小写字母,没有空格)
What's the identifier of your extension?(扩展的标示?)
What's the description of your extension?(扩展的描述是什么?)
Initialize a git repository?(是否初始化 git 仓库?)
Which package manager to use? (因为我们装的是 npm,所以选 npm 就行了,如果你有 yarn,你也可以选 yarn)
使用哪一种包管理器(来下载各种 npm 包)
前面的准备的差不多啦!那我们就要开始开「绿皮小火车」啦。
进入刚创建的文件目录 cd test-extension
,文件目录:
或许你觉得文件目录嘛,一看就知道了,不就是几个配置信息或者项目说明嘛,但是这里面的配置信息「非常重要」x3,重要到你可能少一个配置或者配置的不对,功能就出不来。所以我们还是稍微花点笔墨聊聊这里的信息。
首先你可以在 package.json
文件里面,看到自己在前一个步骤里面设置的各个值,配置内的各个主要的含义如下,这里有个小点注意一下,如果你的 VS Code 比较旧,且更新不了最新的,你就把下面的 engines
设置的版本低一点,比如我就改成了 ^1.52.0
确保一定能兼容目前的 VS Code 编辑器就可以 :
{ "name": "test-extension", // 插件的名字 "displayName": "test-extension", // 在插件市场展示的名字 "description": "vscode extension sample", // 插件描述 "version": "0.0.1", // 插件版本 "engines": { // 最低支持 vscode 的版本 "vscode": "^1.52.0" }, "categories": [ // 插件的类别,用于在插件市场做区分 "Other" ], "activationEvents": [ // 插件激活的事件列表,可以有多个触发机制,所以是数组形式 "onCommand:test-extension.helloWorld" ], "main": "./extension.js", // 插件主入口 "contributes": { // 贡献点,用于拓展插件功能的配置项,这里不会细讲,先用 command 举例 "commands": [ { "command": "test-extension.helloWorld", "title": "Hello World" } ] }, "scripts": { "lint": "eslint .", "pretest": "npm run lint", "test": "node ./test/runTest.js" }, "devDependencies": { "@types/vscode": "^1.55.0", "@types/glob": "^7.1.3", "@types/mocha": "^8.0.4", "@types/node": "^12.11.7", "eslint": "^7.19.0", "glob": "^7.1.6", "mocha": "^8.2.1", "typescript": "^4.1.3", "vscode-test": "^1.5.0" } }
熟悉了配置之后,我们再来看一下插件的入口文件 extsnsion.js
,里面主要有两个主要的函数,一个是 activate
表示激活插件时的处理,一般是注册命令等一些初始化的操作;另一个是 deactivate
,表示插件失活的时候做的处理,其实听名字你也应该有体感,这就是插件的生命周期里的两个钩子函数嘛。
// 引了 vscode 这个模块,这样你就可以用它里面的很多很多功能啦 const vscode = require('vscode'); /** * @param {vscode.ExtensionContext} context */ function activate(context) { console.log('Congratulations, your extension "test-extension" is now active!'); let disposable = vscode.commands.registerCommand('test-extension.helloWorld', function () { vscode.window.showInformationMessage('Hello World from test-extension!'); }); context.subscriptions.push(disposable); } function deactivate() {} module.exports = { activate, deactivate }
我们来分析一下上面这段代码,你可以看到里面 registerComman
了一个命令,是不是有种似曾相识的感觉?没错,就是上面在 package.json
里面有提到的那个 command
,让我们摘出来一起看看:
..., // package.json "contributes": { // 贡献点,用于拓展插件功能的配置项,这里不会细讲,先用 command 举例 "commands": [ { "command": "test-extension.helloWorld", "title": "Hello World" } ] }, ...
... // extension.js function activate(context) { console.log('Congratulations, your extension "test-extension" is now active!'); let disposable = vscode.commands.registerCommand('test-extension.helloWorld', function () { vscode.window.showInformationMessage('Hello World from test-extension!'); }); context.subscriptions.push(disposable); } ...
这样看起来是不是很直观了?在 package.json
里面设置的 command
的值,就是 extension.js
里面 registerCommand
的值。那这几行命令是什么意思呢?让我们一起来运行看看:
他会帮你打开一个新的 VS Code 编辑器,插件只会加载到这个编辑器中。现在我们使用调用快捷键(MacOS) command+p ,输入 >Hello World
(不区分大小写):
回车一下,你会发现在右下角一个不起眼的角落里输出了这么一个提示:
我相信聪明的你们结合代码一定已经恍然大悟了对不对!不知道你们有没有这个疑问,上面那个 console.log
去哪里看?别急,我们回到插件代码的那个编辑器中,仔细看下面这边,他会在我们输入上面的命令之后才出现,因为在 package.json
里面我们配置插件的激活时机就是 onCommand:test-extension.helloWorld
:
那我们现在抱着「刻意学习」的思路,改一下上面的代码,比如把 test-extension
改成 test
:
..., // package.json "activationEvents": [ "onCommand:test.helloWorld" ], ..., "contributes": { "commands": [ { "command": "test.helloWorld", "title": "Hello World" } ] }, ...
// extension.js ... function activate(context) { console.log('我在这里!!'); let disposable = vscode.commands.registerCommand('test.helloWorld', function () { vscode.window.showInformationMessage('我改变了 command 的名字!'); }); context.subscriptions.push(disposable); } ...
再按照上面说的触发方法再来一遍,发现依旧是可以的!所以这里的名字只是一个命名空间,你可以改成你想要的任何名字,来适用于比较复杂的插件体系。既然是个命名空间,那其实不要这个前缀也可以。
前面介绍了那么多,大家有没有发现其实这个体系也不难,有大佬在前面铺路,其实我们只要按照规则“填空”就好了,那现在我们就来实现一个小小的功能——加一个按钮和他的点击事件。
修改我们的 package.json
如下,因为当前我希望插件加载的时候就已经订阅了按钮的点击事件,所以这里我们可以把 activationEvents
改成 *
,这样的话我们的插件在一开始就可以激活并注册事件了:
..., "activationEvents": [ "*", ], "contributes": { "commands": [ { "command": "test.helloWorld", "title": "Hello World" }, // 注册一下按钮点击的事件,再配一个小图标 { "command": "test.button", "title": "按钮", "icon": { "light": "./media/light/preview.svg", "dark": "./media/dark/preview.svg" } } ], // 在这里加一下下面这个配置 "menus": { "editor/title": [ { "command": "test.button", "group": "navigation" } ] } }, ...
然后回到我们的 extension.js
里面增加一个简单的消息提醒:
function activate(context) { console.log('我在这里!!'); let disposable = vscode.commands.registerCommand('test.helloWorld', function () { vscode.window.showInformationMessage('我改变了 command 的名字!'); }); // 新增一个按钮的点击命令操作内容 let button = vscode.commands.registerCommand('test.button', function () { vscode.window.showInformationMessage('按钮点击'); }); // 记得这个新的命令也要放进去订阅一下 context.subscriptions.push(disposable, button); }
看一下效果:
是不是很简单的就自定义了 VS Code 的样式?那我们现在就来分析一下我们上面做的事情。首先,我们修改了 package.json
里的配置,增加了一个 menus
,这个 menus 是什么呢?答案是菜单。菜单项定义包含选择时应调用的命令以及该项应显示的条件(when
),所以你也可以给这个菜单项显示加个显示的逻辑,比如我们规定在打开 javascript
文件时才显示这个按钮:
{ "contributes": { "menus": { "editor/title": [ { "when": "resourceLangId == javascript", "command": "test.button", "group": "navigation" } ] } } }
而 group
的含义呢,是用来定义菜单项的排序和分组的。来自官网的一个图,表示不同的 group
之间存在的顺序关系,当然这个菜单不是上面我们写的那个,而是 editor/context
,所以不同的菜单之间的 group
其实是有细微差别的,但是大体都差不多,而 navigation
的显示优先级是最高的:
我们也可以加一个这个看看:
"menus": { "editor/title": [ { "command": "test.button", "group": "navigation", "when": "resourceLangId == javascript" } ], "editor/context": [ { "command": "test.button", "group": "navigation", "when": "resourceLangId == javascript" } ] }
效果是一样的:
如果你好奇还有哪些菜单,我这里简单整理「翻译」了一下官网的内容(仅常见菜单非全部):
配置菜单项的名称 | 菜单位置 |
---|---|
commandPalette | 全局命令面板 |
explorer/context | 资源管理器上下文菜单 |
editor/context | 编辑器右键上下文菜单 |
editor/title | 编辑器标题栏,不配置图片就显示文字 |
editor/title/context | 编辑器标题右键上下文菜单 |
debug/callstack/context | 调试栈视图的上下文菜单 |
debug/toolbar | 调试工具栏 |
scm/title | SCM 标题菜单 |
view/title | 看标题菜单 |
touchBar | macOS 触摸栏 |
timeline/title | 时间轴视图标题菜单栏 |
extension/context | 扩展程序视图上下文菜单 |
到此,关于“如何实现编写插件来改造VS Code编辑器”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。