VSCode扩展编写总结(二)功能实现

扩展实现

默认创建的extension中有一个hello world的简单实现。通过注册命令,在用户输入了特定指令后触发相应的操作。
我这次做的插件主要包含三个功能:

  1. markdown文件的可视化编辑功能,打开某个markdown文件,自动预览其效果,在右侧显示独立的预览窗口
  2. 显示当前文档结构树,可以通过树形栏目进行快速跳转
  3. 显示markdown代码示例

文件自动预览功能

要实现点击某个markdown文件,就能自动在右侧显示预览边栏,这个功能怎么做呢?首先需要理清楚预览的触发条件:

  1. vscode启动后,如果当前默认打开的文件是markdown文件,需要自动预览
  2. 切换当前的活动编辑窗口,如果窗口内的文件是markdown文件,需要自动预览
  3. 在Gitbook Struct中右键某一项,编辑时,打开对应的文件,并自动预览

通过查看vscode的官方说明文档,可以得知,vscode.window.activeTextEditor代表的是当前的活动文档,若为空,则代表当前没有打开的文档,否则是一个代表当前Editor的对象。

而将markdown文件在右侧进行预览,vscode已经集成了现成的complex command,只需要通过如下代码调用即可:


vscode.commands.executeCommand("markdown.showPreviewToSide")) // 将当前活动窗口的内容在右侧预览

但光是执行这样的命令仍然存在问题,因为vscode有窗口分组的功能,每次调用刚才的命令,就会在右侧新建一个预览窗口,多次点击文件预览之后,窗口分组就会乱七八糟。而且,如果简单的给vscode.window.onDidChangeActiveTextEditor绑定预览处理,点击相同文件时,也会触发预览刷新。这些小问题都会导致在实际使用插件时,体验很糟。但好在只要理顺事件的触发处理逻辑,就能够让这个功能按照预期实现。vscode的命令执行返回的都是thenable对象,这意味着我们可以根据自己的需要链式调用各种命令或者操作。具体的代码就不赘述,细节可以看github。核心思想就是通过Promise/Thenable控制内部命令的执行顺序,workbench.action.closeEditorsInOtherGroups执行后能避免产生过多的右侧窗口分组,而当ChangeActiveTextEditor触发时,需要考虑当前的activeEditor是否已经在右侧预览,如果是,则不需要再新建预览窗口,否则会导致迭代触发onDidChangeActiveTextEditor。

显示文档目录树

本来想在VSCode的Activity Bar中显示一个新图标,来执行显示文档目录树的功能。但翻看了VSCode扩展相关的文档之后,发现目前还不支持这样的操作,只能新建一个Explorer的视图来实现这个功能。要创建新视图,需要在package.json中配置contributes内容。只需要添加如下配置信息即可:


 "contributes": {
        "commands": [
           // ...
        ],
        "views": {
            "explorer": [
                {
                    "id": "bookstruct",
                    "name": "Gitbook Structure"
                }
            ]
        }
// ....

这样vscode就会在explorer区域创建一个新窗口。窗口创建好了,还需要去读取目录结构信息。对于gitbook项目来说,目录信息就存放在SUMMARY.md这个文件中。vscode.workspace.rootPath存放了当前工作目录的路径信息,在当前工作路径的根目录下,读取SUMMARY.md的内容并解析,就能得到目录树的信息。解析的实现会单独写一篇博客来讲。为了将树形目录的数据传递给VSCode,需要通过执行下面的代码

            vscode.window.registerTreeDataProvider('bookstruct', this.bookStruct);            

registerTreeDataProvider接受两个参数,第一个参数是在package.json中views中定义的id,而第二个参数必须满足如下接口:

  • 包含方法getChildren(element),若参数element为空,则返回整个目录树,否则返回对应节点的目录子树
  • 包含方法getTreeItem(element: T),根据当前的element返回一个TreeItem信息,TreeItem是一个POJO,包含label属性(显示的文本),command(触发的命令),collapsibleState(是否展开),iconPath(可选,图标路径),contextValue(在when判断时被读取为viewItem的值)
  • 事件onDidChangeTreeData,可选,但一般如果要动态更新树形栏目,则需要有

显示Markdown示例代码

本来打算简单点,点击状态栏的"Markdown Example Code"图标,就用一个弹窗把常用的Markdown代码显示出,奈何VSCode并不提供弹窗的接口,只好退而求其次,反正都是markdown,干脆就用markdown文件来显示。

当用户点击图标时,新建一个新的markdown文件,同时左侧显示markdown源代码,右侧显示预览效果,即可代码起到提示作用。

通过vscode.workspace.openTextDocument({
content:'xxx',
language:'markdown'
}),可以创建一个空白的文档,但该文档并不会显示在VSCode的工作区域,还需要通过调用vscode.window.showTextDocument让它显示到当前工作窗口。

所有的vscode调用都是promise/thenable对象。

详细的实现见github代码。

Show Comments

Get the latest posts delivered right to your inbox.