DEV Community

hikerpig
hikerpig

Posted on • Originally published at hikerpig.cn on

为 Pintora 写一个 VSCode 插件

Pintora 是我最近在折腾的一个开源项目,类似于 Mermaid.js 和 PlantUML,由文字生成图表。

光是有在线编辑器还不太够,花了几天折腾了一个 VSCode 插件 - pintora-vscode,支持语法高亮、实时预览和导出图片。由于有上面两个项目的开源的 VSCode 插件珠玉在前,实现起来不难,也很有趣。

功能实现

语法高亮

pintora 内置了几种类型的图表,语法几乎无相同之处,通过首单词来决定图表类型。 首先熟读官方的代码高亮指南 Syntax Highlight Guide,接着跟着 vscode-mermaid-syntax-highlight 的源码结构照猫画虎。然后和 TextMate 的语法规则搏斗一下就行。

package.json 中关于语法的 contributes:

"languages": [ { "id": "pintora", "extensions": [ ".pintora" ], "configuration": "./language-configuration.json" } ], "grammars": [ { "language": "pintora", "scopeName": "source.pintora", "path": "./out/pintora.tmLanguage.json" }, { "scopeName": "source.pintora", "path": "./out/pintora.tmLanguage.json", "injectTo": [ "text.html.markdown" ], "embeddedLanguages": { "meta.embedded.block.pintora": "pintora" } } 
Enter fullscreen mode Exit fullscreen mode

该插件提供了两种情形下的代码高亮

  1. pintora 语言文件,由 languages 处定义,后缀为 .pintora 的文件
  2. markdown 文件中的 pintora 块(由 embeddedLanguages 指定),将其也认为是 pintora 语言。

由于这个语言不断会有新的图表类型加进来,同时它不是标准的编程语言,而是类似于标记语言的 DSL,匹配规则比较长,因此采用了 yaml 来写语法配置 pintora.tmLanguage.yaml,使用 yaml-import 包来将其转化为最终的 json 文件。

--- fileTypes: [] injectionSelector: L:markup.fenced_code.block.markdown patterns: - include: '#pintora-code-block' - include: '#pintora' repository: pintora-code-block: begin: (?<=[`~])pintora(\s+[^`~]*)?$ end: (^|\G)(?=\s*[`~]{3,}\s*$) patterns: - include: '#pintora' pintora: patterns: !!import/deep - diagrams/ 'component__element': !!import/single repository/component__element.yaml 'activity__element': !!import/single repository/activity__element.yaml 'style__clause': !!import/single repository/style__clause.yaml 'style__part': !!import/single repository/style__part.yaml scopeName: source.pintora 
Enter fullscreen mode Exit fullscreen mode

!!import/deep!!import/single 都是 yaml-import 支持的指令,分别指引入一个目录内所有文件和引入一个文件,这样子不同图表的语法规则可以放在各自的文件里维护,看起来清晰简洁。

以下以组件图 Component Diagram 为例。

  • componentDiagram 开头的,进入组件图的匹配规则里
  • patterns 里只有两个 includes,主要是 component__element 这个在上面配置中的 repository 中声明的可重用语法规则,是因为组件图的语法支持多层嵌套,嵌套内外的语法相同,这样一个分形的结构,需要在顶层的 repository 里声明好,才能复用。
- comment: Component Diagram begin: \b(componentDiagram) beginCaptures: '1': name: keyword.control.pintora patterns: - include: '#component__element' - include: '#style__clause' end: (^|\G)(?=\s*[`~]{3,}\s*$) 
Enter fullscreen mode Exit fullscreen mode

下面再给出 component__element.yaml 的局部示意。

patterns: - comment: 'package' begin: !regex |- (package|node|folder|frame|cloud|database|rectangle|component)\s+"([^"]+)" \s*({) beginCaptures: '1': name: keyword.control.pintora '2': name: string '3': name: punctuation.bracket.open.pintora end: '(})' endCaptures: '1': name: punctuation.bracket.close.pintora patterns: - include: '#component__element' 
Enter fullscreen mode Exit fullscreen mode

package这个语法规则中,花括号中的内容,会通过 - include: '#component__element' 继续使用此语法规则来解析。

待解析的语言示意如下,详细语法可见 组件图说明

database "MySql" { folder "This is my folder" { [Folder 3] } frame "Foo" { [Frame 4] } } 
Enter fullscreen mode Exit fullscreen mode

在 Webview 中实时预览

当打开一个 .pintora 文件,并执行了 Preview Pintora Diagram command 时,会在右侧新建一个新的 Webview,在里面展示当前文件所生成的图表。

preview command

由于 pintora 具有在浏览器环境和 Node.js 环境下执行的能力,比起 PlantUML 这样的 Java 程序,实时预览对 Web 更加友好,在 Webview 中展示,是再合适不过的了。能根据用户对源文件的更改,实时更新右侧预览效果,而且此过程中不会写临时文件到磁盘。

在内置 Markdown 预览内展示 pintora 代码块

参考了 vstirbu/vscode-mermaid-preview 插件才发现 VSCode 的内置 markdown 预览也是能支持扩展的,官方文档在 Markdown Extension,也将 vscode-mermaid-preview 作为一个推荐例子。

下面是 pintora-vscode 预览 markdown 文件的效果。

markdown preview

内置 Markdown 预览界面也是一个 Webview,使用 markdown-it 作为渲染库,因此需要扩展的话,也是基于 markdown-it 的插件来实现的。

contributes 的写法如下:

 "markdown.markdownItPlugins": true, "markdown.previewScripts": [ "./out/markdown-script.js" ], 
Enter fullscreen mode Exit fullscreen mode
  • markdown.markdownItPlugins,表示 pintora 插件会提供 markdown-it 的插件,在 pintora extension 的隔离环境中运行。通过这种方式提供的插件,可以通过 vscode 接口拿到一些配置,生成 html 的时候可以通过 data- 属性带上,之后在 Webview 里可以根据这些属性来得到 pintora 主题和渲染器(renderer)等用户设置。注意此时生成的 html 里会保留 pintora DSL 的文本,转成图表的工作需要在 Webview 中进行。
  • markdown.previewScripts,在预览的 Webview 里插入可执行的脚本,这里主要是使用 @pintora/standalone 处理 pintora 文本,生成 svg 或 canvas 展示在页面上,这个会是最终用户看到的样子。

导出图表图片

这部分就比较简单,就是使用 require('child_process').spawn 新建一个子进程,使用 node 执行 @pintora/cli (pintora 本身提供的 node 命令行程序)即可。

之所以采用子进程而不是在 extension 环境下直接 import { render } from '@pintora/cli' 来渲染,是因为使用 rollup 打包 node-canvas 的时候报错了(其实好像也是能实现的,见 How to pack canvas.node by rollup? · Issue #1504 · Automattic/node-canvas)。之后可以看一下能不能集成进来,目前看起来效果其实也挺好的,而且子进程的方式倒是也有优势,执行不会阻塞插件主线程。

参考

顺藤摸瓜可以看到一些优秀的工具。

Top comments (0)