读书笔记:《Vim实用技巧》第二版

2020-01-18  本文已影响0人  Whyn

[TOC]

前言

记得在刚学 Vim 的时候,就把 《Vim实用技巧》(英文名:Practical Vim)通读了一遍(当时读的应该是第一版),可以说就是这本书让我真正进入了 Vim 的世界。

也因为在很早期就系统学习了 Vim 的相关技巧,因此使用 Vim 还是挺得心应手的。只是 Vim 的功能实在是繁多,日常使用也就只是用了 Vim 特性的十之一二,很多的东西不常用,慢慢地也就忘记了。

刚好看到 《Vim实用技巧》 第二版,就想着温故知新,重新再读一遍,把自己不常用但感觉好用的技巧给记录下来,方便以后查阅。

:据网上介绍,《Vim实用技巧》 第二版与第一版内容上区别不大,主要改动为:

摘抄

:以下只会记录本人不熟悉或觉得需要记录的内容,不会事无巨细记录完整内容。

" 只加载 code/essential.vim 配置文件
vim -u code/essential.vim
guu " 当前行转成小写
gUU " 当前行转成大写
g~~ " 当前行反转大小写
  1. normal mode:正常模式

  2. insert mode:插入模式

  3. command-line mode:命令行模式,即按下:键时,Vim 就会切换到命令行模式。
    ▪ 出于历史原因,在命令行模式中执行的命令又被称为 Ex 命令
    ▪ 在按/调出查找提示符或用<C-r>=访问表达式寄存器时,命令行模式也会被激活。
    ▪ 更多命令行模式提供的命令,请查看: :h ex-cmd-index

  4. visual mode:可视模式

  5. select mode:选择模式,与其他的文本编辑器类似的功能,在文本选中状态下,按键会直接替换选中区域。
    :在选中状态下,按<C-g>可切换select modevisual mode,下方提示栏可查看当前模式:

    visual-select-mode
  6. operator-pending mode:操作符待决模式,即在输入操作符(比如c/d/y,具体查看::h operator)后,进入只接受动作命令的状态。比如在执行动作命令dw时,当按下操作符d时,该模式立即激活,此时 Vim 会记录d这个按键并等待后续命令动作,当按下w时,该模式立即结束,并执行dw操作。

更多模式,请查看::h mode

:%d                " 删除所有行
:%s/target/replace " 对所有行执行替换操作
:echo expand('%')  " 显示当前文件名
" 注释所有行
:%normal I// 
" 指定宏命令
:normal @q
" 执行点命令
:%normal .
:reg . " 查看 . 寄存器
:reg / " 查看 /  寄存器
:reg 0 " 查看复制专用寄存器
:reg a " 查看寄存器 a
:read !ping www.baidu.com

ping www.baidu.com
:w !sh

:可以通过范围指定传递给{cmd}的缓冲区内容:

ping www.baidu.com

echo "only run this command"

" 高亮选择 echo 语句,然后输入以下命令,就只会执行 echo 这句的命令
:'<,'>w !sh
:bufdo echo expand('%')
:args *.*     " 将当前目录所有文件都加入到参数列表中,不包含子目录文件

:args **/*.*  " 将当前目录及其子目录的所有文件都加入到参数列表中

:args **/*.js " 将当前目录及其子目录的所有 .js 文件都加入到参数列表中

:args **/*.js **/*.css " 将当前目录及其子目录的所有 .js 和 .css 文件都加入到参数列表中

:使用args添加文件到参数列表时,会同时将该文件添加到缓冲区中。

" ** 表示当前文件夹及其子文件夹内所有文件
set path+=**,app/**,/usr/local/include

:find nameoffile.vim
" 或 tab键自动补全路径
:find nameof<TAB>
位置标记 跳转位置
'' 当前文件中上次跳转动作之前的位置
'. 上次修改的地方
'^ 上次插入的地方
'[ 上次修改或复制的起始位置
'] 上次修改或复制的结束位置
'< 上次高亮选区的起始位置
'> 上次高亮选取的结束位置
escape({string}, {chars})
   " {chars} 参数指定哪些字符串需要进行转义:
   " 正向查找:escape({string},'/\')
   " 反向查找:escape({string},'?\')

getcmdtype() " 该函数在正向查找时,返回'/',方向查找时,返回'?'

" 正向查找
:/\V<C-r>=escape("https://www.baidu.com?key=whyn",getcmdtype().'\')
" 反向查找
:?\V<C-r>=escape("https://www.baidu.com?key=whyn",getcmdtype().'\')
xnoremap * :<C-u>call <SID>VSetSearch('/')<CR>/<C-R>=@/<CR><CR>
xnoremap # :<C-u>call <SID>VSetSearch('?')<CR>?<C-R>=@/<CR><CR>
function! s:VSetSearch(cmdtype)
    let temp = @s
    norm! gv"sy
    let @/ = '\V' . substitute(escape(@s, a:cmdtype.'\'), '\n', '\\n', 'g')
    let @s = temp
endfunction
:[range]s[ubstitue]/{pattern}/{string}/[flags]

以下介绍substitute常用的标志位:flags
g:修改一行内所有匹配。
c:交互式确认。
n:不进行替换,只是显示匹配个数。
e:匹配不到时,不显示错误信息。
&:重用上一次substitute的所使用的标志位。
更多详细信息,请查看::h s_flags/

以下介绍替换域一部分常用特殊符号:

符号 描述
\r 插入一个换行符
\t 插入一个制表符
\\ 插入一个反斜杠
\1 插入第一个子匹配
\2 插入第二个子匹配(以此类推,最多到\9
\0 插入匹配模式pattern的所有内容
& 插入匹配模式pattern的所有内容
~ 使用上一次substitute{string}
\={Vim script} 执行{Vim script}表达式,并将其结果作为替换{string}

▪ 如果substitute命令使用了寄存器内容,而该寄存器内容存在特殊字符(如&~)时,则需要手动进行转义,当然还有更简单的方法,就是直接引用寄存器内容即可:

:%s//\=@{register}/g

" 比如,替换域直接引用复制专用寄存器
:%s//\=@0/g

*:当执行替换:s/target/replacement后,后续使用&可重复执行上一次的substitue命令,normal模式按下&Ex模式输入:&均可起作用。
:正常模式下使用g&会在全局执行上一次的substitute命令,相当于执行了::%s//~/&

:vimgrep /\Vleader/ **/*.vim " 递归查找当前文件夹所有拥有字符串 leader 的 .vim 文件,存储到 quickfix 列表中
:set hidden                  " 对 quickfix 列表进行命令执行前,需要设置该选项
:cfdo %s//Leader/gc          " 使用 cfdo 命令对 quickfix 列表各条目文件进行替换命令操作
:cfdo update                 " update 用于保存文件(当文件有改动时,才进行保存)

