[技巧] 用 VSCode 把 OneNote 快速轉換到 Markdown + 包含程式碼

工作上的筆記總是寫在 OneNote,但如果想要貼部分資訊到 GitHub/GitLab 中,單純用複製貼上整個格式就會跑掉,還要手動一個一個調整滿麻煩的。有沒有什麼快速的方法來把 OneNote 轉換到 Marrkdown 格式呢?



範例 OneNote 文件

由於 OneNote 中我常常用 tables 來把 code 片段包起來,所以很常筆記會變成下面這樣:

Pandoc 工具

網路上常見的解法背後其實常常用了 Pandoc 這個工具,如果我們按照這個教學方法把上面的 OneNote 文件轉換成 Word 再轉成 Markdown 會變成這樣:


我們會發現 tables 與程式碼的 indents 整個都跑掉了,就不是我預期的。

而且還要先匯出成 Word,再開 PowerShell 輸入指令,如果頻繁的做這件事就會覺得很累,尤其是還在修改部分細節的時候。

VSCode + Replace Rules 插件

如果我們直接複製 OneNote 的文字,在 VSCode 貼上,會發現他會貼上這樣的格式:


除了文字前面的小圖示,我們會發現他的 indents 終於有保留下來。所以我一開始都是手動用 Ctrl+D 或 Ctrl+Shift+L 把 tabs + 小圖示選起來再改成 Markdown 的格式。Tables 比較麻煩要手動在前後加上 ```

這個動作做久了之後我就想到,這個動作其實和 find regex & replace 在做的事其實差不多,為何我不用 regex 來自動做到呢?

如果我們去裝 Replace Rules 這個插件,並且在 settings.json 中加上一些設定 (在文章的最後)。

之後,先在最下方空兩行 (為了讓 table bottom regex 可以正常運作),我們就可以在 VSCode 中按 Ctrl+Shift+P 然後輸入 Run Ruleset -> Ruleset: OneNote plaintext to markdown,剛剛貼過來的 OneNote 文件就會變成下方這樣:


我們再手動把 Title/Subtitle 加上 # 與 ##,把結果貼到 GitHub 的 editor 上,預覽就會正確地把 code block 顯示出來:


再也不用手動慢慢轉換啦。

筆記格式的限制

這個方法看似快速,當然有其限制:
  • 最後一行
  • 不支援有 indent 的 numbered list:因為 indent 第一層的文字會是 a. b. c. ...,第二層是 i. ii. iii. ...,這個用 regex 來取代就會讓 replace rules 多非常多設定,而且不知道到底要支援到第幾個項目。當然如果你要自己加這方面的 replace rules 當然可以,就依樣畫葫蘆就可以了。如果一開始知道要轉換到 markdown,通常我在撰寫就會盡量用 bulleted list,然後在 bulleted list 最上面先用文字敘述說底下這些項目要按照順序
  • 不支援 heading:因為從 OneNote 貼過來 VSCode 後,標題和一般文字都長得一樣,無法用 regex 去判別,不過標題通常不多,所以手動加一下 # 通常不會多花時間
  • 不支援文字以外的東西:畢竟貼到 VSCode 的時候只會有文字留下來
  • Table 只支援 1x1:其他形狀的表格用 regex 做大概很難吧
  • Indent 只支援到 level 5:OneNote 如果 indent 層級太多恐怕會有點難閱讀,Markdown 也是

Replace Rules 設定

在 VSCode 中按 Ctrl+Shift+P 輸入 Open User Settings (JSON) 後在適當的地方貼上以下的設定:


{
    //-------------------------------------------------------------------------
    // Extension: Replace Rules
    //-------------------------------------------------------------------------
    "replacerules.rules": {
        "OneNote plaintext to markdown: Bulleted list level 1": {
            "find": "^(\\t){1}• ",
            "replace": "- ",
        },
        "OneNote plaintext to markdown: Bulleted list level 2": {
            "find": "^(\\t){2}○ ",
            "replace": "    * ",
        },
        "OneNote plaintext to markdown: Bulleted list level 3": {
            "find": "^(\\t){3}§ ",
            "replace": "        + ",
        },
        "OneNote plaintext to markdown: Bulleted list level 4": {
            "find": "^(\\t){4}□ ",
            "replace": "            - ",
        },
        "OneNote plaintext to markdown: Bulleted list level 5": {
            "find": "^(\\t){5}® ",
            "replace": "                * ",
        },
        "OneNote plaintext to markdown: Numbered list level 1": {
            "find": "^(\\t){1}(\\w+)\\. ",
            "replace": "$2. ",
        },
        "OneNote plaintext to markdown: Table level 1 top": {
            // 1st group: Any line that doesn't start with tab
            // 2nd group: A line that starts with 1 tab
            "find": "^([^\\t\\n]+\\n)(\\t{1}[^\\t\\n]+\\n)",
            "replace": "$1    ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 1 bottom": {
            // 1st group: A line that starts with 1 tab
            // 2nd group: Any line that doesn't start with tab
            "find": "^(\\t{1}[^\\t\\n]+\\n)([^\\t\\n]*\\n)",
            "replace": "$1    ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 2 top": {
            // 1st group: Any line that doesn't start with tab
            // 2nd group: A line that starts with 2 tabs
            "find": "^([^\\t\\n]+\\n)(\\t{2}[^\\t\\n]+\\n)",
            "replace": "$1        ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 2 bottom": {
            // 1st group: A line that starts with 2 tabs
            // 2nd group: Any line that doesn't start with tab
            "find": "^(\\t{2}[^\\t\\n]+\\n)([^\\t\\n]*\\n)",
            "replace": "$1        ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 3 top": {
            // 1st group: Any line that doesn't start with tab
            // 2nd group: A line that starts with 3 tabs
            "find": "^([^\\t\\n]+\\n)(\\t{3}[^\\t\\n]+\\n)",
            "replace": "$1            ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 3 bottom": {
            // 1st group: A line that starts with 3 tabs
            // 2nd group: Any line that doesn't start with tab
            "find": "^(\\t{3}[^\\t\\n]+\\n)([^\\t\\n]*\\n)",
            "replace": "$1            ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 4 top": {
            // 1st group: Any line that doesn't start with tab
            // 2nd group: A line that starts with 4 tabs
            "find": "^([^\\t\\n]+\\n)(\\t{4}[^\\t\\n]+\\n)",
            "replace": "$1                ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 4 bottom": {
            // 1st group: A line that starts with 4 tabs
            // 2nd group: Any line that doesn't start with tab
            "find": "^(\\t{4}[^\\t\\n]+\\n)([^\\t\\n]*\\n)",
            "replace": "$1                ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 5 top": {
            // 1st group: Any line that doesn't start with tab
            // 2nd group: A line that starts with 5 tabs
            "find": "^([^\\t\\n]+\\n)(\\t{5}[^\\t\\n]+\\n)",
            "replace": "$1                    ```\n$2",
        },
        "OneNote plaintext to markdown: Table level 5 bottom": {
            // 1st group: A line that starts with 5 tabs
            // 2nd group: Any line that doesn't start with tab
            "find": "^(\\t{5}[^\\t\\n]+\\n)([^\\t\\n]*\\n)",
            "replace": "$1                    ```\n$2",
        },
        "OneNote plaintext to markdown: Tab to spaces": {
            "find": "\\t",
            "replace": "    ",
        },
    },
    "replacerules.rulesets": {
        "OneNote plaintext to markdown": {
            "rules": [
                "OneNote plaintext to markdown: Bulleted list level 1",
                "OneNote plaintext to markdown: Bulleted list level 2",
                "OneNote plaintext to markdown: Bulleted list level 3",
                "OneNote plaintext to markdown: Bulleted list level 4",
                "OneNote plaintext to markdown: Bulleted list level 5",
                "OneNote plaintext to markdown: Numbered list level 1",
                "OneNote plaintext to markdown: Table level 1 top",
                "OneNote plaintext to markdown: Table level 1 bottom",
                "OneNote plaintext to markdown: Table level 2 top",
                "OneNote plaintext to markdown: Table level 2 bottom",
                "OneNote plaintext to markdown: Table level 3 top",
                "OneNote plaintext to markdown: Table level 3 bottom",
                "OneNote plaintext to markdown: Table level 4 top",
                "OneNote plaintext to markdown: Table level 4 bottom",
                "OneNote plaintext to markdown: Table level 5 top",
                "OneNote plaintext to markdown: Table level 5 bottom",
                "OneNote plaintext to markdown: Tab to spaces",
            ]
        }
    },
}


結語

通常我會在 OneNote 先寫好 spec,再貼到 GitLab 上給團隊的其他人 review,但是 spec 塗塗改改的過程中要一直轉成 Markdown 如果還要叫我用什麼工具去執行我就會覺得很麻煩。用 VSCode 轉換雖然對於筆記的撰寫格式限制滿多的,但轉換的過程非常快速,而且對於寫程式的來說 VSCode 幾乎是經常開著的狀態,何不就地取材一下,加速我們的軟體開發呢?

留言

此網誌的熱門文章

[試算表] 追蹤台股 Google Spreadsheet (未實現損益/已實現損益)

[Side Project] 互動式教學神經網路反向傳播 Interactive Computational Graph

[插件] 在 Chrome 網頁做區分大小寫的搜尋