【Mastering Vim 2_05】第四章:深入理解 Vim 的结构化文本

devtools/2025/2/23 5:33:47/

最新版《Mastering Vim》封面,涵盖 Vim 9.0 版特性

【最新版《Mastering Vim》封面,涵盖 Vim 9.0 版特性】

文章目录

  • 第四章 深入理解结构化文本
    • 1 Vim 内置的自动补全功能
    • 2 YouCompleteMe 插件对自动补全的增强
    • 3 tags 文件的用法
    • 4 Exuberant Ctags 简介
    • 5 借助 Undotree 插件实现 Vim 撤销树的可视化

写在前面
本章围绕 Vim 的结构化文本做了一些探索,从文本的自动补全开始,进而过渡到文本高亮和函数、方法等的快速定位,最后提到了 Vim 独有的撤销树操作,没有特别复杂的知识点。美中不足的是对 YouCompleteMe 插件的介绍过于笼统,不具备实战价值。后面得空再来尝试一下 WSL 和 Windows 环境的安装应用。

第四章 深入理解结构化文本

本章概要

  • Vim 自带的自动补全功能与 YouCompleteMe 插件简介
  • Exuberant Ctags 在浏览大型代码库中的应用(实现声明和引用间的即时跳转)
  • Undotree 插件在浏览 Vim 的复杂撤销树中的具体用法

相关源码:https://github.com/PacktPublishing/Mastering-Vim-Second-Edition/tree/main/Chapter04


1 Vim 内置的自动补全功能

打开第四章源码文件 Chapter04/welcome.py,任意位置输入 pr,然后在插入模式下按 Ctrl + N,将弹出一个 Vim 自带的自动补全候选列表:

图 4.1 Vim 内置的自动补全列表

【图 4.1 Vim 内置的自动补全列表】

常见操作:

  • 选中下一项:Ctrl-n
  • 选中上一项:Ctrl-p
  • 继续输入其他内容:自动补全列表消失

此外,Vim 还提供了一个 插入补全(insert-completion) 模式,支持多种补全方式。常见的补全方式有以下四种,通过键入 CtrlX 与以下按键相结合得到不同的自动补全列表:

  1. CtrlL:以整行形式自动补全(如图 4.2 所示);
  2. Ctrl]:基于标签(tags)进行自动补全(如图 4.3 所示);
  3. CtrlF:基于当前路径下的所有文件名提供自动补全列表(如图 4.4 所示);
  4. S:在打开语法检查的情况下(:set spell + Enter),基于语法修改建议提供自动补全列表(如图 4.5 所示);

以下截图为上述四种补全类型分别在示例文件 welcome.py 中的实测效果:

图 4.2 基于当前文件的整行内容(Ctrl + XL)提供自动补全候选列表

【图 4.2 基于当前文件的整行内容(Ctrl + XL)提供自动补全候选列表】

图 4.3 基于 tags 标签(Ctrl + X])提供自动补全候选列表(关于标签的具体含义稍后详述)

【图 4.3 基于 tags 标签(Ctrl + X])提供自动补全候选列表(关于标签的具体含义稍后详述)】

图 4.4 基于当前路径下的文件名(Ctrl + XF)提供自动补全列表(注意:本例无需输入 “pr” 字样,否则没有匹配项)

【图 4.4 基于当前路径下的文件名(Ctrl + XF)提供自动补全列表(注意:本例无需输入 “pr” 字样,否则没有匹配项)】

图 4.5 基于当前输入的语法建议提供自动补全列表(需要先启用 set spell 语法检查)

【图 4.5 基于当前输入的语法建议提供自动补全列表(需要先启用 set spell 语法检查)】

更多插入补全模式的用法介绍,详见 Vim 帮助文档(:help ins-completion:help 'complete'):

图 4.6 Vim 内置的插入补全模式帮助文档页截图

【图 4.6 Vim 内置的插入补全模式帮助文档页截图】

YouCompleteMe__66">2 YouCompleteMe 插件对自动补全的增强

YouCompleteMe 插件是对 Vim 内置的自动补全特性的增强,可根据不同的编程语言提供特定的语法支持。只不过这一节感觉作者不怎么用心,按书中给出的操作很难一次性安装成功,因为插件本身有很多隐形限制条件:

  • 插件对每种语言的支持情况各不相同,需要提前配置 Vim 的语法兼容包。例如对 +python3 的编译支持;
  • 如果想利用插件在 Python 环境下的自动补全,解释器版本还不能低于 v3.9
  • 手动安装时还不能以 root 用户进行安装(权限过高);
  • ……

为了一个自动补全,还得提供这么多周边依赖支持,于是果断放弃。后面有空再来尝试安装。以下为书中提供的操作步骤:

# 1. 安装相关依赖
$ sudo apt install cmake llvm
# 2. 检查当前 Vim 版本是否支持 python3
$ vim --version | grep python3
+cmdline_info      +libcall           +python3           +virtualedit
# 3. 在 vimrc 文件中加入下列 Vim-Plug 配置内容
" Increase vim-plug timeout for YouCompleteMe.
let g:plug_timeout = 300
Plug 'ycm-core/YouCompleteMe', { 'do': './install.py' }
# 4. 保存配置并立即安装插件
:w | so $MYVIMRC | PlugInstall