quickfix列表中的每个条目文本执行命令::cfdo {cmd}
更多信息,请查看::h quickfix

:[range] g[lobal][!] /{pattern}/[cmd]

global命令缺省作用范围为整个文件(%),其他大多数命令(如::delete/:substitute/:normal)的缺省作用范围为当前行。
[cmd]命令可以是除:global命令之外的任何 Ex 命令。缺省则默认为:print
global命令的执行机制global命令在指定的[range]内的文本上执行时通常分为两轮操作:
第一轮:Vim 标记匹配[pattern]的所有行
第二轮:在所有标记的文本行上执行[cmd]

举个栗子:将含有TODO注释的行复制到寄存器中

:qaq " 清空寄存器 a
:g/\VTODO\yank A " 将匹配内容追加到寄存器 a 中。注:不能使用小写,否则最后一条会覆盖前面内容
:reg a " 此时查看下寄存器 a 的内容,应该可以看到操作成功的内容

可以结合t命令将匹配文本行复制到文本结尾

:g/\VTODO/t$ 

global命令后的[cmd]也可以带一个范围,此时[cmd]命令的范围是基于global范围之上的,其格式为:

:[range1] g[lobal][!] /{pattern}/[range2][cmd] " range2 基于 range1,即 cmd 在匹配行内在进行范围选择

举个栗子:对以下 css 属性按字母进行排序,即排序{}内的所有内容:

html {
  border: 0;
  font-size: 100%;
  margin: 0;
  font: inherit;
  vertical-align: baseline;
  padding: 0;
}

body {
  background: white;
  color: black;
  line-height: 1.5;
}

执行使用以下命令即可完成:

:g/\V{/ .+1,/\V}/-1 sort

上述代码中:
/\V{/:表示global命令的匹配
.+1,/\V}/-1:表示cmd的范围,此处范围基于global的匹配范围
上述代码其真实形式如下:

:g/\V{/ (.+1,/\V}/-1 sort) " 括号()范围内即为 cmd
grepprg="grep -n $* /dev/null"  
grepformat="%f:%l:%m,%f:%l%m,%f %l%m

其中:
grepprg中,$* 表示占位符,最终会被:grep命令的参数代替。
grepformat中,各占位符的意思如下:

占位符 描述
%f 文件名
%l 行号
%c 列号
%m 匹配行的文本

grepformat字符串可以使用,分割指定多组解析格式,比如 Vim 缺省的grepformat

grepformat="%f:%l:%m,%f:%l%m,%f %l%m

即表示对格式为%f:%l:%m%f:%l%m%f %l%m的行可以进行解析,比如,外部grep的输出如下所示:

department-store.txt:1:Waldo is beside the boot counter.
goldrush.txt:6:Waldo is studying his clipboard.
goldrush.txt:9:The penny farthing is 10 paces ahead of Waldo.

可以看到是符合%f:%l:%m的解析格式的,因此 Vim 可以对外部命令grep进行解析并输出到quickfix窗口中。

因此,自定义一个外部grep命令传递给 Vim 简直不要太容易了。

举个栗子:比如我们使用rgrep(号称当前最快的文本搜索神器)进行文本搜索,外部命令如下:

rg --line-number --column --no-heading --smart-case {word}

上述命令输出如下:

basic\settings.vim:2:5:let {word}=' '

根据命令与输出,我们就可以对 Vim 内置的:grep命令进行配置了:

set grepprg=rg\ --line-number\ --column\ --no-heading\ --smart-case\ $*
set grepformat=%f:%l:%c:%m

配置完成后,在 Vim 中输入::grep! {word},然后使用命令:copen打开quickfix窗口,就可以看到搜索结果了。

:args *.txt " 把当前目录所有 .txt 文件加到参数列表中(不递归子目录)
:vimgrep /\Vgoing/g ## " 搜索参数列表中所有出现 going 内容的文件

其他

以下是对 Vim 的一些补充内容。

:verbose map <leader>q " <leader>q按键绑定信息

zz:让光标所在的行居屏幕中央
zt:让光标所在的行居屏幕最上一行
zb:让光标所在的行居屏幕最下一行

更多g命令,请参考:help g

上一篇下一篇

猜你喜欢

热点阅读