<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kilo on 映屿</title>
    <link>https://blog.verdant.ee/tags/kilo/</link>
    <description>Recent content in Kilo on 映屿</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    
      <managingEditor>i@glowisle.me (五葉地錦)</managingEditor>
    
    
      <webMaster>i@glowisle.me (五葉地錦)</webMaster>
    
    
    
    <lastBuildDate>Sat, 06 Dec 2025 13:26:47 +0800</lastBuildDate>
    
    
    <atom:link href="http://blog.verdant.ee/tags/kilo/atom.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>剖析千行C语言文本编辑器Kilo的技术细节</title>
      <link>https://blog.verdant.ee/posts/kilo-analysis/</link>
      <pubDate>Sat, 06 Dec 2025 13:26:47 +0800</pubDate><author>i@glowisle.me (五葉地錦)</author>
      <guid>https://blog.verdant.ee/posts/kilo-analysis/</guid>
      <description>&lt;p&gt;今天上午学习了一下&lt;a href=&#34;https://github.com/antirez/kilo&#34;&gt;Kilo&lt;/a&gt;的源代码。我很早以前就对文本编辑器的实现方法感兴趣了。&lt;/p&gt;&#xA;&lt;p&gt;Kilo是一个很简易却不简陋的项目，清晰地展示了如何构建一个终端下的文本编辑器，它的目的不是真正让你学会去开发一个高标准高质量，能投入使用的文本编辑器，而是理解文本编辑器的核心骨架、理解一个看似庞大一团糟的问题的拆解思路。这是一个很好的起点。也过了一把爽玩C语言的瘾（虽然我并没有写几行代码）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;程序分析&#34;&gt;程序分析&lt;/h2&gt;&#xA;&lt;p&gt;整个项目只有一个文件，一千三百行代码。我用了大概一个半小时梳理了程序的执行流程，手画了一个流程图。为了美观，我又用&lt;a href=&#34;https://www.glowisle.me/posts/%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8graphviz%E7%BB%98%E5%88%B6%E7%A8%8B%E5%BA%8F%E6%B5%81%E7%A8%8B%E5%9B%BE/&#34;&gt;Graphviz&lt;/a&gt;绘制了一个电子版：&lt;/p&gt;&#xA;&lt;p&gt;&lt;figure class=&#34;image-caption&#34;&gt;&#xA;    &lt;img src=&#34;https://images.glowisle.me/kilo-graph.png&#34; alt=&#34;程序流程图（大意）&#34;&gt;&#xA;    &lt;figcaption&gt;程序流程图（大意）&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&lt;/p&gt;&#xA;&lt;p&gt;这张图我省略了一些深的函数调用，但也能帮助我大体上掌握这个程序的执行流程。结合这张图与源码，我发现文本编辑器的核心功能——打开、编辑、保存，实现难度并不大，在C语言中容易踩坑的是缓冲区处理、文件读写这种老生常谈的内容。在这个程序中，调用最多、最重磅的部分是&lt;code&gt;initEditor&lt;/code&gt;这个函数，以及后续的高亮处理，尤其是前者在窗口尺寸计算、修改后的做法上花费了大功夫。其实和终端环境的交互才是最麻烦的点，它提供的封装和抽象并不多，有很多需要自己手动调试的地方，繁琐是显著特征。&lt;/p&gt;&#xA;&lt;h3 id=&#34;终端信号处理&#34;&gt;终端信号处理&lt;/h3&gt;&#xA;&lt;p&gt;我发现在终端程序里，需要快捷键的部分都是使用Raw mode和signal相关的函数组合实现的，在理解&lt;code&gt;signal&lt;/code&gt;这个函数和它的有关宏的概念时，耗费了比较长的时间。&lt;/p&gt;&#xA;&lt;p&gt;简单来说，signal用于处理用户在终端发出的信号，比如&lt;code&gt;SIGINT&lt;/code&gt;代表由&lt;code&gt;C-c&lt;/code&gt;产生的中断信号，&lt;code&gt;SIGIGN&lt;/code&gt;代表忽略信号，即接受到这个信号以后什么都不做，关于如何接受信号，就要说起&lt;code&gt;signal()&lt;/code&gt;这个函数。定义如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;void&lt;/span&gt; (&lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;signal&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; sig, &lt;span style=&#34;color:#dbbc7f&#34;&gt;void&lt;/span&gt; (&lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;func)(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;)))(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来非常复杂，说人话就是接受两个参数，第一个参数是int类型的&lt;code&gt;sig&lt;/code&gt;，是信号编号，比如&lt;code&gt;SIGINT&lt;/code&gt;，这是要接收的信号。第二个参数是一个函数指针，接受一个返回void，参数是int类型的信号处理函数，使用第二个函数中的函数对接受到的信号做处理。函数返回原来的信号处理函数（函数指针）。&lt;/p&gt;&#xA;&lt;p&gt;可以用typedef简化理解：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 定义信号处理函数的类型&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;void&lt;/span&gt; (&lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;sighandler_t&lt;/span&gt;)(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 用 typedef 重写 signal 声明&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;sighandler_t&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;signal&lt;/span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; sig, &lt;span style=&#34;color:#dbbc7f&#34;&gt;sighandler_t&lt;/span&gt; func);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在Kilo中，&lt;code&gt;C-c&lt;/code&gt;是被忽略的，因为它非常容易导致丢失修改，可以这样实现：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;signal&lt;/span&gt;(SIGINT, SIGIGN);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不过，在Kilo的实现，是通过调用&lt;code&gt;editorReadKey()&lt;/code&gt;，从Raw Mode 中读取一个按键存入数组，用switch匹配按键对应的值再返回给调用方，调用方也通过switch，匹配对应的操作函数。而在&lt;code&gt;C-c&lt;/code&gt;的部分，则是直接break掉了。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e67e80&#34;&gt;CTRL_C&lt;/span&gt;: &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Ctrl-c */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* We ignore ctrl-c, it can&amp;#39;t be so simple to lose the changes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;         * to the edited file. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;break&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种实现方法也有一定局限性，不同的终端模拟器可能发送不同的转义字符，硬编码转义字符会出现不适配的情况。并且使用&lt;code&gt;read()&lt;/code&gt;阻塞读取输入有性能瓶颈。&lt;/p&gt;&#xA;&lt;h3 id=&#34;精妙的数据结构与算法&#34;&gt;精妙的数据结构与算法&lt;/h3&gt;&#xA;&lt;p&gt;这个程序最有趣的地方在于清晰、通用的数据结构的设计，以&lt;code&gt;editorConfig&lt;/code&gt;为例：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;struct&lt;/span&gt; editorConfig {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; cx, cy;     &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Cursor x and y position in characters */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; rowoff;     &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Offset of row displayed. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; coloff;     &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Offset of column displayed. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; screenrows; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Number of rows that we can show */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; screencols; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Number of cols that we can show */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; numrows;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Number of rows */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; rawmode;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Is terminal raw mode enabled? */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    erow &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;row;      &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Rows */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; dirty;      &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* File modified but not saved. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;filename; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Currently open filename */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; statusmsg[&lt;span style=&#34;color:#d699b6&#34;&gt;80&lt;/span&gt;];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;time_t&lt;/span&gt; statusmsg_time;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;struct&lt;/span&gt; editorSyntax &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;syntax; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Current syntax highlight, or NULL. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们定义了一个&lt;code&gt;editorConfig&lt;/code&gt;类型的变量，它全局唯一，维护了程序的基本状态，包括行、列、滚动偏移、终端尺寸。让程序状态的流转非常清楚。这些内容都是一个文本编辑器需要关心的最核心内容：光标位置、视图偏移、数据和文件的状态等信息。&lt;/p&gt;&#xA;&lt;p&gt;通过这个结构体，能简单地获取程序当前的状态，或者为某项功能对状态作出修改，对一个新手来说还是挺拓宽思路的，至少我想不到怎么设计这些数据结构。&lt;/p&gt;&#xA;&lt;h3 id=&#34;数据与显示的分离&#34;&gt;数据与显示的分离&lt;/h3&gt;&#xA;&lt;p&gt;在&lt;code&gt;editorConfig&lt;/code&gt;中嵌套了一个&lt;code&gt;erow&lt;/code&gt;类型的变量，里面的东西也可以展开说说，定义如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#e67e80&#34;&gt;struct&lt;/span&gt; erow {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; idx;           &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row index in the file, zero-based. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; size;          &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Size of the row, excluding the null term. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; rsize;         &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Size of the rendered row. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;chars;       &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row content. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;render;      &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row content &amp;#34;rendered&amp;#34; for screen (for TABs). */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;hl; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Syntax highlight type for each character in render.*/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; hl_oc;         &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Row had open comment at end in last syntax highlight&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;                          check. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} erow;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里面有一个&lt;code&gt;render&lt;/code&gt;字段，在&lt;code&gt;editorUpdateRow()&lt;/code&gt;中，有这样的代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;, nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; j, idx;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* Create a version of the row we can directly print on the screen,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;     * respecting tabs, substituting non printable characters with &amp;#39;?&amp;#39;. */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;free&lt;/span&gt;(row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;for&lt;/span&gt; (j &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;; j &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;lt;&lt;/span&gt; row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size; j&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;if&lt;/span&gt; (row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;chars[j] &lt;span style=&#34;color:#7a8478&#34;&gt;==&lt;/span&gt; TAB)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            tabs&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; allocsize &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt;)row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;if&lt;/span&gt; (allocsize &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;gt;&lt;/span&gt; UINT32_MAX) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;Some line of the edited file is too long for kilo&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b2c98f&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;循环的if中使用的 &lt;code&gt;TAB&lt;/code&gt; 定义在KEY_ACTION枚举，值为9，在ASCII码中是&lt;code&gt;\t&lt;/code&gt;也就是水平制表符。代码在统计tab的数量。&lt;/p&gt;&#xA;&lt;p&gt;问题在于，一个&lt;code&gt;\t&lt;/code&gt;在内存中占1字节，但在屏幕显示的时候会占据八个字符的宽度，这里就体现出&lt;code&gt;render&lt;/code&gt;的作用了，如果一行有两个&lt;code&gt;\t&lt;/code&gt;，每个最多展开为八个空格，那么所需要计算的大小就是&lt;code&gt;2 * 8 + chars的大小&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#dbbc7f&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#dbbc7f&#34;&gt;long&lt;/span&gt;)row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那个恒为0的变量&lt;code&gt;nonprint&lt;/code&gt;可能是为将来打印不可见字符设计的。结尾的&lt;code&gt;+1&lt;/code&gt;为&lt;code&gt;&#39;\0&#39;&lt;/code&gt;预留。&lt;/p&gt;&#xA;&lt;p&gt;按照这个公式，给&lt;code&gt;render&lt;/code&gt;分配内存：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;malloc&lt;/span&gt;(row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; tabs &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; nonprint &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;随后，这些代码在非制表位填充空格：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;idx &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e67e80&#34;&gt;for&lt;/span&gt; (j &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;; j &lt;span style=&#34;color:#7a8478&#34;&gt;&amp;lt;&lt;/span&gt; row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;size; j&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e67e80&#34;&gt;if&lt;/span&gt; (row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;chars[j] &lt;span style=&#34;color:#7a8478&#34;&gt;==&lt;/span&gt; TAB) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render[idx&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e67e80&#34;&gt;while&lt;/span&gt; ((idx &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#7a8478&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 在非制表位填充空格&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render[idx&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#e67e80&#34;&gt;else&lt;/span&gt; { &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 正常字符直接赋值&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;render[idx&lt;span style=&#34;color:#7a8478&#34;&gt;++&lt;/span&gt;] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;chars[j];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;row&lt;span style=&#34;color:#7a8478&#34;&gt;-&amp;gt;&lt;/span&gt;rsize &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; idx; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 在循环结束的时候，idx等于写入字符总数&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;row&lt;span style=&#34;color:#7a8478&#34;&gt;-&lt;/span&gt;render[idx] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#39;\0&amp;#39;&lt;/span&gt;; &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;//在字符末尾添加结束符&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然有点绕，但设计还是非常巧妙的！&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h3 id=&#34;代码高亮&#34;&gt;代码高亮&lt;/h3&gt;&#xA;&lt;p&gt;源码中使用大量篇幅实现了代码高亮，定义了一些关键字：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;C_HL_extensions[] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.c&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.h&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.cpp&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.hpp&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;.cc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;NULL&lt;/span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#7a8478&#34;&gt;*&lt;/span&gt;C_HL_keywords[] &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* C Keywords */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;auto&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;break&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;case&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;continue&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;do&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;else&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;enum&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;extern&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;for&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;goto&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;if&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;register&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;return&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;sizeof&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;static&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;struct&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;switch&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;typedef&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;union&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;volatile&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;while&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;NULL&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* C++ Keywords */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;alignas&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;alignof&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;and&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;and_eq&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;asm&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;bitand&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;bitor&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;class&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;compl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;constexpr&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;const_cast&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;deltype&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;delete&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;dynamic_cast&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;explicit&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;export&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;friend&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;inline&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;mutable&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;new&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;noexcept&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;not&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;not_eq&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;nullptr&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;operator&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;or&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;or_eq&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;private&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;protected&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;public&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;reinterpret_cast&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;static_assert&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;static_cast&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;template&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;this&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;thread_local&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;throw&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;try&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;typeid&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;typename&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;virtual&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;xor&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;xor_eq&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;/* C types */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;int|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;long|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;double|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;float|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;char|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;unsigned|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;signed|&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;void|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;short|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;auto|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;const|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;bool|&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d699b6&#34;&gt;NULL&lt;/span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在具体实现&lt;code&gt;editorUpdateSyntax()&lt;/code&gt;中，简单粗暴地遍历字符匹配这些关键字。在一般的教学例子中这样实现是可以的，我认为在具体的工程中应当用词法分析、语法分析和字典树去匹配。更易于维护和拓展，也能适配复杂的嵌套。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;上述分析提到的缺点都可以作为优化方向，比如提供更简单操作接口，用词法分析技术或接入LSP服务器，为程序提供Lua接口来扩展插件……不过我相信在古老的纯C应用中，添加这些功能的繁琐程度和开发周期简直是灾难级别的。但是在处理快捷键上，使用&lt;code&gt;termcap&lt;/code&gt;库的难度应该小于修改代码高亮部分的难度。&lt;/p&gt;&#xA;&lt;p&gt;这个项目最值得学习的点是如何将抽象的功能和终端联系起来、如何设计合理的数据结构以及标准库的使用。是阐释「程序 = 数据结构 + 算法」的很好例子。不过我自己是想不到那些函数该什么时候用，没准还会手动实现标准库造好的轮子呢。&lt;/p&gt;&#xA;&lt;p&gt;学习的过程很好玩，从主函数开始探索整个程序，一段一段地跳转调用，A调用B，B调用C，C调用D，理解了逻辑后再把它们画成图，对感兴趣的部分深入研究，有一种前人用他的智慧抚平我大脑褶皱的感觉……读懂它，几乎就等于一只脚趾踩上了理解Vim / Nano等项目的大门吧。&lt;/p&gt;&#xA;&lt;p&gt;想自己重新实现一次，然后加入自己的优化，比如联动Lua / Zig甚至是Go来实现上层的功能，好玩好玩真好玩。&lt;/p&gt;&#xA;&lt;p&gt;头皮好痒，要长脑子了！&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在查资料的过程中又发现了 &lt;a href=&#34;https://github.com/martanne/vis&#34;&gt;vis&lt;/a&gt; 和 &lt;a href=&#34;https://github.com/zyedidia/micro&#34;&gt;micro&lt;/a&gt;（它甚至是用Go写的），又有新玩具了！&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
  </channel>
</rss>
