在單一 Rust 核心上搭配各平台外殼的理由
如果要在三個作業系統推出相同的編輯器,有一條只需要寫一次 UI 的路。我們曾經走過那條路,後來又折返回來,改成在一個核心上加上一組針對各平台打造的輕量殼層。

如果要在 Linux、macOS 和 Windows 推出相同的編輯器,就得把 UI 做三次。
一開始,我們想避開這項成本。我們用一套跨平台工具組,在三個作業系統上繪製相同的畫面。這項決定讓我們通過了第一道效能閘門,也通過了韓文輸入的第一個里程碑。
後來,優先順序改變了。
效能比設計重要,設計比成本重要。時間與成本不列入限制條件。
按照這項標準重新審視後,答案也改變了。
我們放棄了單一 UI
跨平台工具組有明確的優點。只需製作一次功能,就能在多個作業系統上使用。
但也有必須付出的代價。它會多使用 40~60MB 的記憶體,啟動速度則慢了 150~200ms。文字呈現不如作業系統內建的文字引擎,系統特有的視覺效果也無法完整使用。
在開發成本很重要時,這是一項合理的選擇。但把成本從限制條件中移除後,就沒有理由再承受這些代價了。
因此,我們決定用 Rust 製作處理文件的核心,負責畫面的殼層則針對各個作業系統分別製作。
這並不是因為原有技術不好。優先順序改變時,即使面對相同的選項,也會得到不同的答案。我們決定付出將功能製作三次的成本,換回效能、文字品質,以及符合系統風格的體驗。
不過,我們並沒有捨棄一切。
我們原封不動地保留了那套測量規範。把游標移到視窗外、依名稱準確找到視窗、同步計算畫面格數。我們也一併沿用了韓文輸入的設計原則。只向作業系統顯示游標所在的那一行,不對正在組合的字元套用樣式。狀態不一致時,不進行猜測,而是重新同步。
即使程式碼被捨棄,經過驗證的原則仍會留下。
我們把殼層的職責定得很小
如果各平台的殼層開始承擔太多工作,三個產品很快就會變得不一樣。因此,我們明確規定了殼層該做什麼,以及不該做什麼。
殼層負責建立視窗並繪製文字。它會把鍵盤與指標輸入傳給核心,並將韓文輸入轉換成編輯指令。它也會套用作業系統的視覺效果。
另一方面,它不解析 Markdown。除了換行之外,它也不決定版面配置,不儲存檔案,也不判斷授權。
我們並未只把這項原則寫在文件裡。如果殼層中出現 Markdown 解析程式碼,自動檢查就會中止建置。
如果只有某個殼層開始處理語法,各作業系統的結果就可能出現細微差異。這類問題也很難找出來。因為即使表格只在某個殼層中顯示得不一樣,真正的原因也未必出在繪製表格的程式碼上。
我們讓只有核心能長久保留下來
核心不依賴任何 UI 技術。即使沒有視窗,也能在終端機中獨立測試。
文件編輯與 Markdown 解析、復原、搜尋、大綱、檔案儲存、匯出和授權驗證,全都由核心負責。

使用者按下一個按鍵後,殼層會將該輸入傳給核心。核心會修改文件,並只重新解析變更位置周圍的部分。它只計算畫面上可見的內容,再將有所變更的結果傳回殼層。殼層只重新繪製受到影響的行,完成一個畫面格。
如果在這個過程中重新讀取整份文件,那就不只是單純的效能問題。這代表違反了最初訂下的預算。
正確性也只在核心中驗證一次。它必須通過全部 652 個 CommonMark 官方範例,只要通過的數量減少,建置就會失敗。我們也會執行持續輸入非預期資料的模糊測試。
我們還會確認儲存後的檔案是否與原始檔案逐位元組相同。如果往返比率低於 1.0,那就不是速度慢,而是遺失了檔案。
即使更換殼層,核心仍會原封不動地保留下來。在 Linux 與 Windows 上會直接連接核心,在 macOS 上則使用 Swift 橋接程式碼。macOS 殼層使用 AppKit 與 TextKit 2 製作,Windows 殼層則使用 DirectWrite 製作。
我們的目標是讓文件維持一致,同時讓操作手感符合各個作業系統。
文件相同,使用感受不同
劃定邊界後,需要統一的項目也變得清楚了。
需要統一的是文件。無論在哪個作業系統上儲存,檔案內容都必須逐位元組相同。表格對齊、行尾以及最後的換行也都不應有所差異。
Markdown 的正確性已經在核心中完成驗證。每次製作新殼層時,都不需要重新證明一次。在新平台上需要確認的是使用感受與效能。
相反地,我們不會統一直接接觸到的使用體驗。
文字會使用各作業系統的文字引擎繪製。韓文輸入法也會直接採用作業系統的方式。半透明效果同樣會在 macOS 與 Windows 上使用系統功能,而在沒有相同功能的 Linux 上,只自行繪製必要的部分。
我們也不會勉強統一視窗按鈕的位置、選單的形式,以及快速鍵的輔助鍵。在不同作業系統上顯示相同的像素並不是我們的目標。更重要的是在每個作業系統上都讓人感覺自然。
我們也針對各作業系統分別訂定了發布條件。必須通過效能標準,並使用內建韓文輸入法完成實際測試後,才能發布。
基準測試表中也只有在該平台的建置版本實際產出後,才會加入那個平台的資料列。如果提前填入尚未測量的資料列,連表格中的其他資料列都會變得難以信任。
這項選擇有明確的代價
相同的功能必須製作三次。韓文輸入也必須實作三次,建置環境也得維護三套。
目前使用的機器無法建置或偵錯 Windows 與 macOS 版本。我們需要額外的硬體與自動化環境。因此,我們不會為這兩個平台標註日期。
我們也保留了重新檢討這項決定的條件。將成本排除在限制條件之外,是這項決定的前提。如果無法取得所需的硬體,就只延後該平台的時程。Linux 版本仍會照常發布。
一項決定不僅需要理由,也需要訂出重新思考的條件。如此一來,當情況發生變化時,就不必從頭重複相同的爭論。
需要長久保留下來的是核心。殼層則必須能隨時捨棄。
我們只把可以捨棄的部分交給平台。