Rust 命令行交互語法高亮實(shí)現(xiàn) (1)
原文地址:
https://bhznjns.github.io/markdown-blog/#static/Calculator.rs/Rust%20%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%BE%93%E5%85%A5%E8%AF%AD%E6%B3%95%E9%AB%98%E4%BA%AE%E5%AE%9E%E7%8E%B0%20(1).md

本文主要介紹筆者對(duì)于 Calculator.rs 中repl
下語法高亮的實(shí)現(xiàn)方式。

實(shí)現(xiàn)前提:raw_mode
的啟用
通常來說,我們?cè)谑褂?rust 時(shí),要想獲取用戶輸入,就必須調(diào)用 rust 標(biāo)準(zhǔn)庫(kù)中的io::stdin().readline
函數(shù)。
但是,通過這種方式來獲取用戶輸入有一個(gè)缺點(diǎn):你只能在用戶輸入完一行,按下回車后才能拿到用戶輸入的內(nèi)容,而在用戶輸入的過程中,你啥也干不了。
那么,有沒有辦法能夠逐個(gè)字符地讀取用戶輸入,并做出自定義的反饋呢?當(dāng)然有。那就是raw_mode
。
這個(gè)raw_mode
并不是一個(gè)對(duì)于命令行的專有名詞,只是在 crossterm 等庫(kù)中對(duì)于這種需求實(shí)現(xiàn)的稱呼。
本文的實(shí)現(xiàn)同樣是圍繞 crossterm 這個(gè)庫(kù)而展開,原因很簡(jiǎn)單,這個(gè)庫(kù)集成了我們要實(shí)現(xiàn)語法高亮的太多功能:光標(biāo)控制、raw_mode、終端信息獲取、輸出上色等等,且其中的一些功能在不同的操作系統(tǒng)上需要有不同的實(shí)現(xiàn),使用這個(gè)跨平臺(tái)且在多款終端上經(jīng)過測(cè)試的庫(kù)比起自己實(shí)現(xiàn)要舒服得多。
相比于常規(guī)的 Readline,在raw_mode
下,用戶的輸入不會(huì)有任何的默認(rèn)行為,包括但不限于顯示輸入字符、回車換行返回、退格刪除字符、光標(biāo)移動(dòng)等。

語法高亮實(shí)現(xiàn)思路
實(shí)現(xiàn)思路非常簡(jiǎn)單易懂,可以簡(jiǎn)單地分為三個(gè)階段:
讀取用戶輸入并存儲(chǔ)到字符串
對(duì)這個(gè)字符串進(jìn)行分析 (tokenize)
使用分析的結(jié)果 (tokens) 進(jìn)行渲染
接下來,我會(huì)逐一介紹這三個(gè)階段。

讀取用戶輸入并存儲(chǔ)
在raw_mode
讀取用戶輸入時(shí),我們需要先對(duì) crossterm 封裝好的鍵盤事件進(jìn)行處理,這段代碼可以用來獲取當(dāng)前的按鍵事件。
然后,我們需要利用這個(gè)函數(shù)來獲取按鍵事件并進(jìn)行處理。
對(duì)讀取到的字符串進(jìn)行解析
這一階段簡(jiǎn)單來說就是對(duì)上一階段獲得的字符串進(jìn)行 tokenize,但是這與編譯代碼時(shí)所使用的分詞器有所不同:這個(gè) tokenizer 所生成的 token 必須能夠被用來較容易地還原出原始內(nèi)容。
這一階段的代碼因人而異,這里僅貼出 Calculator.rs 項(xiàng)目中使用的 tokenizer 代碼,謹(jǐn)供參考:
還有其中的一些定義:
通過這一步驟,可以生成形如下面這樣的 token:
使用分析的結(jié)果進(jìn)行渲染
這一步就很簡(jiǎn)單了,直接循環(huán)第二步所產(chǎn)生的 token,然后通過token.colored
獲取 token 對(duì)應(yīng)的StyledContent
,直接 print 出來即可。
在第一階段的loop
中調(diào)用:
最終實(shí)現(xiàn)效果如下:


完整代碼如下