前言
这是第 n 次写这篇博客了。前面都是写了几次之后又删掉,因为主题不明,终于也有觉得自己写作主题不明的那天了。其实这篇文章也不全是关于 VIM,因为我确实不好定性。
以前自己使用的 Markdown 编辑器是 Typora[1],不得不说,Typora 是真的优雅。虽然自己不再使用,但尝试过其他编辑器后,总是觉得没有一款可以比得上 Typora。
Typora 收费后各种替代品涌现,打算占据这片高地,比较有名的是 MarkText[2]。可惜有些东西还真
替代不了,虽然 MarkText 真的很简洁,走了 Typora 风格,但是使用起来总觉得不够丝
滑。
我还使用过 Zettlr[3],主要用于论文写作,因为
Typora 不支持文献引用。后面 Zettlr 出现小 bug,导致表格录入的数据都无法保存。这个问题很快就被修复了,但是当时我正在赶论文,根本没有时间等待,直接使用 VIM
写。
俺是万万没想到,原来 VIM 写 Markdown 一样可以很顺手。当时不知道Better BibTex[4]可以直接在 VIM 内调用 Zotero 的引用框,为了完成文献的插入,我用 vimscript,VIM
的脚本语言,写了一个引用文献的脚本,然后完成了自己的论文。
事后想想,好像没有使用 Zettlr 的道理了:原本它的优势是文献引用,现在自己写了一
个小脚本也解决问题了。此外,当时为了交叉引用引入新语法,Zettlr 也渲染不了,于是我就彻底抛弃 Zettlr 扎根 VIM。
使用 VIM 写作还是碰到了很多问题,很多都是自己并不完全了解 VIM 导致的,但有些问
题 VIM 到现在还是没有解决。这也是我写这篇博客的原因:把自己碰到的一些问题总结
到一起。
用 VIM 写 Markdown
|物理行和逻辑行
物理行指的是没有回车符断开的一行,或者叫做“段落”,逻辑行指的是段落内部可以分成
的行。这很抽象,下面一张图解释一下物理行和逻辑行:

上图中,第 50、54 行属于物理行,一般只占一(逻辑)行。第 46、48 行也是物理行,
但是由于字数太多进行了段内分行,所以出现了逻辑行。简而言之,图片中标有行号的就是物理行。
逻辑行的行数是随着窗口大小改变的,比如我把窗口变大之后,逻辑行就变少了,因为一
行可以容纳的字符变多了。

一般的编辑器不区分物理行和逻辑行,也就是光标或者鼠标把肉眼能见到的行,都算作一
行,不幸的是 VIM 不这样。
VIM 的 j 和 k 是物理行的移动,逻辑行使用 gj
和 gk
。写文章的时候,一行包含
的字符太多是没法用 j
、k
进行逻辑行移动的。
举个例子:上面第一张图,想要从第 46 行的“镜像之后”移动到下面的“但是可以”。其他编辑器最笨也可以使用方向键往下移动,但是 VIM 使用方向键或者 j
就会直接跳到第
47 行,要移动到下一逻辑行必须使用 gj
或者 g
,否则就需要使用l
键一直往右移动,直到碰到自己想到的位置。
使用 gj
也不是不行,只是需要敲两次按键,对我们来说太麻烦了。解决办法是使用
VIM 的按键映射(map)。在 VIM 配置文件 ~/.vimrc
添加:
nnoremap j gj
nnoremap k gk
nnoremap g
nnoremap g
nnoremap gj j
nnoremap gk k
nnoremap g
nnoremap g

上面是把两种按键对换了:物理行使用 gj
和 gk
、g
和 g
进行移动,逻辑行使用 j
和 k
或者方向键即可。
至于为什么使用的是 nnoremap
而不是简单的 map
,这得涉及到 VIM 的一些语法,
一两句话说不清。nnoremap
是 normal not recursively map
的缩写,表示普通模式下对按键进行非递归映射。
|长段落
第一个问题,物理行和逻辑行解决了,但是没有彻底解决:它只是解决了如何在物理行和
逻辑行直接移动,没有解决逻辑行和物理行的问题。
物理行指的是一个段落,为方便叙述统一使用“段落”替代,请看下面的物理行:

在段落里移动只能使用 h
和 l
,因为 VIM 最引以为豪的文本对象(text-object)
对东亚文字不起作用。英语或者说拉丁字母的词是通过空格来区分的,但是中文、韩文、
日文、喃字、字生(中日韩越壮字符)是没有空格的。
VIM 可以使用 w
进行字词的移动,或者 f
快速跳转,那是借助了拉丁文字的空格判
断。中文没有空格,这些文本操作只能识别标点符号。所以上面那些长长的段落,在 VIM
中只能非常笨拙地一个字符一个字符移动。
至于解决办法,没有,VIM 到现在都没有解决这个问题。
不过使用 Markdown 就有点好处了。Markdown 有一条规则:回车符不是分段的标志,空行才是段落的标志。举个例子:
不过使用 Markdown 就有点好处了。Markdown 有一条规则:回车符不是分段的标志,空
行才是段落的标志。举个例子:
看起来是两行,毕竟有一个回车嘛,在“空”之后。但 Markdown 中,这被当作一行看待,
Markdown 会把这两行合成一个段落,然后在两行之间添加一个空格,对于拉丁文字来说很方便。渲染后成为:
不过使用 Markdown 就有点好处了。Markdown 有一条规则:回车符不是分段的标志,空 行才是段落的标志。举个例子:
那 Markdown 如何插入一个真正的段落?使用一个空行就行:
不过使用 Markdown 就有点好处了。Markdown 有一条规则:回车符不是分段的标志,空
行才是段落的标志。举个例子:
正是 Markdown 的特性,我们不用让 VIM 的段落太长,可以在自己想要断开的地方断开,
反正渲染之后都会被当作一行对待。

