<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: hikerpig</title>
    <description>The latest articles on DEV Community by hikerpig (@hikerpig).</description>
    <link>https://dev.to/hikerpig</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F130942%2F2a3a7f30-feb3-4c4d-82ed-077e8910181e.png</url>
      <title>DEV Community: hikerpig</title>
      <link>https://dev.to/hikerpig</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hikerpig"/>
    <language>en</language>
    <item>
      <title>为 Pintora 写一个 VSCode 插件</title>
      <dc:creator>hikerpig</dc:creator>
      <pubDate>Sat, 15 Jan 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/hikerpig/wei-pintora-xie-ge-vscode-cha-jian-18p4</link>
      <guid>https://dev.to/hikerpig/wei-pintora-xie-ge-vscode-cha-jian-18p4</guid>
      <description>&lt;p&gt;&lt;a href="https://pintorajs.vercel.app/"&gt;Pintora&lt;/a&gt; 是我最近在折腾的一个开源项目，类似于 Mermaid.js 和 PlantUML，由文字生成图表。&lt;/p&gt;

&lt;p&gt;光是有在线编辑器还不太够，花了几天折腾了一个 VSCode 插件 - &lt;a href="https://marketplace.visualstudio.com/items?itemName=hikerpig.pintora-vscode"&gt;pintora-vscode&lt;/a&gt;，支持语法高亮、实时预览和导出图片。由于有上面两个项目的开源的 VSCode 插件珠玉在前，实现起来不难，也很有趣。&lt;/p&gt;

&lt;h1&gt;
  
  
  功能实现
&lt;/h1&gt;

&lt;h2&gt;
  
  
  语法高亮
&lt;/h2&gt;

&lt;p&gt;pintora 内置了几种类型的图表，语法几乎无相同之处，通过首单词来决定图表类型。 首先熟读官方的代码高亮指南 &lt;a href="https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide%E6%A0%87%E8%AF%86%E6%9D%A5%E9%A2%84%E8%A7%88%E5%8D%81%E5%87%A0%E7%A7%8D%E5%B7%A5%E5%85%B7%E7%9A%84%E7%BB%93%E6%9E%9C%E3%80%82"&gt;Syntax Highlight Guide&lt;/a&gt;，接着跟着 &lt;a href="https://github.com/bpruitt-goddard/vscode-mermaid-syntax-highlight"&gt;vscode-mermaid-syntax-highlight&lt;/a&gt; 的源码结构照猫画虎。然后和 TextMate 的语法规则搏斗一下就行。&lt;/p&gt;