安装过程中,如果遇到像 c++: internal compiler error: Killed (program cc1plus) 这样的报错信息,说明设备的可用内存不足,可以尝试下列方式扩充交换空间(Linux 环境下):

# 用 dd 命令临时开辟 1Gb 大小的交换文件
$ sudo dd if=/dev/zero of=/var/swap.img bs=1024k count=1000
$ sudo mkswap /var/swap.img
$ sudo swapon /var/swap.img

安装就绪后,编辑任意内容应该能看到如下效果:

图 4.7 <a class=YouCompleteMe 插件生效后的自动补全列表效果" />

【图 4.7 YouCompleteMe 插件生效后的自动补全列表效果】

YouCompleteMe 的列表项可通过 Tab 键切换,并且支持在文件顶部展示选中项的 docstring 文档。

如果在 Python 环境下工作,YouCompleteMe 还支持自动跳转到光标所在函数的定义处,执行命令为::YcmCompleter GoTo + Enter。也可以用上一章介绍的 noremap 语法,将其映射为某个快捷键(例如 Leader 键 + ]):

noremap <leader>] :YcmCompleter GoTo<cr>

3 tags 文件的用法

查看某个方法的定义是代码编写过程中的常见操作,为此 Vim 也提供了原生支持,通过按 gd 就能将光标所在的单词对应的定义高亮显示出来,例如:

图 4.8 Vim 原生支持函数定义的定位

【图 4.8 Vim 原生支持函数定义的定位】

虽然 Vim 不提供相应的语义支持,但 Vim 支持 tags 标签。Vim 中的标签是一个具有语义内涵的单词和结构的文件。在 Python 语境下,类、函数、方法都可以视为 Vim 标签。

Ctags__127">4 Exuberant Ctags 简介

Exuberant Ctags 是一个外部工具(详见:http://ctags.sourceforge.net),实现了对 Vim 标签特性的增强。通过在当前代码库生成一个对应的 tags 文件,可以对代码中的函数及方法的定义进行快速定位(支持跨文件模块操作)。

安装步骤如下:

# 适用环境:Debian
$ sudo apt install universal-ctags
# 在当前文件夹下生成 tags 文件
$ ctags -R .
$ ls
README.md  kitchen  tags  welcome.py

再次打开 welcome.py,将光标移至第 10 行的方法名 prepare 下,按 Ctrl] 将自动定位并打开 kitchen/egg.py 文件:

图 4.9 通过 ctags 定位到 prepare 方法的一处定义

【图 4.9 通过 ctags 定位到 prepare 方法的一处定义】

ctags 相关的几个操作:

  • Ctrl-]:进入光标所在标签的(其中一个)定义处;
  • Ctrl-t:返回到光标所在标签对应的引用位置;
  • :tselect:简写为 :ts,列出光标所在标签可能的定义位置列表;也可以直接使用 g] 组合键(如图 4.10 所示);
  • :tn:tp:适用于存在多个同名标签的情况,分别实现上翻和下翻。

图 4.10 执行命令 :tselect 列出的多个定义位置信息,输入对应的数字 + 回车键,即可跳转到对应的文件

【图 4.10 执行命令 :tselect 列出的多个定义位置信息,输入对应的数字 + 回车键,即可跳转到对应的文件】

甚至可以在 Vim 外通过命令 vim -t <tag_name> 直接将匹配到的定义文件用 Vim 打开。例如,将匹配到的第一个 prepare 标签定义文件用 Vim 打开,可以执行命令:vim -t prepare + Enter;效果和 vim kitchen/egg.py + Enter 一致。

注意

ctags 的有效定位离不开 tags 文件,并且需要启用 Vimtags 选项,设置为 :set tags=tags;。这里末尾的分号 ; 必须写全,否则无法在其他父级目录中检索。为了让 tags 文件所在的代码库实时更新 tags 的内容,可以在 Vim 每次保存文件时更新 tags,即在 vimrc 文件中实现以下配置:

" Regenerate tags when saving Python files.
autocmd BufWritePost *.py silent! !ctags -R &

这里的 *.py 是仅对 Python 源码文件生效,也可以根据实际情况进行扩充,例如:

autocmd BufWritePost *.cpp,*.h silent! !ctags -R &

最后需要注意的是,Vim 的这种快速定位方案的特点也是它的短板:必须单独生成一个 tags 文件放到项目根目录。相比其他 IDE 确实感觉要 low 很多(实际项目中我个人是不会考虑用 ctags 的)。

5 借助 Undotree 插件实现 Vim 撤销树的可视化

之前学过 Vim 的撤销操作(u 命令),本节对这一知识点进行了扩展。Vim 除了简单的撤销当前操作外,还支持更为复杂的撤销树(undo tree)操作。这可以通过 Undotree 插件进行可视化展示,如图 4.11 所示:

图 4.11 利用 Undotree 插件实现 Vim 撤销树的可视化效果

【图 4.11 利用 Undotree 插件实现 Vim 撤销树的可视化效果】

应用场景:文件编辑过程中,先进行一次内容变更(记为 X),然后用 u 命令撤销变更;再进行第二次变更(记为 Y),然后再用 u 撤销。此时,多次执行 u 命令只能还原变更 Y 的内容,人们普遍认为变更 X 是无法还原的。想撤销到变更 X,可利用 Undotree 插件的可视化界面来实现。

安装方法(通过 Vim-Plug 工具):

  1. 打开 vimrc 文件,在 call plug#begin()call plug#end() 之间新增一行内容:Plug 'mbbill/undotree'
  2. 保存并快速安装插件,执行命令::w | so $MYVIMRC | PlugInstall + Enter

要显示 Vim 撤销树,输入命令 :UndotreeToggle + Enter 即可(如图 4.11 所示)。

此外也可以自定义一个快捷键来快速切换撤销树的显示与隐藏,如 <f5> 键,配置到 vimrc 文件中:

" Map Undotree to <F5>
noremap <f5> :UndotreeToggle<cr>

撤销树的操作和其他窗口类似,支持方向键 JK 上下导航,按 Enter 键加载对应版本的撤销内容。Undotree 插件提供的侧边栏分为上下两部分:上方为撤销历史记录的树形结构;下方为当前光标对应版本的具体变更情况(风格类似 Git)。

更多用法,可以通过 :help undo-tree 进行了解。


http://www.ppmy.cn/devtools/161108.html

相关文章

Windows 快速搭建C++开发环境,安装C++、CMake、QT、Visual Studio、Setup Factory

安装C 简介 Windows 版的 GCC 有三个选择&#xff1a; CygwinMinGWmingw-w64 Cygwin、MinGW 和 mingw-w64 都是在 Windows 操作系统上运行的工具集&#xff0c;用于在 Windows 环境下进行开发和编译。 Cygwin 是一个在 Windows 上运行的开源项目&#xff0c;旨在提供类Uni…

JavaScript异步编程方式多,区别是什么?

在JavaScript中&#xff0c;常见的异步编程方式有回调函数、Promise、Generator函数和async/await&#xff0c;以下用大白话介绍它们的区别并给出代码示例&#xff1a; 回调函数 概念&#xff1a;就是把一个函数当作参数传给另一个函数&#xff0c;等那个函数完成任务后再调用…

前端新手如何从CtrlC+V开始?(前端开源UI平台汇总)

前言 如果你是个前端小白&#xff0c;面对一堆满屏的div标签和css就头晕眼花&#xff1f;别担心&#xff0c;咱都是从“代码搬运工” 开始的。当你的同桌还在和flex布局玩"你动我猜"的时候&#xff0c;你已经像拼乐高一样把现成的按钮组件搭成炫酷界面。这可不是作弊…

RabbitMq 基础

文章目录 一、初识 MQ 1.1 同步调用&#xff1a;1.2 异步调用&#xff1a; 二、RabbitMQ三、SpringAMQP 3.1 依赖和配置文件3.2 消息发送和接收&#xff1a; 3.2.1 消息发送&#xff1a;3.2.2 消息接收&#xff1a; 3.3 WorkQueues 模型&#xff1a;3.4 交换机类型&#xff1a…

Git环境搭建指南

Git 是当今最流行的版本控制系统&#xff0c;无论是个人开发还是团队协作都离不开它。本文将从零开始&#xff0c;手把手教你 在Mac、Windows、Linux三大操作系统上快速搭建Git环境&#xff0c;并验证安装是否成功。 # 一、Mac系统安装Git # 方法1&#xff1a;通过Homebrew安装…

nginx负载均衡, 解决iphash不均衡的问题之consistent

原因分析 客户端IP分布不均&#xff1a;部分IP段请求集中&#xff0c;导致哈希到同一后端。 服务器数量变动&#xff1a;增删节点时&#xff0c;传统ip_hash未使用一致性哈希&#xff0c;导致分布重置。 哈希键范围过小&#xff1a;例如仅使用IPv4前24位&#xff0c;不同IP可…

简单易懂,解析Go语言中的Map

目录 3. map3.1 初始化3.2 增删改查3.3 源码3.4 负载因子3.5 扩容 3. map 3.1 初始化 var/new声明nil map;make初始化map同时可以指定容量;字面量;向nil map中插入会报panic func main() {var m1 map[int]int //panic: assignment to entry in nil mapm2 : *new(map[int]i…

Flutter CupertinoNavigationBar iOS 风格导航栏的组件

CupertinoNavigationBar 是 Flutter 中用于创建具有 iOS 风格导航栏的组件&#xff0c;它提供了类似 iOS 应用中导航栏的外观和交互效果。下面将详细介绍它的相关信息和具体用法。 特点 iOS 风格&#xff1a;具有 iOS 系统原生导航栏的外观和动画效果&#xff0c;包括标题样式…