vimbook-OPL(11-13)

CH11 Dealing with Text Files(处理文本)

这一章主要讲将 Vim 用作类似字处理器时所提供的功能,包括自动文本回绕,文本格式化命令,自动文本格式化选项(formatoptions)以及处理不同操作系统下的换行符还有就是字处理器 Troff 相关的命令,最后介绍了 Vim 自带的 rot13 算法混淆功能。

11.1 自动文本回绕

自动文本回绕在处理注释或者写文档时十分有用,一般我们要求文档一行的长度不要超过 80 字符,如果每次都需要手动插入换行符是相当累的一件事。Vim 提供了自动换行的功能,通过选项 :set textwidth=80 当输入的文本长度超过 80 字符时将自动插入一个换行符。还有一种可以设置右边边距的选项 :set wrapmargin=10 当插入的字符距离屏幕右边 10 列时将自动插入一个换行符。这个选项只有在 textwidth 为 0 的情况下才会生效。书中原文是 The ’textwidth’ option overrules ‘wrapmargin’.

gq{motion} 命令能够对选中的文本块进行自动排版,gq 在未配置的情况下使用内部排版机制,内部排版机制依据 textwidth 和 formatoptions 来进行排版。{motion} 可以是 4j 这样的表示下行 4 行,更为常见的是 } 表示从当前位置到段落尾部,或者 ip 表示当前段落。ip 这种 {motion} 是文本对对象在帮助文档 :h object-select 中可以找到完整的解释。

gqq 可以格式化当前行。

11.2 文本排版

  • :[range]center [width] 将范围 range 中的文本按照 width 长度进行中间对齐,width 缺省为 textwidth;
  • :[range]right [width] 将范围 range 中的文本按照 width 长度进行向右对齐,width 缺省为 textwidth;
  • :[range]left [indent] 将范围 range 中的文本按照 indent 长度从左边缩进,不指定则为 0,表示向左对齐;

以上 range 请参考 vimbook-OPL(5-10) 篇中的 print 命令,里边详细讲解了各种 range 的选择方式。我想到一个有用的场景就是在写文档的时候,我们需要对某些代码的句子或者一些配置进行居中对齐,用 center 命令就不用收到缩进了。

11.3 格式化微调

‘joinspaces’ 选项可以调整当用 J 命令粘合两行文本时,如果第一行以 ‘.’ 、’?’ 和 ‘!’ 之后插入两个空格,如果设置的是 ’nojoinspaces’ 则只插入一个空格。‘formatoptions’ 在第 9 章已经详细讲解过了,详细查看帮助文档 fo-table 标签。

Vim 自动排版时默认用的内部的排版机制,通过 ‘formatprg’ 选项可以设置外部程序作为 gq 的排版机制,如在 Linux 下 :set formatprg=fmt 将 fmt 程序作为排版程序。或者调用 !ip fmt 直接格式化本段内容。

11.4 文件格式

文件在不同的系统下有以不同的符号作为换行符,典型的 Linux/macOS 以 <NL> 作为换行符,Windows 以 <CR><NL> 作为换行符。Vim 编辑器能够自动识别文件格式,并用正确的换行符。Vi 没有功能,所以对待 Windows 下的换行符会显示一个 ^M 字符。通过设置 :set fileformats=unix,dos 可以告知 Vim 先以 unix 方式识别文件,如果不成功再以 dos 形式识别文件。如果只指定一种格式那么当文件格式不一致时会发生转换。

当打开选项 ’endofline’ 时,会自动在文件的末尾加上一个换行符。这个标记在通常在文本文件都是设置的,因而不需要怎么关注。而在二进制文件中可以设置 ’noendofline’ 来关闭自动添加换行符功能。

11.5 Troff 相关的移动