&lt;p&gt;看 &lt;code&gt;package.json&lt;/code&gt; 中关于语法的 &lt;code&gt;contributes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pintora"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;".pintora"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./language-configuration.json"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"grammars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pintora"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scopeName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source.pintora"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./out/pintora.tmLanguage.json"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scopeName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source.pintora"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./out/pintora.tmLanguage.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"injectTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"text.html.markdown"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"embeddedLanguages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"meta.embedded.block.pintora"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pintora"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;该插件提供了两种情形下的代码高亮&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;pintora&lt;/code&gt; 语言文件，由 languages 处定义，后缀为 &lt;code&gt;.pintora&lt;/code&gt; 的文件&lt;/li&gt;
&lt;li&gt;markdown 文件中的 &lt;code&gt;pintora&lt;/code&gt; 块（由 &lt;code&gt;embeddedLanguages&lt;/code&gt; 指定），将其也认为是 &lt;code&gt;pintora&lt;/code&gt; 语言。&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;fileTypes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;span class="na"&gt;injectionSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;L:markup.fenced_code.block.markdown&lt;/span&gt;
&lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#pintora-code-block'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#pintora'&lt;/span&gt;
&lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pintora-code-block&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;(?&amp;lt;=[`~])pintora(\s+[^`~]*)?$&lt;/span&gt;
    &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;(^|\G)(?=\s*[`~]{3,}\s*$)&lt;/span&gt;
    &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#pintora'&lt;/span&gt;
  &lt;span class="na"&gt;pintora&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!!import/deep&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;diagrams/&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;component__element'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!!import/single&lt;/span&gt; &lt;span class="s"&gt;repository/component__element.yaml&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;activity__element'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!!import/single&lt;/span&gt; &lt;span class="s"&gt;repository/activity__element.yaml&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;style__clause'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!!import/single&lt;/span&gt; &lt;span class="s"&gt;repository/style__clause.yaml&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;style__part'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!!import/single&lt;/span&gt; &lt;span class="s"&gt;repository/style__part.yaml&lt;/span&gt;
&lt;span class="na"&gt;scopeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;source.pintora&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;以下以组件图 Component Diagram 为例。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;以 &lt;code&gt;componentDiagram&lt;/code&gt; 开头的，进入组件图的匹配规则里&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;patterns&lt;/code&gt; 里只有两个 &lt;code&gt;includes&lt;/code&gt;，主要是 &lt;code&gt;component__element&lt;/code&gt; 这个在上面配置中的 &lt;code&gt;repository&lt;/code&gt; 中声明的可重用语法规则，是因为组件图的语法支持多层嵌套，嵌套内外的语法相同，这样一个分形的结构，需要在顶层的 &lt;code&gt;repository&lt;/code&gt; 里声明好，才能复用。
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Component Diagram&lt;/span&gt;
  &lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;\b(componentDiagram)&lt;/span&gt;
  &lt;span class="na"&gt;beginCaptures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keyword.control.pintora&lt;/span&gt;

  &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#component__element'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#style__clause'&lt;/span&gt;

  &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;(^|\G)(?=\s*[`~]{3,}\s*$)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;下面再给出 &lt;code&gt;component__element.yaml&lt;/code&gt; 的局部示意。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;package'&lt;/span&gt;
    &lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!regex&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
      &lt;span class="s"&gt;(package|node|folder|frame|cloud|database|rectangle|component)\s+"([^"]+)"&lt;/span&gt;
      &lt;span class="s"&gt;\s*({)&lt;/span&gt;
    &lt;span class="na"&gt;beginCaptures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keyword.control.pintora&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;punctuation.bracket.open.pintora&lt;/span&gt;
    &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(})'&lt;/span&gt;
    &lt;span class="na"&gt;endCaptures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;punctuation.bracket.close.pintora&lt;/span&gt;
    &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#component__element'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;在 &lt;code&gt;package&lt;/code&gt;这个语法规则中，花括号中的内容，会通过 &lt;code&gt;- include: '#component__element'&lt;/code&gt; 继续使用此语法规则来解析。&lt;/p&gt;

