Rust 命令行交互语法高亮实现 (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

本文主要介绍笔者对于 Calculator.rs 中repl
下语法高亮的实现方式。

实现前提:raw_mode
的启用
通常来说,我们在使用 rust 时,要想获取用户输入,就必须调用 rust 标准库中的io::stdin().readline
函数。
但是,通过这种方式来获取用户输入有一个缺点:你只能在用户输入完一行,按下回车后才能拿到用户输入的内容,而在用户输入的过程中,你啥也干不了。
那么,有没有办法能够逐个字符地读取用户输入,并做出自定义的反馈呢?当然有。那就是raw_mode
。
这个raw_mode
并不是一个对于命令行的专有名词,只是在 crossterm 等库中对于这种需求实现的称呼。
本文的实现同样是围绕 crossterm 这个库而展开,原因很简单,这个库集成了我们要实现语法高亮的太多功能:光标控制、raw_mode、终端信息获取、输出上色等等,且其中的一些功能在不同的操作系统上需要有不同的实现,使用这个跨平台且在多款终端上经过测试的库比起自己实现要舒服得多。
相比于常规的 Readline,在raw_mode
下,用户的输入不会有任何的默认行为,包括但不限于显示输入字符、回车换行返回、退格删除字符、光标移动等。

语法高亮实现思路
实现思路非常简单易懂,可以简单地分为三个阶段:
读取用户输入并存储到字符串
对这个字符串进行分析 (tokenize)
使用分析的结果 (tokens) 进行渲染
接下来,我会逐一介绍这三个阶段。

读取用户输入并存储
在raw_mode
读取用户输入时,我们需要先对 crossterm 封装好的键盘事件进行处理,这段代码可以用来获取当前的按键事件。
然后,我们需要利用这个函数来获取按键事件并进行处理。
对读取到的字符串进行解析
这一阶段简单来说就是对上一阶段获得的字符串进行 tokenize,但是这与编译代码时所使用的分词器有所不同:这个 tokenizer 所生成的 token 必须能够被用来较容易地还原出原始内容。
这一阶段的代码因人而异,这里仅贴出 Calculator.rs 项目中使用的 tokenizer 代码,谨供参考:
还有其中的一些定义:
通过这一步骤,可以生成形如下面这样的 token:
使用分析的结果进行渲染
这一步就很简单了,直接循环第二步所产生的 token,然后通过token.colored
获取 token 对应的StyledContent
,直接 print 出来即可。
在第一阶段的loop
中调用:
最终实现效果如下:


完整代码如下