在 Rust 的 CLI 生態系中,正靜靜地爆發流行病。症狀:參考文檔文字,讓人想測試自己是否有色盲,錯誤訊息,平鋪直敘到可以塞在門下,以及文檔,其風格表達力甚至不如收據。但同時,任何現代終端模擬器都支援粗體字、斜體字、顏色和 Unicode 繪圖符號。終端 所能與其應有的功能之間的差距。 要顯示,並且,大多數 CLI 工具 允許 顯示,按規模來說與大峡谷相當,按深度來說 — 與無視用戶的馬里亞納海溝相當.
Marcli 這次斷裂被關閉了。它取用 CommonMark Markdown,回傳給終端機帶有樣式的 ANSI 輸出。您在寫 Markdown。您的使用者看到的是美觀排版好的文字。整個想法都在一句話裡,而且這剛好夠了。

它做了什麼
Marcli 解析 Markdown 通過 comrak — 那個驅動 GitHub 渲染的解析器 — 會遍歷 AST,為每個支援的元素產生 ANSI 逃脫序列。被包圍的代码塊中的語法高亮由 syntect 提供。 — 與 Sublime Text 相同的語法。沒有 Pygments 的調用,沒有在執行時加載,沒有任何配置儀式。只是直接運行 — 這個概念似乎讓其他生態系認為是激進的.
單次函數調用:
let output = marcli::render("# Hello\n\nSome **bold** text.", &Default::default());
println!("{}", output);這是一個典型情況下的全部公開 API。函數 render 接受一個 Markdown 字符串和一個 RenderOptions 的結構,返回String 裝載了 ANSI 字串序列。沒有任何「建構者」模式需要透過鏈接呼叫十七次。沒有任何需要事先實現的 trait。沒有任何隱藏在依賴樹中的非同步執行時,等待著吞噬你的組件的機會。
支援的元素
Marcli 處理完整的 CommonMark 規範加上幾個擴充功能:
標題 — h1 顯示為黃色粗體,h2 — 藍色粗體,h3 及以下 — 白色粗體。三個層級的視覺層次結構,沒有一行配置。
行內格式化 — 粗體、斜體、劃線和行內代碼,每種都有自己樣式的 ANSI。
標記列表 — 以三角形标记(▸)取代慣用的 ASCII 標點符號。雖是細節,但可讀性卻異常提升.
編號列表 — 圈起的數字符號(①, ②, ③, …)最多到二十項。超過二十項後則使用括號中的數字。雖然目前沒有人抱怨,不過,如果理智正常,也沒人會編制超過二十項的列表.
被圈起的程式碼塊 — 在由 Unicode 繪圖符號組成的框架中,帶有語言標題。當指定語言時,語法高亮會自動啟用.
引言 — 左側帶有暗淡的垂直線條,在視覺上與周圍文本區分開,無需憤怒.
表格 — 全套符號繪圖框架,列對齊正確(左對齊、右對齊、居中)。標題加粗顯示.
任務清單 — 帶有複選框的標記(☑ / ☐)。
主題分隔符 — 淺色水平線(使用繪圖符號)。
連結 — 文本藍色加粗,後面URL使用淺色字體.
圖片 — 替代文本帶URL在括號內,因為終端機,儘管我們盡了全力,仍然不是圖片查看程序.
這不是窮人的子集。這是任何理性的人希望在終端機中顯示的所有內容,沒有任何超出的部分。
自動亮語法,無需設定
包含語言標示的區塊碼會自動透過內建的syntect語法高亮顯示。Rust、Python、Elixir、JavaScript、Go、C、SQL、TOML、YAML、Markdown本身——只要syntect認識該語言,Marcli就會為其高亮顯示。若不認識,則區塊會以同樣的框架顯示為純粹的樣式化文本。沒有任何錯誤、警告或經驗值降級——只是沒有顏色的代碼。這種「不因未知輸入而失敗」的革命性概念,看起來行業正以地球變遷的速度朝其發展。
fn main() {
let md = "```rust\nfn greet(name: &str) {\n println!(\"Hello, {}!\", name);\n}\n```";
let output = marcli::render(md, &Default::default());
println!("{}", output);
}關鍵字會被著上一種顏色,字串則會被著上另一種顏色,註解會被變淡並以斜體突出,函數名稱會在背景中突出顯示。從 TextMate-範圍(syntect)映射到 ANSI 序列的過程是內建的,並涵蓋了所有主要的標記類別:關鍵字、字串、數字、註解、運算符、名稱、儲存修飾詞、實體名稱、支援宏、常數、變數和差異標記。每個類別都有合理的預設值,而且每個都可以被重新設定。
可以完全關閉語法高亮:
let mut theme = marcli::Theme::default();
theme.syntax_highlight = false;
let opts = marcli::RenderOptions { theme, ..Default::default() };透過 TOML 完整設定顏色主題
Marcli 的輸出視覺方面均由其結構控制Theme:標題顏色、清單標記、代碼塊邊框、表格符號、語法高亮代碼樣式、主題分隔線寬度、引文前綴——所有這些。預設值是合理的,但如果你需要企業色彩或高對比主題以提升無障礙性,你只需要覆蓋你感興趣的特定字段。其他的保持不變。Marcli 不是那種庫,它不需要你更改一個參數就要求你從頭重新構建整個配置,嘟囔著什麼「完整性」。
主題會從TOML檔案載入:
let theme = marcli::Theme::load(".marcli.toml").unwrap_or_default();
let opts = marcli::RenderOptions { theme, ..Default::default() };
let output = marcli::render(markdown, &opts);部分TOML檔案僅覆寫指定欄位;其餘內容保持預設值。未知鍵值會被靜默忽略,因此主題能與未來版本保持兼容。
主題系統並非後期通過特性旗標強行附加的權宜之計,這是一種架構設計。渲染器不包含任何硬編碼的ANSI序列——每個樣式都從結構中讀取。Theme在渲染時。這意味著可以產生完全未經風格化輸出,將所有樣式置於空白行中,或根據特定終端的顯示能力調整輸出,僅修改必要的序列。設計如此明顯,令人驚訝為何還需要解釋。
支援 xterm.js 和網頁終端 CRLF
若您的 CLI 工具也能在網頁終端中運作(xterm.js,例如),換行符就有意義。Marcli 允許設定換行符序列:
let opts = marcli::RenderOptions {
newline: "\r\n".into(),
..Default::default()
};
let output = marcli::render(markdown, &opts);每一個內部換行符——在列表元素之間、代碼塊內部、段落之間——都使用配置的序列。沒有後處理的正則表達式。沒有「用\n替換」\r\n 和我們將會禱告》。在結構上正確 — 這種方法會引發一些開發者接近文化衝擊的狀態.
刪除 ANSI 以獲得純文本
有時候需要沒有顏色的結構化渲染 — 用於記錄、重定向到文件、用於那些被 escape 序列壓制的可訪問性工具。選項 escape_sequences 解決了這個問題:
let opts = marcli::RenderOptions {
escape_sequences: false,
..Default::default()
};
let output = marcli::render(markdown, &opts);
// output содержит структурированный текст
// с маркерами и отступами, но без ANSI-кодовMarcli 也將 strip_ansi 作為公共函數導出,如果需要清除任意文本中的逃逸序列:
let plain = marcli::strip_ansi(some_ansi_string);依賴歷史
Marcli 依賴六個庫:comrak (Markdown 解析)、syntect (語法高亮)、serde 和 toml (主題序列化)、regex (移除 ANSI) 和once_cell (懶惰的靜態檔案)。所有預設情況下都關閉了可能的功能。沒有任何 tokio。沒有任何 hyper。沒有任何 tower。沒有任何 serde_json。沒有任何組合爆炸的 feature-旗標。沒有任何超出 serde 所需的過程式宏。
依賴樹設計上非常狹窄。CLI 工具,用於添加 Marcli 以渲染輸出。--help 或診斷錯誤,並非突然繼承 HTTP 客戶端和 TLS 堆疊。在 Rust 的世界裡,為了格式化日期添加一個 crate 有時會牽連上網絡堆疊的一半,這值得特別提及——或許還值得喝一杯香檳吧。
為何是 Marcli,而不是替代方案
Rust 的生態系統提供幾種針對終端機的樣式化輸出方法,每種方法都解決自己的問題。麻煩在於,它們大多數並不適用於你。
用於「著色」的套件(例如,colored、owo-colors、ansi_term) — 這是一系列筆刷。它們允許塗鴉獨立的行。它們不解析結構、不處理清單、不渲染表格、不高亮程式碼。如果你的內容已經是結構化的,所有格式化邏輯你都要自己寫。Marcli 將 非結構化的 Markdown 轉換成 完全結構化的終端輸出 完全不同的抽象層。比較它們——就像問,鐵錘和房子哪個更好一樣。
TUI-框架(例如,ratatui,tui-rs) — 這是完整的終端 UI 工具。它們控制佈局、小工具、事件循環、替代螢幕緩衝區。如果你正在開發互動應用程式 — 它們非常出色。如果你需要印製樣式化的說明並離開 — 這是針對文具按鈕的鎚子。Marcli — 一個像文具按鈕大小的工具。沒有任何對更大的聲稱,也幸好如此。
termimad — 最近的可替代方案。它也能在終端渲染 Markdown。但 termimad 使用自己的解析器(與 CommonMark 不兼容),擁有另一種(且更不透明)的主題化模型,並且不提供透過 syntect 的語法高亮。Marcli 使用 comrak 進行解析(CommonMark,與 GitHub 兼容),使用 syntect 進行語法高亮(Sublime Text 的語法),並且透過扁平的、可序列化的結構設定每個視覺參數。Theme。如果您重視符合規範和可配置性——選擇是明顯的.
mdcat — 一個獨立的命令行工具,而不是庫。您無法將其嵌入到您的二進制檔案中並調用render()。它要么作為外部進程啟動,要么需要用戶機上存在其二進制檔案。Marcli — 一個庫:添加到Cargo.toml,呼叫函數,獲取字串。無共同居住義務。
輕鬆的理由
每個用於 CLI 且需要與人溝通的 Rust 庫都會遇到同樣的問題:如何呈現結構化文本?答案幾乎總是三種之一:ⓐ 直接輸出裸文本並希望用戶能讀懂(一種近乎精神病的樂觀主義),ⓑ 手動編寫 ANSI 格式化,需要數十次調用format! 使用硬编码的 escape 字串序列(帶有自虐色彩的工匠精神),ⓒ 強迫框架體積大於應用程式本身(來自 node modules 的架構設計)。
Marcli 提供了一個方案 ⓓ: 使用 Markdown 格式來編寫訊息、參考文本、錯誤說明、變更日誌和診斷,這是一種您的團隊已經熟悉的格式,您的編輯器已經能夠高亮顯示,您的文件化流水線已經能夠渲染,通過單一調用函數,就能將其轉換為人們 希望閱讀的終端輸出。
價格 — 一個函數調用和六個傳遞性依賴。效益 — 你的 CLI 發布到世界上的每一個文本片段,都可以是粗體、斜體、語法高亮、標記、表格和引用,完全支援主題且無需手動處理 ANSI 結束序列.
你的終端值得更好的。println! Marcli — 一種讓他變得更好的方法。而如果他不值得 — 嗯,至少你的用戶值得.
連結
▸ 存儲庫: github.com/Oeditus/marcli-rust
▸ 文檔: docs.rs/marcli
▸ 套件: crates.io/crates/marcli