&lt;p&gt;待解析的语言示意如下，详细语法可见 &lt;a href="https://pintorajs.vercel.app/zh-CN/docs/diagrams/component-diagram/"&gt;组件图说明&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database "MySql" {
  folder "This is my folder" {
    [Folder 3]
  }
  frame "Foo" {
    [Frame 4]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  在 Webview 中实时预览
&lt;/h2&gt;

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

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TpTMQHBU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/BmbbfwJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TpTMQHBU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/BmbbfwJ.png" alt="preview command" width="880" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  在内置 Markdown 预览内展示 pintora 代码块
&lt;/h2&gt;

&lt;p&gt;参考了 &lt;a href="https://github.com/vstirbu/vscode-mermaid-preview"&gt;vstirbu/vscode-mermaid-preview&lt;/a&gt; 插件才发现 VSCode 的内置 markdown 预览也是能支持扩展的，官方文档在 &lt;a href="https://code.visualstudio.com/api/extension-guides/markdown-extension"&gt;Markdown Extension&lt;/a&gt;，也将 vscode-mermaid-preview 作为一个推荐例子。&lt;/p&gt;

&lt;p&gt;下面是 pintora-vscode 预览 markdown 文件的效果。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--okqH7oKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/kyQEexU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--okqH7oKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/kyQEexU.png" alt="markdown preview" width="880" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;contributes&lt;/code&gt; 的写法如下：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"markdown.markdownItPlugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"markdown.previewScripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"./out/markdown-script.js"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  导出图表图片
&lt;/h2&gt;

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

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

&lt;h1&gt;
  
  
  参考
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide"&gt;Syntax Highlight Guide | Visual Studio Code Extension API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://macromates.com/manual/en/language_grammars"&gt;Language Grammars — TextMate 1.x Manual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;顺藤摸瓜可以看到一些优秀的工具。&lt;/p&gt;

</description>
      <category>pintora</category>
      <category>vscode</category>
      <category>工具</category>
    </item>
    <item>
      <title>万物皆可 fzf</title>
      <dc:creator>hikerpig</dc:creator>
      <pubDate>Fri, 08 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/hikerpig/fzf-50i2</link>
      <guid>https://dev.to/hikerpig/fzf-50i2</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt; 是一个速度和适用范围都极好的工具，借助 UNIX 管道，可以接入日常几乎所有终端 CLI 操作中，对所有的 list 都能有美好的可交互式模糊过滤体验。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's an interactive Unix filter for command-line that can be used with any list; files, command history, processes, hostnames, bookmarks, git commits, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KfZ6BWJN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-media-1.freecodecamp.org/images/1%2ALTR424sh7y8E8rUzsUnFsQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KfZ6BWJN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-media-1.freecodecamp.org/images/1%2ALTR424sh7y8E8rUzsUnFsQ.gif" alt="" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  一些技巧
&lt;/h1&gt;

&lt;h2&gt;
  
  
  使用 &lt;code&gt;-m&lt;/code&gt; 参数支持多选
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat $(fzf -m)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;然后就可以实用 tab 或者 shift+tab 实现多选。在使用 cat/rm 等命令时很好用。&lt;/p&gt;

&lt;h2&gt;
  
  
  使用 &lt;code&gt;**&lt;/code&gt; 作为 trigger
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unset **&amp;lt;Tab&amp;gt;
unalias **&amp;lt;Tab&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  善用 &lt;code&gt;--preview&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;交互式地选择 git branch。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# git interactive checkout
gcb() {
  result=$(git branch -a --color=always | grep -v '/HEAD\s' | sort |
    fzf --height 50% --border --ansi --tac --preview-window right:70% \
      --preview 'git log --oneline --graph --date=short --pretty="format:%C(auto)%cd %h%d %s" $(sed s/^..// &amp;lt;&amp;lt;&amp;lt; {} | cut -d" " -f1) | head -'$LINES |
    sed 's/^..//' | cut -d' ' -f1)

  if [[$result != ""]]; then
    if [[$result == remotes/*]]; then
      git checkout --track $(echo $result | sed 's#remotes/##')
    else
      git checkout "$result"
    fi
  fi
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  实用脚本
&lt;/h1&gt;

&lt;h2&gt;
  
  
  fzf-tab 替换 zsh 的默认补全选择菜单
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Aloxaf/fzf-tab"&gt;Aloxaf/fzf-tab&lt;/a&gt; ，如其自我介绍&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Replace zsh's default completion selection menu with fzf!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这个脚本可以将 zsh 的默认 completion 结果转给 fzf 显示，让你在选择补全结果的时候获得 fzf 加持。&lt;/p&gt;

&lt;p&gt;若使用 zimfw 安装，在 &lt;code&gt;.zimrc&lt;/code&gt; 内添加:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zmodule Aloxaf/fzf-tab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://asciinema.org/a/293849"&gt;&lt;img src="https://camo.githubusercontent.com/23f2047adb6e4d8642564c1adb3c5a0426db2c79ec15d48da1f0979892684a23/68747470733a2f2f61736369696e656d612e6f72672f612f3239333834392e737667" alt="asciicast" width="708" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  fzf-fasd, 配合 fasd 可代替 &lt;code&gt;z&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/wookayin/fzf-fasd"&gt;wookayin/fzf-fasd: 🌸 fzf + fasd integration&lt;/a&gt; ，实现了类似 z 的效果， &lt;code&gt;z&amp;lt;Tab&amp;gt;&lt;/code&gt; 后使用 fzf 选择最近进入过的目录。&lt;/p&gt;

&lt;h2&gt;
  
  
  其他零零散散
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bindkey | fzf # 搜索当前 shell 里所有绑定了的快捷键

fzf --preview "bat {} --color=always" # 快速预览当前以及子目录下文件内容，不在乎颜色的话 bat 可以换成 cat

cd $(find . -type d | fzf) # 可选择进入某个深层子目录
cd $(fd --type directory | fzf) # 与上面类似，不过使用更便捷的 fd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  参考文章
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://einverne.github.io/post/2019/08/fzf-usage.html"&gt;每天学习一个命令：fzf 使用笔记 - Verne in GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/better-programming/boost-your-command-line-productivity-with-fuzzy-finder-985aa162ba5d"&gt;Boost Your Command-Line Productivity With Fuzzy Finder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>使用 zimfw 作为 zsh 配置框架</title>
      <dc:creator>hikerpig</dc:creator>
      <pubDate>Wed, 14 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/hikerpig/zimfw-zsh-4gf7</link>
      <guid>https://dev.to/hikerpig/zimfw-zsh-4gf7</guid>
      <description>&lt;p&gt;之前使用 oh-my-zsh 的自带 git/nvm 等插件有时候有性能问题，同时据说其框架本身相比其他一些主打速度的配置框架(prezto/zimfw 等)也会慢一点。&lt;/p&gt;

&lt;p&gt;稍微折腾了一下，使用 &lt;a href="https://github.com/zimfw/zimfw"&gt;zimfw&lt;/a&gt; 作为 zsh 配置框架，它在&lt;a href="https://github.com/zimfw/zimfw/wiki/Speed"&gt;速度&lt;/a&gt;和可配置性间达到了很好的平衡。&lt;/p&gt;

&lt;p&gt;个人切换后，试了下跑 zprof，启动时间只有原先的一半，度量方式参考 &lt;a href="https://stevenvanbael.com/profiling-zsh-startup"&gt;Profiling zsh startup time&lt;/a&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  插件加载
&lt;/h2&gt;

&lt;p&gt;用上&lt;a href="https://github.com/zimfw/zimfw/wiki/Modules"&gt;自带的模块&lt;/a&gt;和&lt;a href="https://github.com/zsh-users/"&gt;zsh-users&lt;/a&gt;里一些优秀的插件，就已经有很好的表现，其中 zsh-users/zsh-autosuggestions 和 zsh-users/zsh-syntax-highlighting 能让 zsh 也有类似于 fish 一样友好的代码高亮反馈和输入自动补全功能。zimfw 比较友好的一点就在于默认配置（使用其提供的安装脚本）里给出的组合就已经很棒了。&lt;/p&gt;

&lt;p&gt;添加插件的方式与 oh-my-zsh 不太一样，后者需要把插件添加到指定的 plugins 目录（一般为 clone repo 或者拷贝文件），然后在 &lt;code&gt;.zshrc&lt;/code&gt; 文件里 &lt;code&gt;plugins=()&lt;/code&gt; 数组里添加插件名。&lt;/p&gt;

&lt;p&gt;而 zimfw 的方式则稍微方便一点，除了类似后者的方式外，还可以不用手动操作目录而是在 &lt;code&gt;.zimrc&lt;/code&gt; 文件中添加模块名（一般为 github 上的 user/repo 名），可在 &lt;a href="https://github.com/unixorn/awesome-zsh-plugins"&gt;awesome-zsh-plugins&lt;/a&gt; 里寻找合适插件，多数与配置框架无关(可以不局限在 oh-my-zsh 提供的插件目录里)。&lt;/p&gt;

&lt;p&gt;修改配置文件后，使用命令 &lt;code&gt;zimfw install&lt;/code&gt; 下载并编译插件(会使用 zsh builtin 的 &lt;code&gt;zcompile&lt;/code&gt; 将 &lt;code&gt;.zsh&lt;/code&gt; 文件编译为 &lt;code&gt;.zwc&lt;/code&gt; 字节码，提高下次加载的速度)，打开新 shell 时新插件生效。&lt;/p&gt;

&lt;h2&gt;
  
  
  外观和主题
&lt;/h2&gt;

&lt;p&gt;zimfw 的主题远不如 oh-my-zsh 丰富，自己的 gallery 里都是偏向极简的风格，不过我们可以自己找一些成熟的主题 &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;powerlevel10k&lt;/a&gt; 使用，带有问答式的新手配置指导，功能丰富，推荐使用。&lt;/p&gt;

&lt;h2&gt;
  
  
  一些配置
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;~/.zimrc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Start configuration added by Zim install {{{
# -------
# Modules
# -------
# Sets sane Zsh built-in environment options.
zmodule environment
# Applies correct bindkeys for input events.
zmodule input
# Sets a custom terminal title.
zmodule termtitle
# Utility aliases and functions. Adds colour to ls, grep and less.
zmodule utility
zmodule directory

#
# Prompt
#
# Exposes git repository status information to prompts.
zmodule git-info

# Theme
zmodule romkatv/powerlevel10k

# Additional completion definitions for Zsh.
zmodule zsh-users/zsh-completions
# Enables and configures smart and extensive tab completion.
# completion must be sourced after zsh-users/zsh-completions
zmodule completion
# Fish-like autosuggestions for Zsh.
zmodule zsh-users/zsh-autosuggestions
# Fish-like syntax highlighting for Zsh.
# zsh-users/zsh-syntax-highlighting must be sourced after completion
zmodule zsh-users/zsh-syntax-highlighting
# Fish-like history search (up arrow) for Zsh.
# zsh-users/zsh-history-substring-search must be sourced after zsh-users/zsh-syntax-highlighting
zmodule zsh-users/zsh-history-substring-search
# }}} End configuration added by Zim install

# Custom 个人推荐的插件
zmodule DarrinTisdale/zsh-aliases-exa
zmodule zsh-users/zaw
zmodule wookayin/fzf-fasd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;介绍一些程序和插件:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://segmentfault.com/a/1190000011327993"&gt;快速跳转工具--FASD 简单介绍&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jkg.tw/p2876/"&gt;打造屬於你自己的極速 Shell「iTerm + zsh + zim + powerlevel10k」&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/starbugs/%E6%89%93%E9%80%A0-10x-engineer-zsh-shell-97e40db76391"&gt;打造 10X Engineer Shell 工作環境. SRE 討生活篇 | by smalltown | Starbugs Weekly 星巴哥技術專欄 | Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://htr3n.github.io/2018/07/faster-zsh"&gt;Faster and enjoyable ZSH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.spencerwoo.com/2020/07/remove-nvm-to-speed-up-zsh/"&gt;Quit using nvm：快删掉这个占据 Zsh 启动时间一半的怪物！ - Spencer's Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Toc Bar, table of content for dev.to, finally.</title>
      <dc:creator>hikerpig</dc:creator>
      <pubDate>Sun, 05 Jul 2020 12:00:53 +0000</pubDate>
      <link>https://dev.to/hikerpig/toc-bar-table-of-content-for-dev-to-finally-333f</link>
      <guid>https://dev.to/hikerpig/toc-bar-table-of-content-for-dev-to-finally-333f</guid>
      <description>&lt;p&gt;&lt;a href="https://greasyfork.org/en/scripts/406337-toc-bar"&gt;Toc Bar&lt;/a&gt; is a userscript I wrote to add floating widget displaying table of content of current page.&lt;/p&gt;

&lt;p&gt;Currently only tailored for some personal most-visited sites, including dev.to .&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;a href="https://tscanlin.github.io/tocbot"&gt;tocbot&lt;/a&gt; for toc generation.&lt;/li&gt;
&lt;li&gt;For some sites, there are no ids on header elements so it would be impossible to navigate by clicking the toc link. Toc Bar will generate ids - which are derived from a simple hash of the header textContent, and prefixed with &lt;code&gt;tocbar-&lt;/code&gt; - for these headers.&lt;/li&gt;
&lt;li&gt;A toggle button is offered, if you don't want toc bar to cover current page content.&lt;/li&gt;
&lt;li&gt;For some sites in SPA mode, if you navigate to another article, there is no easy way detecting url change in userscript, so I add a refresh button to refresh TOC contents.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K8QlEfbT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/hikerpig/toc-bar-userscript/master/images/screenshot-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K8QlEfbT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/hikerpig/toc-bar-userscript/master/images/screenshot-2.png" alt="devto" width="880" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>userscript</category>
      <category>devto</category>
      <category>toc</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