这里介绍 句子(sentence) 段落(paragraph)小节(section) 的概念。在代码中我们一般并不会用到这些概念,在书写文档时或者一般性文章时还是蛮有用的。

  • 句子:指的是以标点符号 ‘.’ ‘!’ 或者 ‘?’ 结尾并紧随一个换行符、空格或者制表符的一串文本,小节和段落的边界也是句子的边界。motion 命令 [count]( 反向 count 个句子, [count]) 正向 count 个句子;

  • 段落:指的是以空行或者段落宏分隔的一大段文本,小节的边界也是段落的边界。空行必须是只有一个换行符,不能有空白字符。motion 命令 [count]{ 反向 count 个段落, [count]} 正向 count 个段落。段落宏指的就是 Troff 的段落宏,通常以点号和两个字符定义,如: .IP .LP 等,点号必须出现在第一列。Vim 中识别的段落宏在选项 ‘paragraphs’ 中定义,段落宏以成对出现的所定义,默认的段落宏如下:

    :set paragraphs=IPLPPPQPP<Space>TPHPLIPpLpItpplpipbp
    
  • 小节:指的是以首列 <C-L> 或者小节宏命令分隔的文本。小节宏以 ‘sections’ 选项中成对出现的字符定义。缺省的小节宏如下:

    :set sections=SHNHH<Space>HUnhsh
    

    通过 ]] ][ 移动到下一个小节,在 C 语言中 ]] 前进到下一个首列 { ,][ 出现在下一个首页 } 处。通过 [[ [] 移动到上一个小节,在 C 语言中 [[ 移动到上一个首列 { 处,[] 移动到上一个首列 } 处。

11.6 用 Rot13 混淆文本

Rot13 是一个很弱的混淆算法,因为英文字母有 26 个,将任意一个字母加上 13 得到的新的字母,对于相加大于 26 的字母进行回绕。这样对任意字母加两次之后又还原回的字母。g?motion 将 motion 表示的文本块进行 Rot13 加密,当需要阅读原文时用 g?motion 就有可以了。g?? 对当前行进行加密。

CH12 Automatic Completion

12.1 自动补全

按住 CTRL-P 可以回溯搜索匹配当前光标的单词,CTRL-N 进行前向搜索,如果光标下什么也没有输入就会回溯遍历所有单词。Vim 的搜索策略如下:

  1. 当前文件;
  2. 其它窗口的文件;
  3. 其它缓冲列表中的文件;
  4. 被卸载的缓冲文件(:bd命令进行卸载);
  5. 当前tags文件中的标识符;
  6. #include 头文件;

自定义补全

:set ignorecase 选项告知 vim 搜索时进行大小写不敏感匹配,设置 :set infercase 可以让 vim 推断光标所在单词的大小写,从而可以转为合适的大小写;用 :set complete=key,key,key 设置补全的搜索顺序,缺省为 “.,w,b,u,t,i” key的含义如下:

.   当前缓冲
b   加载的缓冲但是不在窗口中
d   在当前文件和 #include 中的定义
i   由 #include 包含的文件
k   'dictionary' 选项定义的文件
k{file} 文件{file}中的单词
t   tags文件中的标识符
u   卸载的缓冲
w   其它窗口中的文件

vim 使用 ‘path’ 选项来指示从哪些目录查询 #include 文件,默认值是 .,/usr/include,,,设置补全的查找字典文件,通过设置 ‘dictionary’ 来指定,每个文件用逗号分隔。

控制搜索项目

CTRL-X CTRL-D  搜索宏定义
CTRL-X CTRL-F  搜索文件名
CTRL-X CTRL-K  搜索字典
CTRL-X CTRL-I  搜索当前文件和 #include 文件
CTRL-X CTRL-L  搜索整行
CTRL-X CTRL-]  搜索 Tags,默认情况下只显示标签名字,当设置 'showfulltag' 选项时将显示完整的函数原型

CH13 Autocommands

13.1 介绍

自动命令是在特定的事件发生时自动执行的命令。事件是 Vim 预设的,比如文件打开、文件写入、文件关闭等。命令:

1
2
3
4
5
:function DateInsert()
:   $read !date
:endfuntion

autocmd FileWritePre * :call DateInsert()<CR>

将在用户写入文件前一刻执行插入时间的自动命令,自动命令的一般形式如下:

1
:autocmd [group] events file_pattern nested command

其中 group 是可选的,用来管理和调用命令。events 是一系列逗号分隔的预定义的事件,file_pattern 是文件名的规则表达式,nested 标识允许自动命令嵌套,允许一个自动命令再触发另一个自动命令,例如:

1
:autocmd FileChangedShell *.c nested e!

以上命令中的 e! 将会触发 FileReadPre 事件。

command 就是最终执行的命令。

13.2 Groups

:augroup 用来开启自动命令组的定义,后面跟随组名。例如:

1
2
3
4
:augroup cprograms
:   autocmd FileReadPost *.c :set cindent
:   autocmd FileReadPost *.cpp :set cindent
:augroup END

以上两个命令就包含在组 cprograms 中,如果你想再添加一个新的命令到组中你可以执行如下命令:

1
:autocmd cprograms FileReadPost *.h :set cindent

自动命令也可以手动执行,例如现在正在编辑 sam.cx 文件,如果需要对其执行 cprograms 组命令,得用一下方式调用:

1
:doautocmd cprograms FileReadPost foo.c

假装当前文件是 foo.c 并以 cprograms 组命令执行 FileReadPost 事件。通用命令格式如下:

1
:doautocmd group event file_name

指的是如果省略 group 将会查找所有的组,如果省略 file_name 将以当前文件的文件名去执行 event ,其中 event 是不可以省略的。

13.3 Events

预定义的事件如下表:

PreEvent PostEvent 触发时机
BufNewFile 当编辑不存在的文件时触发
BufReadPre BufReadPost 在读取缓冲前/后触发
BufFilePre BufFilePost 当用:file 命令改变一个缓冲的文件名前/后
FileReadPre FileReadPost 当用:read 命令读取一个文件的前/后
FilterReadPre FilterReadPost 在使用过滤(filter)命令读取前/后
FileType 当filetype 选项被设置时
Syntax 当 syntax 选项被设置时
StdinReadPre StdinReadPost 当冲标准输入输出读取前/后
BufWritePre BufWritePost 将缓冲写入到文件的前/后
BufWrite 与 BufWritePre 一样
FileWritePre FileWritePost 写入缓冲的一部分到文件中
FileAppendPre FileAppendPost 添加到文件的前/后
FilterWritePre FilterWritePost filter 命令写入文件的前后
FileChangedShell 当vim执行shell时发现文件的的修改时间发生改变
FocusGained FocusLost 当 GUI 版本的 vim 获得、丢失输入焦点时
CursorHold 当用户停止输入updatetime选项指定的时间时会执行
BufEnter BufLeave 当buffer进入或离开时触发
BufUnload 当缓冲卸载时触发
WinEnter WinLeave 当进入或离开窗口时触发
BufCreate BufDelete 当缓冲被创建或者删除时触发
VimEnter 当 vim 被打开并且初始文件被执行之后触发
VimLeavePre 当 Vim 被关闭并且 .viminfo 还没有写入
VimLeave 当 Vim 被关闭并且 .viminfo 已经写入内容
FileEncoding 当 fileencoding 选项被设置时触发
TermChanged 当 term 选项被改变时触发
User 当 :doautocmd 时可以指定此事件,vim 不会触发

13.4 File Patterns

自动命令的文件正则表达式运用了 UNIX 的标准,以下是一些正则的用法:

符号 含义
* 匹配任意长度的所有字符
? 匹配任意单个字符
, 分割多个表达式
\? 精确匹配 ? 字符
\, 精确匹配 , 字符
\character character 作为搜索模式字符,如 + 等

实用命令

:autocmd 列举所有自动命令,更一般的 :autocmd group [event] [pattern] 会列举在 group 下的 event 事件下的文件类型的自动命令。event 可以是 * 号表示匹配所有事件。用 :autocmd! 将删除所有自动命令,:autocmd! group [event] [pattern] 将将删除匹配的自动命令。以下命令可以忽略特定事件的自动命令,如:

1
:set eventignore=WinEnter,WinLeave

将忽略窗口进入和离开事件,调用 :set eventignore=all 将忽略所有事件。