https://www.philippflenker.com/hecto/ 的阅读笔记。 我是学完[[Rust权威指南]] 之后才看的这个。
手把手教你写个 文本编辑器(1300+行代码)。
作者是真有耐心,真手把手教。看文章+手敲一遍,我大概花了十几个小时。
收获很大,很值得。
day1 环境搭建
https://www.philippflenker.com/hecto-chapter-1/
就是初始化项目。
day2 读用户输入
https://www.philippflenker.com/hecto-chapter-2/
介绍了 io的模式。
默认模式是缓冲i/o , 缓冲i/o 是遇到回车才发送给用户程序。
但是文本编辑器需要即时i/o, 即没输入1个字符,程序都需要立即拿到。
第三方库termion
可以让我们使用 raw mode
(即时I/O)。
之所以stdout().into_raw_mode().unwrap()
是因为stdout 才是控制 终端模式的。
变量名_stdout
之所以加个_
是rust 的所有权机制导致的。
如果不加,会马上释放掉,从而丢失了状态。
提到了控制字符和打印字符。
0-31,127 都是控制字符。
32-126 都是打印字符。
ctrl+x
就是把x的前3位置0了。
二进制表示用{:#b}
占位符。
提到了错误处理。。rust
是用Result
来传递错误。Result
是个枚举,要么有值,有么错误。 unwrap
就是配合Result
使用的。如果值正常,就返回值,否则panic
。
提到了match
模式匹配。
day3 原始输入和输出
https://www.philippflenker.com/hecto-chapter-3/
讲的就是不要重复造轮子,直接用termion
实现按键的检测。
Key::Char
,Key::Ctrl
,Key::Alt
等
去掉了底层的处理,,
从处理字节 变成了处理字符。
提到了Matches
必须穷举。
提到了分离代码。。main只提供入口,尽量精简。
然后没有;
的语句是表达式,也是函数的返回值。
struct
里的函数,第一个参数不是&self
的是关联函数,也叫静态方法。否则就是关联方法。
cargo clippy
可以提供一些建议,让我们的代码更地道。
提到了转义序列 可以指示中断干些事,譬如为文本着色啥的。
x1b[2J
x1b
= 按下esc
具体说明看vt100
J
是清除屏幕命令
2
是J
的参数。0表示清除cursor-end 位置的屏幕。1表示清除start-cursor 位置的屏幕。2表示清除start-end 位置的屏幕。默认值是0
然后关于转义序列的知识 termion
也封装好了。
控制鼠标的位置,也是用的转义序列。用的H
命令。譬如\x1b[12;40H
鼠标显示在12行,40列。
使用了pub use terminal::Terminal;
重导出声明,这样后面用的时候 直接用Terminal
就可以了
saturating_add
用户防止移除的。。譬如u8 最大致是255
直接 255+1 = 0
但是使用 255.saturating_add(1) 返回的是255, 可能产生溢出时,直接返回最大值。
隐藏/显示 鼠标 这里也是转义序列:
x1b[25h
隐藏。x1b[?25l
x1b[25l
显示。 x1b[?25h
第一次提到了解构。 let Position{mut x, mut y} = position;
day4 查看文件
https://www.philippflenker.com/hecto-chapter-4/
新增Row
, 和 Document
结构体。
引入了宏#[derive(Default)]
这玩意是实现代码的代码,会自动给你实现Default
方法。
引入了From
trait, 这个用于类型转换的。用的挺多的。
例子中就是把&str
转成了Row
对象。
Since we have implemented default
on Document
, we can use unwrap_or_default
here.
现在才懂unwrap_or_default
干嘛用的。。就是有值返回值,没值是也不抛出错误,而是调用default方法。
目前的结构体:
editor: 主体
document: 文件内容
row: 一行内容
terminal: 表达展示的,譬如移动鼠标,清屏等。
cursor_position: 跟踪鼠标位置
offset: 跟踪document内容偏移量。
https://play.rust-lang.org/ 可用来实验简单的代码。
我们计算鼠标位置 只适应于ascii.
像utf8, 有的字符有2/3个字节,就有问题了。
得把按字节计算的方式改成按字符的方式计算了。
这个借助于第三方库unicode-segmentation
day5 编辑文件
https://www.philippflenker.com/hecto-chapter-5/
这篇感觉没啥新东西。
只能说设计的结构良好。
就是在编辑rows和row 2个数据。
后面说的都是 clippy 提示的。
然后性能优化了下。
极致的性能一般会导致可维护问题,这需要权衡。
day6 搜索
https://www.philippflenker.com/hecto-chapter-6/
1
开始实现的简单搜索,就是在每行里搜字符串,这里要注意的是,返回的索引位置是字符位置,而不是字节位置。
并且遇到第一个匹配行就返回了。
之前封装的prompt
派到用场了。。就是接收用户输入的字符串。。
Option
用的很多。让你不得不考虑可能为空的情况。
2.
然后实现的是实时搜索。就是没输入1个字符就搜素。
这里引入了闭包,作为prompt每次按键的一个回调。同时也引入了泛型。
|_,_,__|{}
这就是个闭包函数。
如果用户取消搜索。还原光标位置。没啥好说的,增加个变量存下原位置。
引入了clone
, 有深拷贝和浅拷贝。clone
,copy
trait。
针对1的。这里可以通过按键,滚动到上一个/下一个匹配的位置上。
向前搜索容易,搜索起始位置就是当前的鼠标位置即可。
向后搜索,得加个方向参数了。遍历的时候也要反向遍历。
这里方向参数用的枚举。
rust
的枚举相当强大。还可以带数据的。
#[derive(PartialEq, Copy, Clone)]
pub enum SearchDirection {
Forward,
Backward,
}
不得不说rfind
真好。省事。
day7 语法高亮
https://www.philippflenker.com/hecto-chapter-7/
-
加颜色
还是用的 转义序列。 前景色+字符+重置前景色
这个作者牛逼,只需要改row.rs
里的render
函数就可以了。 -
改进。
上面的有明显的缺陷。
不够抽象,太具体。不能复用。 增加新的特征加高亮时不方便。
改进方式是抽象了Type
类型,提供了to_color
返回对应的前景色。
row
增加了highlighting -
改进。之前每个字符都加了颜色。但是连续的字符用同样的颜色时,加一个就够了。
_ => color::Rgb(0,0,0)
, 这里我改成了黑色。要不然有点不正常。 -
把高亮规则 应用到搜索结果上。增加
Match
成员。
又复用了之前写的find
方法。很快搜出匹配的下标,增加高亮类型。 -
改进数字高亮,变量名带数字的不能高亮。。必须是被空格/符号 分隔开的数字才高量。 这个感觉容易,修改
highlight
方法就可以了。 -
优化,让数字可以支持小数点。这个实现其实有bug,
3.4.5
也会当成数字高亮。 -
增加文件类型检测。就是根据文件名检测。
前面的行号是我尝试加的。
-
应用文件类型,,根据文件类型做高亮。
修改highlight
方法, 增加个HighlightingOptions 参数,然后改调用就可以。比较简单。 -
另存为是有问题,没有恰当高亮。修复下
这里隐藏了numbers属性,改成了提供方法。 -
增加特性,高亮字符串。就是"包起来的字符"。引入了新概念
*c
解引用。
-
修复bug,
\"
有转义时不能当做结束字符。 处理有点巧妙。遇到\
直接高亮2个字符。 -
增加特性,高亮字符。‘a’ 这种。和高亮字符串不一样。因为还有生命周期
'a
语法是不用闭合的。合法的其实只有2中。'x' 和 '\x'
.
-
增加特性,高亮 单行注释。 这个较简单。
-
重构,提高代码质量
把hightlight
分功能出来。不同的高亮规则用自己的函数。 -
高亮关键词。
有之前的铺垫,这个应该好容易。。关键词 就是 高亮搜索词 一样的逻辑。
感觉实现的有问题。譬如in
-
fix
in
的问题
需要有分隔符的 才是关键词。类似数字一样 得加个判断。
is_separator
调用得改成Self::is_separator
。 -
高亮次要关键词。和高亮主要关键词 一样的。
-
高亮 多行注释。
这个特殊点,之前都是单行处理。这个要跨行了。
方法是修改highlight
方法,增加个是否是多行注释的标记。
同时增删改 都需要修改。 -
性能优化
控制高亮从 document 移动 editor中。因为只有需要显示的文档才需要高亮。
改动分2步。
第一步 是 高亮文档开始 到 显示的行。
第二步是 只高亮 显示的行。