Markdown 語言伺服器介紹
2022 年 8 月 16 日,由 Matt Bierner (@MattBierner)釋出
Markdown 支援是我在 2016 年加入 Visual Studio Code 團隊時接手的第一個功能。哇,真的已經過去六年了嗎?不過這真是個完美的匹配。我使用 Markdown 的時間已經很長了,以至於我經常發現自己會下意識地在 Twitter、Outlook 以及幾乎所有游標停留的文字框裡輸入反引號和星號。這些年來,能夠逐步完善 VS Code 內建的 Markdown 支援,並看到我們的 Markdown 擴充套件如何直接或間接地影響了像 webview 和 notebook 這樣的核心功能,這讓我感到非常有成就感。
因此,我很高興能分享一個我過去半年來一直在默默推進的專案,我認為這個專案代表了 VS Code Markdown 工具的下一個發展方向:Markdown 語言伺服器。透過這個語言伺服器,我們將 VS Code 內建的大部分 Markdown 語言工具——從文件大綱、智慧摺疊到路徑補全等所有功能——提供給其他編輯器和工具使用。我們的目標是,用那些通常只與程式語言相關聯的智慧功能來推動 Markdown 工具向前發展。
Markdown 語言伺服器的工作分為兩個新的(且名稱相似!)開源庫:
-
Markdown 語言服務 (Markdown Language Service) - 一個提供處理 Markdown 工具的 TypeScript 庫。
-
Markdown 語言伺服器 (Markdown Language Server) - 一個基於該語言服務構建的 Markdown 語言伺服器。
雖然這些庫仍處於早期階段,但它們已經被 VS Code 1.70+ 版本使用(希望你甚至沒有注意到 :-))。我們甚至已經從這次切換中看到了一些好處,比如將 Markdown 工具移至一個獨立的程序,這樣它就不會阻塞其他擴充套件程式。
不過,在我講得太遠之前,你可能會問:為什麼需要一個 Markdown 語言伺服器呢?說實話,我自己也花了六年時間才想通這一點。這也追溯了我對 Markdown 認知的演變過程:從最初把它看作是加了幾個星號、方括號和井號來點綴的純文字,到後來把它理解為一種標記語言,並且認為它可以從我們為 TypeScript 或 Python 等程式語言提供的許多相同工具中受益。
深入瞭解 Markdown 工具
在我發現 VS Code 之前,我主要用一個簡單的文字編輯器來編碼。這意味著我必須記住符號名稱,並且每次想用它們時都要手動輸入。如果我想重新命名一個變數,我會進行文字查詢/替換,並希望我的單元測試能捕捉到那些不可避免的輸錯或搞亂名稱的情況。這是一種緩慢且不可靠的工作方式,但我當時很滿足,因為我不知道事情可以變得更好。直到我最終接觸到更智慧的工具後,我才真正意識到我以前的工作流程是多麼原始。
最近,我對 Markdown 也有了同樣的感悟。多年來,我一直滿足於使用 VS Code 相對簡單的 Markdown 編輯器。我對語法高亮和內建的 Markdown 預覽感到滿意。文件大綱和可點選的編輯器連結只是額外的福利。我已經習慣了手動寫出連結。我已經接受了如果我更改了一個標題名稱,就需要進行文字搜尋來更新所有指向該標題的連結。而且因為我把 Markdown 看作是比花哨的純文字多不了多少的東西,我甚至無法想象還有更好的方式存在。
但有一天,在我感覺像是第一百次輸錯圖片路徑後,我終於恍然大悟:這毫無樂趣!我為什麼要把生命浪費在手動輸入和驗證這些連結上?這應該是工具該乾的活!我知道我想要的不是隨便一個工具,我想要的是一個能幫助我像閱讀和編寫文字一樣處理 Markdown 的工具,而不是把 Markdown 原始碼隱藏在某種所見即所得(WYSIWYG)式的 UI 魔法背後。這與 VS Code 的精神以及我們對程式語言支援的思考方式非常一致。為什麼我們為傳統程式語言提供的許多智慧功能不能同樣應用於 Markdown 呢?第二天,我就開始著手開發連結補全功能。
連結補全是一種建議功能,可以幫助你編寫指向當前檔案內標題或工作區中其他檔案的連結。我甚至添加了對補全指向其他 Markdown 檔案內標題連結的支援。太棒了!這只是一個小小的補充,但卻極大地提高了我的工作效率。很快,我就無法想象沒有它我該怎麼活。
Markdown 補全功能的成功讓我欣喜若狂,我陶醉地想象著接下來還能為 Markdown 帶來哪些其他的語言智慧功能。我想象著自己自信地在標題上按 F2 鍵來安全地重新命名它們。我幻想著紅色的波浪線從模糊的文字海洋中射出,幫助識別無效連結。這一切看起來都那麼理所當然!為什麼我幾年前沒有想到呢?我開始將 Markdown 理解為結構化文字,而不僅僅是純文字,更好的 Markdown 工具的可能性似乎無窮無盡。
Markdown 語言功能
我不會用每個新功能背後的故事來煩你,也不會深入探討它們是如何實現的全部細節。總而言之,我採取了一種增量式的方法,這使得在我投入到 VS Code Markdown 支援的有限時間內,所有這些努力都成為可能。例如,我沒有直接開始構建重新命名支援,而是先讓一個可靠版本的“查詢所有引用”功能執行起來(因為如果你想重新命名一個符號,你首先需要知道所有引用它的地方)。以增量方式工作,並在已有功能的基礎上構建新功能,也幫助我在實現新功能時測試舊功能。例如,實現連結重新命名幫助我捕捉了大量連結檢測的 bug。(這種方法的唯一缺點是,你會意識到你那“如此優雅”的高塔是建立在幾個非常雜亂的正則表示式之上的)。
當報告無效檔案/圖片連結的實驗性支援在春末推出時,我退後一步審視我的工作。現在的 Markdown 語言功能集包括:
- 文件大綱
- 工作區符號
- 文件連結
- 智慧摺疊
- 智慧選擇
- 自動補全
- 重新命名
- 查詢所有引用
- 轉到定義
- 損壞連結的診斷
- 檔案移動/重新命名時更新連結
我知道這些新工具將使處理 Markdown 的工作更快、更安全。但當我回顧這個常見於程式語言的功能列表時,一個想法一直在困擾著我。幾個月前我還認為這很荒謬,但現在,當我再次思考時,我意識到也許是時候開發一個 Markdown 語言伺服器了。
你被“服務”了嗎? (Are you being servered?)
到 2022 年春末,所有 VS Code 的 Markdown 工具仍然執行在常規的擴充套件 API 上。雖然我想探索將所有這些工具遷移到一個正式的語言伺服器上,但進行這種改變會有實際的工程成本。我需要確保這是值得的。
我為此反覆糾結了一個多月。儘管現有程式碼狀況良好,但仍有許多未知數。如果我進行到一半才發現行不通怎麼辦?我以前甚至從未真正開發過一個語言伺服器。
在 debating 這一切的同時,我讓自己忙於重構 Markdown 擴充套件的原始碼,就好像它將要被遷移到語言伺服器一樣。我試圖隔離對 VS Code 擴充套件 API 的依賴,將更多邏輯切換為使用服務注入,並確保測試不依賴於檔案系統。這樣,即使我最終沒有邁出語言伺服器這一步,至少我也在清理程式碼庫。
最終,有幾個考慮說服了我,一個 Markdown 語言伺服器是正確的下一步。首先是一個相當平凡的原因:我發現為 Markdown 檔案高效地實現連結診斷非常具有挑戰性。在一個大型的 Markdown 工作區,比如 vscode-docs,我總是會不小心阻塞擴充套件主程序幾百毫秒。這很不好。而語言伺服器則作為其自己的程序執行。不僅如此,語言伺服器現在還有一種新的拉取模型 (pull model) 用於診斷,我非常想嘗試一下。
然後還有一些更高尚的理由。例如,一個 Markdown 語言伺服器對其他編輯器和工具也會很有用。這包括 VS Code 團隊釋出的另一個編輯器:Monaco!更不用說像 Markdown CLI 工具這樣的可能性了。如果我沒有時間自己構建這樣一個工具,也許其他人可以,以語言伺服器為起點。我在 VS Code 的 Markdown 工具上投入了大量工作,如果所有這些工作也能惠及他人,那將是一件很棒的事。
透過提供一個新的語言伺服器,我也許還能啟動一個圍繞改進 Markdown 工具的共同努力。VS Code 既是開源軟體的多產者也是使用者,我已清楚地看到了這類專案帶來的好處。一個開源的 Markdown 語言伺服器將幫助其他編輯器,但反過來也會吸引貢獻,最終幫助 VS Code!與其讓每個編輯器/工具重複勞動實現自己的 Markdown 支援,一個語言伺服器可以把開發者聚集在一起,共同致力於一個能讓所有人受益的更大專案。
如果沒有一個實際構建語言伺服器的計劃,所有這些宏大的思考都是無關緊要的。即使經過我所有的重構,將程式碼遷移到語言伺服器仍將是一項巨大的工作!這看起來勢不可擋,直到我意識到我不必一次性完成所有工作。我可以增量地構建伺服器,一次從 VS Code Markdown 擴充套件中遷移一個功能到新的 Markdown 語言伺服器。如果我做得對,我可以將每個小的增量遷移都簽入,這樣使用者在構建新的語言伺服器時就可以測試它。理想情況下,使用者永遠不會注意到一個功能何時從擴充套件遷移到了語言伺服器。
也許這很明顯,但我已經成為這種增量式處理大型程式碼變更方法的忠實信徒。沒有幾十萬行程式碼的 PR,也沒有持續數月(或數年!)的龐大功能分支。相反,對 `main` 分支進行一系列小的、安全的更改。如果一切按計劃進行,完成所有工作的最後一次提交應該是平淡無奇的。這就是我們在整個 VS Code 程式碼庫中逐步使用嚴格空值檢查時採取的方法,這也是我覺得我可以快速且儘可能平穩地將所有 VS Code 的 Markdown 工具遷移到新的語言伺服器的方式。
劇透一下:它成功了!我一次一個地遷移了語言功能。我邊做邊學,在需要時進行重構。診斷是最後一個遷移的功能,因為我不僅將它們遷移到語言伺服器,還重寫了它們以使用語言伺服器新的拉取診斷模型。整個工作的最後一次提交主要是刪除了 Markdown 擴充套件中現在未使用的程式碼。所以今天,如果你使用的是 VS Code 1.70+,幾乎所有的 Markdown 語言功能都使用了新的語言伺服器。
共同構建更好的 Markdown 工具
在許多方面,過去六個月在 VS Code 的 Markdown 工具方面的進展比我過去六年在這個領域工作的進展還要多。今天,我們推出了許多新工具,其中一些是 Markdown 以前從未有過的。許多這些功能讓最普通的 Markdown 讀者和作者受益,而另一些則只有高階使用者才能體會到。然而,儘管取得了所有這些進展,我知道我們才剛剛開始探索 Markdown 工具的可能性。
真正讓我對 Markdown 語言伺服器感到興奮的是,現在這個專案已經超越了 VS Code 本身。透過使我們的 Markdown 工具易於使用,我希望我們能幫助推動所有人的 Markdown 工具向前發展。這些開源專案是邀請大家共同構建 Markdown 工具未來的邀請函。如果你有興趣做出貢獻,請檢視這些新專案,看看你能用它們創造出什麼。你可以提交 bug 報告和功能請求,甚至可以提交一個 PR!還有許多我甚至沒有夢想過的智慧 Markdown 語言功能。讓我們一起構建它們吧!
如果你有興趣檢視原始碼或做出貢獻,可以在 GitHub 和 npm 上找到 Markdown 語言服務和伺服器。
-
Markdown 語言服務 (Markdown Language Service) - 一個提供處理 Markdown 工具的 TypeScript 庫。
-
Markdown 語言伺服器 (Markdown Language Server) - 一個基於該語言服務構建的 Markdown 語言伺服器。
編碼愉快!
Matt Bierner, @MattBierner