引入程式碼來源:深入分析markdown-it-quote外掛的魔法

2023-07-24 18:06:33

引入程式碼來源:深入分析markdown-it-quote外掛的魔法

markdown-it-quote是一個用於markdown-it的外掛,支援多種程式碼圍欄功能。

這是 SourceCodeTrace 專案之一,提供一種 Markdown Fence 的解析方案,包括對程式碼塊的參照、高亮、連結等功能。

SourceCodeTrace Project 幫助您在部落格、文章記錄的過程中,引入對應專案以及版本,行號等資訊,讓後續的讀者,通過參照來源,能夠進行更加深入的學習,在部落格或文章中引入程式碼塊時,儘量提供程式碼的來源資訊。

對於新的Markdown格式,如果你覺得寫起來很複雜, 可以用 MarkdownQuote 外掛,讓你在IDE中高效地複製程式碼塊。
更多細節請參閱 SourceCodeTrace 專案

Markdown 寫法

以下是一些用法範例,演示程式碼塊包含的不同資訊,方便大家在部落格的記錄中,引入程式碼塊的來源資訊。

  • 程式碼塊歸屬 第3125到3131行,並且將第3126到3130行標記為高亮,並連結到URL:

    ```java {3125-3131} {3129,3131} (https://github.com/10cl/fwkdev/blob/android-13.0.0_r52/dev/src/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java#L3125-L3131)
            // 凍結時使用當前調整,解凍時設定調整。
            if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                    && !opt.shouldNotFreeze()) {
                mCachedAppOptimizer.freezeAppAsyncLSP(app);
            } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
                mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
            }
        }
    
  • 程式碼塊歸屬 第3125到3131行,並連結到URL:

    ```java {3125-3131} (https://github.com/10cl/fwkdev/blob/android-13.0.0_r52/dev/src/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java#L3125-L3131)
            // 凍結時使用當前調整,解凍時設定調整。
            if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                    && !opt.shouldNotFreeze()) {
                mCachedAppOptimizer.freezeAppAsyncLSP(app);
            } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
                mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
            }
        }
    
  • 只連結到URL:

    ```java (https://github.com/10cl/fwkdev/blob/android-13.0.0_r52/dev/src/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java#L3125-L3131)
            // 凍結時使用當前調整,解凍時設定調整。
            if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                    && !opt.shouldNotFreeze()) {
                mCachedAppOptimizer.freezeAppAsyncLSP(app);
            } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
                mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
            }
        }
    
  • 僅高亮第1至2行:

    ```java {1-2}
            // 凍結時使用當前調整,解凍時設定調整。
            if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                    && !opt.shouldNotFreeze()) {
                mCachedAppOptimizer.freezeAppAsyncLSP(app);
            } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
                mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
            }
        }
    
  • 高亮第3行:

    ```java{3}
            // 凍結時使用當前調整,解凍時設定調整。
            if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                    && !opt.shouldNotFreeze()) {
                mCachedAppOptimizer.freezeAppAsyncLSP(app);
            } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
                mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
            }
        }
    
  • 僅指定語言:

    ```java
            // 凍結時使用當前調整,解凍時設定調整。
            if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                    && !opt.shouldNotFreeze()) {
                mCachedAppOptimizer.freezeAppAsyncLSP(app);
            } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
                mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
            }
        }
    

使用方法

要使用markdown-it-quote,首先通過NPM安裝該包:

npm i markdown-it-quote

然後,您可以在JavaScript程式碼中這樣使用它:

const MarkdownIt = require('markdown-it');
const markdownQuote = require('markdown-it-quote');

const md = new MarkdownIt();
md.use(markdownQuote);

md.render(markdownString);

請注意,語言名稱和左大括號之間的高亮行是可選的。

為了增加自定義樣式,您可以使用以下CSS程式碼為程式碼引入連結一些自定義樣式:

.gist-meta-quote {
  font-size: 12px;
  padding: 10px;
  overflow: hidden;
  color: white;
  border-radius: 0 0 6px 6px;
}

