之前介绍过众多的motion,根据移动范围来排序的话有 l
、e
、w
、j
等等,但是面对那么长的代码文件,仅仅使用这几个简单的motion不知道要移动多少次才能找到我想要的代码,这个速度有时候还不如我用鼠标移动光标。vim作为编辑器之神当然提供了快速移动光标的方式了,这篇文章我们就来了解一下如何使用vim在代码间进行快速跳转。
利用标签,快速跳转
vim中提供了标签的方式进行跳转,事先可以在对应位置设置标签,后面通过标签访问该标签所在位置
可以使用m{a-z}
来在任意位置设置标记,而后使用`{a-z}来回到对应标记位置。该命令可以回到之前设置标签时光标所在行和列。
vim可以支持从a到z的26个位置标记,一般来说我们用不到这么多,即使你能全部用到,可能早就忘了前面标记的在哪个位置了。这种方式有一个最大的问题就是在标记之后从显示上无法知道我们的标记位于何处。
除了由用户主动使用m
来设置位置标记以外,vim还会自动为我们设置标记,例如上次修改、上次跳转、上次高亮等等。
下表列举出了,如何回到这些vim自动标记所在位置
位置标记 | 含义 |
---|---|
`` | 当前文件中上次跳转动作之前所处的位置 |
`. | 上次修改的地方 |
`^ | 上次进入插入模式的位置 |
`[ | 上次修改或者复制的起始位置 |
`] | 上次修改或者复制的结尾位置 |
`< | 上次高亮选区的起始位置 |
`> | 上次高亮选区的结尾位置 |
在匹配的括号间进行跳转
可以使用 %
在一组括号中使用,可以跳转到下一个匹配的()
、[]
、{}
。例如下列操作
我们可以配合operator
来使用,删除括号中的内容。例如下面的代码
var foo = {
"obj":{
"test": "1",
"arr": [1, 2, 3]
}
}
可以使用将光标移动到对应的位置,然后使用d%
就可以删除对应的内容了。当然也可以使用文本对象来进行
跳转列表
浏览器中会记录浏览历史,并且提供了去到上一页和下一页的功能。vim中也提供了类似的功能,vim会记录我们每一次的跳转,可以通过相关命令来跳转到上一次跳转和下一次跳转的位置。
我们先介绍什么是跳转。跳转似乎很容易理解,似乎光标每次的移动都算是一次跳转。但是vim中的跳转并不是这样的。我们可以先这样理解,motion
允许我们在一个文件中进行移动。而跳转则是不同文件间的移动。就像在浏览器中从一个页面打开另一个页面。为了类比浏览器的操作,你也可以把每次跳转记录理解成历史访问文件的记录。
可以使用:jumps
来查看跳转记录。
从上图中可以看到这样几个现象:
- 跳转列表中记录了所在文件以及上次光标所在的行和列。最后几行由于我们处在当前文件中,所以没有列出文件名称来,而是直接给出光标所在行的文本内容,由于我这里打开文件之后立即查看了跳转列表,光标处于第0行这个虚拟行,所以会显示空白内容。
- 它记录了光标所在的行列,所以后面我们在恢复的时候可以直接定位到具体位置。
- 与浏览器类似,之前打开vim的时候访问文件的记录也在里面,它并没有随着vim的关闭而被清除。
- 任何能改变当前窗口中活动文件的命令都可以作为跳转命令,像
find
、edit
之类的。
了解了跳转列表之后,我们现在来访问一下这个跳转列表。可以使用<Ctrl i>
来访问前一个跳转,<Ctrl o>
来访问后一个跳转。
在nvim-config
中随意打开一个文件,然后使用edit
打开另一个,接着就可以使用<Ctrl i>
和 <Ctrl o>
在两个文件中切换了
我们再来联想一下浏览器中的历史记录,我们发现有时候访问同一个页面的不同位置可能会产生多条历史记录。例如访问同一页面的不同锚点。那么我们之前说的将跳转理解为历史文件访问记录可能就不对了,同一个文件也可以产生多个跳转记录。 例如gg(G)
、%
、{a-z}
等等。而h j k l w f
之类的就不作为一次跳转。用一句话来总结就是大范围的光标移动才会被作为一次跳转。什么会被作为大范围的移动呢?我个人的理解是一次移动有能力移动至少半屏,而像50j
之类的虽然也可以移动50行,也达到半屏以上,但是前面加数字表示的是重复,它是重复了多次,并不算一次移动。
我们使用 split 或者 vsplit 再打开一个新的窗口,然后在两个窗口中分别使用:jumps 发现二者并不相同。vim可以维护多套跳转列表,每个窗口都有自己的一套独立的跳转列表。这个与浏览器中的也类似,新窗口并不能进行前进和后退操作,而且只两个操作也只能跳转到由这个窗口打开的网页上。
改变列表
回忆一下,我们不管在文件的哪个位置,使用u
撤销修改的时候光标总能跳转到对应修改的位置,或者使用.
能回到上次修改的位置。如果以前没有注意这个细节的,也可以现在试试。
vim在会话期间会维护一张表,表里记录了每个缓冲区的每一次修改。这个就是所谓的改变列表。可以使用:changes
来查看这个列表
这个列表与跳转列表类似,都标记了行号与列号。我们可以通过g;
和 g
, 来访问下一个和上一个记录。你可以拿;
和,
来类比记忆。这两个操作符是配合f
来使用的。;
移动到下一个匹配位置,,
移动到下一个匹配位置。
我们可以使用.
来跳转到上一次修改的位置,而 `^
则更具体一点。它代表的是上一次退出插入模式光标所在位置。如果我们在做出修改并且退出插入模式之后,移动光标查看了下其他类似代码的实现,然后想快速回到之前编辑的位置继续编辑,可以使用 `^
将光标移动到对应位置,然后使用i进入插入模式,当然也可以使用gi
一步到位,直接从上次编辑位置进入插入模式。
需要注意的是,vim会为每一个打开的窗口维护一个跳转列表,但是更新列表只有一个,而且跳转列表并不会随着vim的退出而消失,但是改变列表则会随着vim的退出而被清空。
跳转到光标下的文件
在我们将当前项目所在的所有路径加入到path中之后(即在项目根目录中执行:set path =./**)可:set 以将光标移动到对应表示相对路径的代码上,执行gf 即可跳转到对应文件。
在上面的例子中,我们只写了settings
这样的文件,它是如何知道要打开 settings.lua
文件的呢,或者说如果有类似的settings.h
或者 settings.js
在同一个位置的话,它该打开哪一个呢?
比如说我们新建一个settings.h
在同样的目录中,再次执行之前的操作,发现它还是能够正确的打开settings.lua
vim 中有一个suffixesadd
变量,它保存的当前缓冲区中执行gf
操作时,可以使用的扩展。我们可以像设置path
一样,例如:set suffixesadd =.java
来允许打开java文件。
使用gf
也是一个跳转,也会被记录到跳转列表中,后续我们可以使用之前介绍的<Ctrl o>
和 <Ctrl i>
来回的在两个文件中切换。
使用全局书签在文件间跳转
之前介绍过在文件中可以使用标记,在文件不同位置进行跳转。那个时候说到使用小写字母设置标记,小伙伴们可能会产生疑惑,那大些字母去哪了呢,为什么只能使用小写字母,而大写字母被空着呢?文章写到这里了,我可以告诉大家,大写字母被用到了全局书签里面。
全局书签与之前介绍的标记使用方式一模一样,只是一个使用大写字母,一个使用小写字母。例如在上面一个例子中,我们在跳转到settings.lua
之前先使用mI
在init.lua
中一个标签,在跳转之后使用 `I
快速跳转回来。
好了,本篇内容就到这里了。下一次将介绍寄存器相关内容。再次感谢大家的阅读