那应该在哪里断开?写代码一般都会有个不成文的规定:一行代码的字符数不多于 78
个。所以我们可以限定自己的一段只有 78 个字符。写文章的时候不可能去数自己一行到底写了多少字符,可以用 VIM 的功能辅助:colorcolumn
。
colorcolumn
可以高亮特定的列。如果我们希望自己的字符数不超过 78 这个列,可以
在 ~/.vimrc
配置文件中添加 set colorcolumn=79
。

这样,只要写到这一列就可以换行了。上面的配色有点难看,但只是为了演示。
上面说了,Markdown 会把回车断开的行拼成一个段落,然后在它们之间添加一个空格。问题就出在这里了:添加一个空格。对拉丁文字来说好处多多,毕竟这样就不用在分段的
地方添加空格,但是对中文来说就多了一个不必要的空格。例如在 回
之后分段,下一
个字符是 家
,合并成一行后成为 回 家
。
这个解决办法还算简单:使用 Pandoc 的功能。Pandoc 扩展里有一个east_asian_line_breaks
功能,可以自动判断如果是东亚字符就不添加空格。

想要使用这个功能需要如此:
pandoc -f markdown+east_asian_line_breaks -t docx paper.md
或者在 YAML 配置中输入:
from: markdown+east_asian_line_breaks
# 或者
reader: markdown+east_asian_line_breaks
下面是使用这个扩展和不使用的效果展示:


east_asian_line_breaks
扩展,可以看到“一”和“点”之间有空格
east_asian_line_breaks
扩展,“一”和“点”之间的空格没有了至此,好像解决了逻辑行和物理行的问题了

|空格
写作的时候经常会碰到中英文混合输入。一般而言,如果碰到这种情况,应该在中英文之
间保留一个空格 — 中文、英文和阿拉伯数字之间应该有空格。
不过一般是看不到这些控制符号的。有些时候写的太快忘了在中英文之间放空格,回头检
查就很麻烦。可以给空格、制表符等这类控制符一个标识,这样检查时就能一眼看出。比
如下图使用一个点号(·)代替空格(可视化),检查的时候可以一眼看出空格在哪里:

上图中出现了一个中文句号后接英文(nnoremap
),它们之间是没有空格的,那里出现
空白只是因为中文句号是全角符号,占用两格。如果不让空格可视化,我们可能以为是一
个英文标点加一个空格 — 没错,我改论文的时候被这东西害惨了。
可视化的方式是在 ~/.virmc
配置文件添加:
set listchars=space:·
同理还可以设置其他的控制符,例如 Tabular
这类,可以在 VIM 中输入:help listchars
回车查看。

listchars
可以设置的部分|平滑滚动
还是物理行和逻辑行的衍生问题:因为 VIM 只认物理行,所以滚动的时候会出现这种问
题:如果一行很长,例如 JavaScript、JSON 文件,那滚动非常难受。
比如下面的演示:长段落滚动直接整段消失,重新绘制,太变态了。
同样是上面的例子,这次看下图。这种时候,长段落不会整段消失,而是尽量铺满屏幕,
用 表示和上面的文字是一个段落。

这就是“平滑滚动”,在 ~/.vimrc
中添加 set smoothscroll
即可。
|使用 VIM 的过滤功能 (Filter)
过滤功能(Filter)其实翻译不是很准确,本质上就是文本和外部命令的交互。VIM 中可
以通过 !
与调用外部命令。举个例子,在命令行模式输入 :!ls
就可以列出当前文
件所在目录下的所有文件了:

然后 VIM 还可以把文件的文本传出去,调用外部命令处理。例如我要在下面随机生成 10
个 100 以内的整数,然后再从小到大排序。可以写下 Python 代码,再传给外部 Python
处理:

这个示例调用了 Python 和 Linux 的 sort
命令。选择范围我做了两次演示:第一次
是使用行数范围,从第 257 到第 259 行;第二次是使用可视(块)模式,直接选定区域。
不过对于很多人来说,这可能没什么用,毕竟写代码的人才会意淫这种用法,是吧?其实
不然,举个例子,我现在要画一个图,只需要在 Markdown 内写下这个图的流程,然后调
用外部命令处理即可:

上面使用的命令是 {range}w !{command}
:想看自己写的画图代码如何,只需要传出代
码,不需要读入,所以使用 w !{comamnd}
(write),如果只是读入则使用r !{command}
(read),传出又读入使用 !{command}
。
觉得画得不错,可以在当前目录直接生成图片 test.png
,然后引用:


何如?这其实有点像 VS Code 的 Code Runner
(好像是这个名字,忘了),但是更灵
活。
没办法,字数有点多了,下次再写。
后语
其实之前我想写写自己写的 Zotero 引用插件,但是那时候使用 vimscript 比较年轻,
有点迷信 Lambda 表达式,导致自己的代码充斥着大量的 Lambda 表达式,后面自己都看不懂。
下面可以看看我之前写的 Lambda,都要走火入魔了。因为自己也看不懂代码,所以就一直没写。主要是那时候以我的知识知道,调用函数其实会拖慢速度,觉得 Lambda 可能快
一点,其实也不然。最近用 vim9script 对自己的插件进行了重构(高大上的词),可能
会真的写一篇文章讲讲 VIM 的插件怎么写。

相关网站
Typora: https://typoraio.cn
[2]
MarkText: https://github.com/marktext/marktext
[3]
Zettlr: https://zettlr.org
[4]
Better BibTex: https://retorque.re/zotero-better-bibtex/
本内容由作者或发布人投稿发表,只做展示阅览使用。发布者:欣IT,转转请注明出处:https://www.xinenw.com/4945