# vim ## Buffers,Windows,Tabs 在现代的文本编辑器,通常会存在`windows`和`tabs`两种抽象。但是,vim和常用的文本编辑器不同,其抽象组成如下: - windows - tabs - buffers ### Buffer buffer是一个内存空间,可以向buffer`写文本`或`编辑文本`。当通过vim打开一个文件后,文件的内容就被关联到buffer中,当打开3个文件,就存在3个buffer。 可以通过如下命令打开多个buffer: ```bash vim file1.txt file2.txt ``` 每当通过vim打开一个新文件,都会为vim创建一个buffer。 如果想要在多个buffer之间进行切换,可以使用如下方式。 #### `:bnext` `:bprevious` 通过`:bnext`和`:bprevious`命令,其支持在多个buffer之间进行切换,切换到前一个/后一个buffer。 #### `:buffer {filename}` 通过`:buffer {filename}`的方式,可以切换到`{filename}`关联的buffer。 > filename可以通过`tab`进行自动补全。 #### `:buffer {n}` 通过`:buffer {n}`的形式,可以通过数字来进行buffer的跳转。 #### `ctrl + ^` 通过`ctrl + ^`的快捷键组合能够跳转到上次编辑的buffer。 如果想要对查看所有buffer,可以通过如下方式: - `:ls` - `:files` - `:buffers` #### `:bdelete` 一旦vim创建了buffer,其将会存在于buffer list中。如果想要移除buffer list中的buffer,可以通过`:bdelete`的方式移除,其支持接收buffer number和file name,示例如下: ```bash :bdelete 1 ``` ```bash :bdelete file1.txt ``` #### exit vim with all buffers closed 如果存在多个buffer,想要关闭所有buffers,可以使用如下方式: ```bash :qall ``` 关闭所有buffer,并且不保存修改 ```bash :qall! ``` 保存并关闭所有buffer ```bash :wqall ``` #### `:e {filepath}` 在当前存在buffer list的同时,还想打开新的文件,可以通过`:e {filepath}`的命令来实现 ```bash :e ~/f2.txt ``` ### windows window是一个buffer的viewport。和目前主流的编辑器类似,vim同样支持打开多个windows,在界面同时展示多个buffer的内容。 #### `:split {filename}` 在当前已经存在buffer被展示到window的情况下,如果想要同时展示多个window,可以使用`:split {filename}`,示例如下: ```bash # 打开一个文件 vim f1 # 打开f1后,此时目前只有一个buffer和一个window,如果此时想要再打开一个window # 打开f2并展示到f1 window的上部分 :split f2 # 执行完上述命令后,vim被分割为两个window,上面为f2,下面为f1 ``` #### 切换窗口 如果想要在多个window之间切换焦点,可以使用如下快捷键(仅在normal模式下) - `ctrl + w + h`:切换到左侧窗口 - `ctrl + w + l`:切换到右侧窗口 - `ctrl + w + j`:切换到下侧窗口 - `ctrl + w + k`:切换到上侧窗口 之后,可以运行如下命令 ```bash :vsplit f3 ``` 运行之后,vim会展示3个窗口,上半部分展示`f3, f2`两个窗口,下半部分展示`f1`窗口。 #### 关闭窗口 如果想要关闭当前窗口,可以调用`ctrl + w + c`快捷键,或是通过`:q`命令。`即使关闭window,关闭文件的buffer仍然存在于buffers中`。 #### split窗口快捷键 - `ctrl + w + s`: 水平方向针对当前buffer打开一个window - `ctrl + w + v`: 垂直方向对当前buffer打开一个window - `ctrl + w + c`:关闭当前window - `ctrl + w + o`:关闭其他window,仅保留当前window #### 窗口相关命令行 - `:vsplit {filename}` - `:split {filename}` - `:new {filename}` ### tabs vim除了支持同时展示多个windows之外,也同样支持`tab`。在vim中,tab和浏览器/现代编辑器类似,tab为对window的布局(垂直/水平多个window的布局)。 在vim中,tab并不表示打开的文件,即使当tab被关闭,文件的buffer仍然会存在。 tab的常用命令如下 #### `:tabnew` `:tabnew {filename}`用于在新tab中打开文件,使用示例如下: ```bash :tabnew file1.txt ``` `:tabnew`并不实际依托于buffer存在,可以直接调用`:tabnew`先创建一个tab,然后在新tab中调用`:e`打开文件进行编辑。 ```bash :tabnew :e file2.txt ``` #### `:tabclose` `:tabclose`命令用于关闭当前tab #### `:tabnext` `:tabnext`命令用于切换到下个tab #### `:tabprevious` `:tabprevious`命令用于切换到前一个tab #### `:tablast` `:tablast`用于切换到最后一个tab #### `:tabfirst` `:tabfirst`用于切换到第一个tab #### `gt` 除了通过命令外,还可以通过快捷键`gt`切换到`下一个tab页面`。 #### `gT` 快捷键`gT`则是可以切换到`前一个tab页面`。 #### `{n}gt` `{n}gt`支持通过tab序号快速切换tab,例如`3gt`可以切换到第三个tab。 #### `vim -p` 在进入vim时,如果想打开多个tab,可以添加`-p`选项,示例如下 ```bash vim -p f1.txt f2.txt f3.txt ``` ### window和buffer的组合 vim支持同时打开多个window,并且,`每一个window都包含了一个buffer`。可以通过`ctrl + w + {dir}`快捷键的方式在window之间切换,但是,`对于当前的焦点window,仍可以通过 :buffer {filename} 的方式来切换当前焦点window对应的buffer`。 例如,当前存在三个windows,`w1, w2, w3`,其打开文件分别为`f1, f2, f3`,可以切换到`w2` window,并且调用`:b f3`命令,此时会将当前的`w2`的buffer切换到`f3`,此时,`w1, w2, w3`对应的打开文件则变成了`f1, f3, f3`。 > 通常,在图形操作系统中使用vim,其`terminal`(如`windows系的Windows Terminal`和`Linux系的KConsole`,其终端本身就支持`tab`功能,此时可以无需使用vim本身的tab功能。 > > 但是,在非图形化系统(如通过ssh访问服务器),此时在服务端未提供图形支持的前提下,可以使用vim tab来实现多tab打开。 ## Registers ### Register Types vim中拥有如下10中register类型: - `unnamed register`: `""` - `numbered register`: `"0-9` - `small delete register`: `"-` - `named register`: `"a-z` - `read only register`: `":`, `".`, `"%` - `alternate file register`: `"#` - `expression register`: `"=` - `selection register`: `"*`, `"+` - `black hole register`: `"_` - `last search pattern register`: `"/` ### register operators 在使用register之前,必须通过操作向register中存储值。可以通过如下操作向register中存值: - `y`: `yank(copy)` - `c`: `delete text and start isnert mode` - `d`: `delete text` 通常,在操作删除文本时,都会将文本保存在register中。 为了粘贴存储在register中的文本,可以使用如下方式: - `p`: 在cursor之后粘贴文本内容 - `P`: 在cursor之前粘贴文本内容 `p`和`P`都接收一个`count`和`register symbol`作为参数,例如, - `10p`: 将register中的文本粘贴10次 - `"ap`: 从`register a`中粘贴文本 - `10"ap`:从`register a`中获取文本并粘贴10次 ### Calling register from insert mode 当处于insert mode,并且需要从指定register中粘贴时,可以使用如下方式: - `Ctrl + R a`:其会将`register a`中的内容粘贴到cursor之后 ### unnamed register 在unnamed register中,存储着最后一次`delete`, `yank`, `change`的内容,可以通过`""p`。 如果做了多次yank/delete/change操作,那么新操作的text会自动覆盖旧操作的text。 默认情况下,`p/P`命令会默认关联unnamed register。 ### numbered register numbered register会自动按照升序对其自身进行填充,存在两种不同类型的numbered register, - `yanked register`: `0` - `numbered register 1-9`: `1-9` #### yanked register 如果通过`yy`进行了整行复制,那么vim实际会将文本保存在两个regiser中: - unnamed register:通过`p`粘贴 - yanked register:通过`"0p`粘贴 `当之后再次yank一个不同text时,vim会对yanked register和unnamed register都随之变化。` 除了yank之外的操作都不会被存储到register 0。`故而,即使yank操作后发生了delete/change操作,register 0中的内容也不会被修改`。 当处于insert mode时,可以通过`ctrl + R 0`来快速粘贴register 0中的内容。 #### non-zero numbered register 当change/delete长度超过1行的文本时,该文本将会被存储在non-zero numbered register中,按照`most-recent`的顺序存储在`1-9`寄存器中。 > 其中,`长度超过一行`可以指`文本未包含完整的某一行,但是出现跨行`的场景,示例如下 > > ``` > 111 > 222 > # 选中范围为第一行末尾的`11`和第二行开头的`2`,此时对选中内容 > 进行change/delete,选中文本仍然会包含在non-zero register中 > ``` > > 但是,如果选中内容位于同一行,并且不包含完整行,那么change/delete内容不会填充到non-zero numbered register中 例如, ``` 111 222 ``` 上述对第一行`111`内容进行删除后,`111`文本将会被包含在`register 1`中。 再次针对`222`调用`dd`删除后,non-zero numbered registers内容如下: - `register 1`: 包含内容为`222` - `register 2 `: 包含内容为`111` ##### `.` 当使用`"1p`粘贴完register 1中的内容时,直接多次调用`.`其会自动粘贴`"2`,`"3`,`"4`寄存器中的内容。 如果从`"5p`开始粘贴,调用`.`后,其会自动粘贴`"6p`。 对于`small deletion`(例如`dw`或`cw`),其并不会被存储到numbered register中,而是会被存储到small deletion register(`"-`)中。 ### small deletion register 对于小于一行的deletion或change,其并不会被存储在numbered registers中,而是会被存储在small deletion register(`"-`)中 例如, - `diw` - `ciw` ### named register named register是vim中最通用的register,其可以针对yank/change/delete的text进行存储,其范围为`a-z`。 named register和前面介绍的registers不同,named register并不会被自动存储,而是需要手动指定存储的named register。 例如,需要将文本存储到`register a`,需要执行如下命令`"ayiw`: - 其中`"a`告知vim将内容存储到`"a`寄存器中 - `yiw`用于复制word 如果需要获取register a中的内容,可以通过`"ap`命令。 #### append to named register 如果,想要对named register中的内容进行新增而不是完全替换,可以使用如下方式: - register a中已经存在内容“hello” - 想要添加“world”到register a中,可以调用`"Ayiw` 此时,register a中的内容将会变为`helloworld`,而不是被替换为`world` ### read-only register vim中含有3个read-only register: - `.`:存储了最后插入的文本(处于insert modez最后入的内容) - `:`:存储了上次执行的命令 - `%`:存储了当前文件名称(绝对路径) ### alternate file register alternate file代表最后打开的文件,`alternate file register`代表最后打开文件的名称。 ### expression register vim中拥有expression register,并可以通过`"=`来执行表达式。 例如,执行表达式`1 + 1`,可以进行如下操作`"=1+1p` - 其会执行`1+1`的运算 - `p`则是会拿到运算结果 在insert mode下,则是可以通过`Ctrl-r =1+1`来执行表达式。 expression register还支持获取其他寄存器的值,示例如下 `"=@ap`可以获取register a的值。 在insert mode下,`Ctrl-r =@a`也可以获取register a的值。 ### selection register 通过selection register,可以从外系统中粘贴文本。 vim中拥有两种selection register:`*`, `+`。当在vim之外的程序(例如chrome browser)中复制文本时,并不能直接通过`p`在vim中粘贴。但是,vim的`"*`和`"+`会和系统的剪切板相关联,可以通过`"+p`和`"*p`来粘贴外部程序中复制的文本。 同样的,如果通过`"+yiw`和`"*yiw`来复制内容,那么复制的内容可以在外部程序中进行粘贴。(需要启用`+clipboard`选项) 如果想要直接通过`p`来粘贴外部程序中复制的内容,可以通过在`.vimrc`中添加如下配置: ``` set clipboard=unnamed ``` ### black hole register 每次当对文本进行delete/change时,文本都会自动保存在vim的寄存器中。如果,此次操作不想保存任何内容到寄存器中,可以使用black hole register `"_`。 例如,想要删除行但是不保存到任何寄存器中,可以使用`"_dd`。 black hole register类似于`/dev/null` ### last search register 如果想要对最后搜索的内容(通过`/`或`?`进行搜索)进行粘贴,可以使用last search register(`"/`)。 如果想要对最后搜索的内容进行粘贴,可以使用`"/p`,insert mode模式下可以使用`Ctrl-r /`。