.gist-meta-quote a {
  float: right;
  color: white;
  text-decoration: underline;
}

vurepress 使用詳解

  • package.json 中引入依賴
    "markdown-it-quote": "^1.0.3"

/package.json?#L7-L7

  • config.json 裡面加入 markdown 拓展
  markdown: {
    extendMarkdown: md => {
      const markdownQuote = require('markdown-it-quote')
      md.use(markdownQuote);
    }
  },

/source/.vuepress/config.js?#L12-L17

  • md 檔案中直接用新的形式來寫程式碼
```java {3125-3131} {3126-3130} (https://github.com/10cl/fwkdev/blob/android-13.0.0_r52/dev/src/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java#L3125-L3131)
        // Use current adjustment when freezing, set adjustment when unfreezing.
        if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                && !opt.shouldNotFreeze()) {
            mCachedAppOptimizer.freezeAppAsyncLSP(app);
        } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
            mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
        }
    }
```

/source/README.md?#L31-L40

完整的可以參考Vuepress整合的patch
隨心客製化樣式以適合您自己的需要。

原理解析

fence 拓展

export default (md) => {
  const fence = md.renderer.rules.fence;
  md.renderer.rules.fence = (...args) => {
    const [tokens, idx, options, , self] = args;
    const token = tokens[idx];

/src/index.js?#L7-L11

markdown-it 的拓展是通過重寫 md.renderer.rules.fence 來實現對 fence的重新解析。

關鍵格式的解析

通過解析得到核心的解析塊

java {3125-3131} {3129,3131} (https://github.com/10cl/fwkdev/blob/android-13.0.0_r52/dev/src/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java#3125-L3131)

從中解析出 程式碼塊所在的起始行、結束行、高亮塊、以及連結。
然後通過預定義的模板將其渲染出來。

從使用者的角度來說,其中核心的要點就是要支援多種格式,比如原始的格式,以及為了推動程式碼塊來源的可追溯性,還需要支援之前預設的格式。

核心的幾塊邏輯,就是通過正規表示式來解析出來的。

const regex1 = /(\S+)\s?(\{([\d,-]+)})\s?(\{([\d,-]+)})\s?([\S]+)/i;
const regex2 = /(\S+)\s+(\{\d+-\d+\})?\s+(\S+)/i;
const regex3 = /(\S+)\s?(\{([\d,-]+)})/i;
const regex4 = /(\S+)\s+([^\{]\S+)/i;
const regex5 = /(\S+)/i;

/src/index.js?#L1-L5

https://regex101.com/r/osOtEv/1

高亮支援

對於高亮的支援,需要支援兩種形式,一種是

  • 起始行-結束行
  • 通過,分割的單行
      lines.map((split, index) => {
        const lineNumber = index + wrapLineNumStart;
        lineNumbersCode += `<span class="line-number">${lineNumber}</span><br>`;

        let inRange = false;
        if (highLightLineNumbers !== undefined) {
          inRange = highLightLineNumbers.some(([start, end]) => {
            if (start && end) {
              return lineNumber >= start && lineNumber <= end;
            }
            return lineNumber === start;
          });

          if (inRange) {
            highlightWrapCode += `<div class="highlighted">&nbsp;</div>`;
          } else {
            highlightWrapCode += `<br>`;
          }
        }
      });

/src/index.js?#L136-L155

連結的定義

      const gistInfo = `<div class="gist-meta-quote"><a href="${linkUrl}" target="_blank">view raw</a></div>`;

/src/index.js?#L168-L169
這裡通過解析到 url 轉換成html格式,點選 view raw 即新視窗開啟原始的程式碼源連結。

這裡如果不通過這種新的markdown格式,也可以僅採用預設程式碼塊,然後加一條連結的形式,指明你的程式碼來源。

貢獻

如果您想為此專案做出貢獻,請按照以下步驟進行:

  1. Fork倉庫。
  2. 建立您的特性分支:git checkout -b my-new-feature
  3. 提交您的更改:git commit -am 'Add some feature'
  4. 推播到分支:git push origin my-new-feature
  5. 提交拉取請求