美文网首页
读书笔记:《Vim实用技巧》第二版

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

作者: Whyn | 来源:发表于2020-01-18 23:06 被阅读0次

    [TOC]

    前言

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

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

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

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

    • 技巧36:批处理运行Ex命令
    • 技巧84:对完整的查找匹配进行操作
    • 技巧86:统计当前模式的匹配个数
    • 技巧97:在多个文件中执行查找与替换
    • 技巧111:使用Vim内置正则表达式引擎的Grep
    • 技巧117:自动补全单词序列

    摘抄

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

    • Vim 在线文档:vimhelp.org
      Vim 在线中文文档 1:vimdoc
      Vim 在线中文文档 2:vimdoc_github

    • 查看按键符号:h key-notation

      key-notation
    • 使用 Vim 的出厂配置:vim -u NONE -N
      其中:
      -u NONE:该标志让 Vim 在启动时不加载.vimrc文件
      -N:该标志使能nocompatible选项,防止进入 vi 兼容模式
      -u标识可在 Vim 启动时加载指定配置文件,如:

    " 只加载 code/essential.vim 配置文件
    vim -u code/essential.vim
    
    • 查看 Vim 版本::version

    • S:删除整行并进入插入模式,相当于^C

    • 当使用f{char}/t{char}时,;正向寻找下一个,,反向回到上一个。

    • i{insert some text}<Esc>称为一次修改,也即进入insert模式到退出回到normal模式,则完成一次修改。因此通过合适的控制一次修改的粒度,就可以控制撤销u的粒度。

    • vu:转成小写
      vU:转成大写
      ~:反转大小写
      gxx:转换当前行。比如:

    guu " 当前行转成小写
    gUU " 当前行转成大写
    g~~ " 当前行反转大小写
    
    • Vim 模式:
    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

    • Viminsert mode时插入寄存器内的文本时(比如,<C-r>{register}),其插入方式相当于键盘一个一个字符输入插入的文本,因此,如果激活了textwidth或者autoindent选项的话,则可能会出现不必要的换行或额外的缩进。
      如果想按原义进行插入,可使用<C-r><C-p>{register} 或者直接回到normal mode进行粘贴。

    • gv高亮上一次选中区域

    • 对于列块可视模式(<C-v>/<C-q>),I/A命令会分别置于当前光标之前/之后,而不是跳转到开头/结尾。
      在可视模式以及操作符待决模式中,i/a命令会被当做一个文本对象的组成成分,而不是直接插入。

    • command-line mode中,%代表所有行/当前文件名(参见::h cmdline-special)。因此:

    :%d                " 删除所有行
    :%s/target/replace " 对所有行执行替换操作
    :echo expand('%')  " 显示当前文件名
    
    • :/<html>/,/<\/html>/p:打印文中<html></html>标签的所有内容。

    • :/<html>/+1,/<\/html>/-1p:打印文中<html></html>标签内的所有内容。

    • 命令行模式使用normal命令结合.命令更加强大

    " 注释所有行
    :%normal I// 
    " 指定宏命令
    :normal @q
    " 执行点命令
    :%normal .
    
    • .:重复上次的普通模式命令
      @::重复上次的 Ex 命令
      执行一次 Ex 命令后,后续相同命令可使用@@命令进行重复
      ▪ 寄存器:总是保存着最后一次执行的命令行命令(参见::h quote
      ./:都是寄存器,可直接使用:register查看所有寄存器内容
    :reg . " 查看 . 寄存器
    :reg / " 查看 /  寄存器
    :reg 0 " 查看复制专用寄存器
    :reg a " 查看寄存器 a
    
    • bn[next]:打开缓冲区列表下一个
      bp[revious]:打开缓冲区列表上一个

    • 命令行模式下,<C-d>命令可以显示所有可用的补全列表::col<C-d>

    • 把当前单词插入到命令行<C-r><C-w>
      把当前字符串插入到命令行<C-r><C-a>

    • 命令行窗口:命令行历史记录,可进行选择与修改。

    • q::打开命令行历史窗口
      q/:打开搜索历史窗口

    • 如果在 bash shell 中运行 Vim 时,可通过Ctrl-z挂起 Vim 进程回到 bash;
      在 bash 中,可以用fg命令回到后台 Vim 进程。

    • 把 bash 命令的结果读入到 Vim 缓冲区::read !{cmd}
      Vim 缓冲区内容作为{cmd}命令的标准输入::write !{cmd}

    :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可以对:ls列出的所有缓冲区上执行 Ex 命令:
    :bufdo echo expand('%')
    
    • 通配符*用于匹配 0 个或多个字符,仅限当前指定目录,不递归子目录
      通配符**用于匹配 0 个或多个字符,仅会递归子目录
    :args *.*     " 将当前目录所有文件都加入到参数列表中,不包含子目录文件
    
    :args **/*.*  " 将当前目录及其子目录的所有文件都加入到参数列表中
    
    :args **/*.js " 将当前目录及其子目录的所有 .js 文件都加入到参数列表中
    
    :args **/*.js **/*.css " 将当前目录及其子目录的所有 .js 和 .css 文件都加入到参数列表中
    

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

    • 参数列表比缓冲区列表更容易管理,这使其成为对缓冲区进行分组的理想方式。
      args:打印数列表。
      args {arglist}:设置参数列表。
      arga[dd] {names}:添加文件参数列表。
      argd[elete] {pattern}:删除参数列表文件。
      %argd[elete]:删除参数列表所有文件。
      :next:跳转到下一个参数列表文件。
      :prev:跳转到上一个参数列表文件。
      :argdo:对参数列表中的每个缓冲区都执行同一条 Ex 命令。
      :参数列表是缓冲区的强烈补充。
      更多详细信息,请查看::h args

    • :lcd {path} 命令让我们可以设置当前窗口的本地工作目录。

    • :e %<TAB>会展开%代表的含义,即文件名
      同理::e %:p<TAB>/:e %:h<TAB>···

    • find命令允许通过文件名查找打开文件,查找目录由path决定。因此可以通过设置path选项,结合find命令可快速打开某个文件:

    " ** 表示当前文件夹及其子文件夹内所有文件
    set path+=**,app/**,/usr/local/include
    
    :find nameoffile.vim
    " 或 tab键自动补全路径
    :find nameof<TAB>
    
    • e:正向移动到当前单词/下一单词的结尾
      ge反向移动到上一单词的结尾

    • word:单词(跨单词移动使用:w/b/e/ge
      WORD:字串(跨字串移动使用:W/B/E/gE

    • b:表示圆括号()
      B表示花括号{}

    • Vim 的自动位置标记

    位置标记 跳转位置
    '' 当前文件中上次跳转动作之前的位置
    '. 上次修改的地方
    '^ 上次插入的地方
    '[ 上次修改或复制的起始位置
    '] 上次修改或复制的结束位置
    '< 上次高亮选区的起始位置
    '> 上次高亮选取的结束位置
    • gi:回到上次插入模式离开的位置,并再次进入插入模式。此命令使用'^标记恢复光标位置,并切换进入插入模式。
      gf:跳转到光标所在的文件。如果文件缺少后缀名,可通过set suffixesadd+=.js进行指定。
    • Vim 不区分<C-i><Tab>键,因此当<Tab>被重新映射时,<C-i>也会被重映射,不会再有跳转功能。

    • 无名寄存器""Vim 默认使用的寄存器。
      黑洞寄存器_:即直接删除:_d{motion}
      详情参考::h quote_quote/:h quote_

    • 串行执行宏:10@{reg}
      并行执行宏:1,$ normal @{reg},对全部行分别同时执行宏。
      :这里的 并行 指定不是同时执行,而是对每个行单独执行,某一行操作失败也不会导致其余行的宏停止。

    • \V原义查找:
      1)正向查找时,需转义字符/
      2)方向查找时,需转义字符?
      3)任何方向查找,都需转义反斜杠字符\
      :可用编程方式转义字符:

    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
    
    • substitute替换命令:其格式如下:
    :[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//~/&

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

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

    • global命令:该模式结合了 Ex 命令和 Vim 的模式匹配这两方面的能力,即该命令可以让我们在符合模式匹配的那些行上执行 Ex 命令。其格式如下:
    :[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
    
    • 定制grep程序Vim:grep命令可以调用外部grep命令,并对外部grep命令结果进行解析,然后输出到quickfix窗口进行查看。
      Vim 通过配置grepprggrepformat两个选项对外部grep命令进行包装:
      grepprg:指定调用的 shell 程序(参见::h 'grepprg'
      grepformat:配置 Vim 解析外部grep命令输出结果的格式(参见::h 'grepformat')。
      Vim 在 Unix 系统中的缺省设置如下:
    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窗口,就可以看到搜索结果了。

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

    其他

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

    • Vim 提供了<Leader>键,用于作为用户自定义命令的命名空间。

    • 书签:m{char}
      跳转到书签(光标到书签所在行首):'{char}
      跳转到书签(具体到定义书签时的光标位置)`{char}

    • 搜索后光标停留在匹配单词的第一个字符上(默认):/word/s
      搜索后光标停留在匹配单词的最后一个字符上:/word/e
      搜索后光标停留在匹配单词的最后一个字符的下一个字符上:/word/e+1
      搜索完成后,可通过命令行输入://e//s改变匹配单词字符位置。

    • %s/{pattern}//gn:统计pattern出现的次数。
      :vimgrep /{pattern}/g %:统计pattern出现的位置。需要使用:copen打开quickfix窗口进行查看。

    • gn代表当前位置的高亮区域。比如搜索时,当前光标所在的匹配单词即可使用gn进行操作:dgn,删除当前高亮单词。

    • /Practical \zsVim:查找Practical Vim,但只高亮Vim
      /Practical \zsVi\zem:查找Practical Vim,但只高亮Vi

    • 查看加载的插件:scriptnames

    • 更改到当前文件所在的目录::lcd %:p:h
      lcd:改变当前窗口的工作路径
      %:代表当前文件的文件名
      :p:当前文件名全路径
      :h:取出当前文件所在的目录

    • 查看按键绑定信息::verbose map {keys}

    :verbose map <leader>q " <leader>q按键绑定信息
    
    • H:移动光标到屏幕的首行
      M:移动光标到屏幕的中间一行
      L:移动光标到屏幕的尾行

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

    • 删除空行::g/^\s*$/d

    • 删除行尾空白::%s/ *$//

    • 删除DOS方式的回车^M:%s/\r//g

    • 移动光标到上一次的修改行'.
      移动光标到上一次的修改点`.
      列出你跳转的足迹::ju(mps)
      依次沿着你的跳转记录向回跳<C-o>
      依次沿着你的跳转记录向前跳<C-i>

    • 缓冲区转成标签页:tab sb[uffer] {num}

    • 为每个窗口都设置选项:windo setlocal scrollbind

    • 好像有很多g命令啊:
      gv高亮上一次选中区域
      ge:反向移动到上一单词的结尾
      gi:回到上次插入模式离开的位置,并再次进入插入模式。此命令使用'^标记恢复光标位置,并切换进入插入模式。
      gI:跳转到开头并进行插入。
      gI效果与I类似,但是I是插入到第一个单词前面,而gI是插入到行起始位置(无论行起始位是否有单词)。
      gf跳转到光标所在的文件。如果文件缺少后缀名,可通过set suffixesadd+=.js进行指定。
      gn代表当前位置的高亮区域。比如搜索时,当前光标所在的匹配单词即可使用gn进行操作:dgn,删除当前高亮单词。
      g$:移动到当前行末尾位置。
      :当set nowrap设置时,g$效果相当于$,此时 屏幕行(screenline) 等于 文本行(text line)
      set wrap时,g$只会移动到当前 屏幕行 的末尾,而$会移动到 文本行末尾
      g0:效果与g$一致,不过是移动到当前行最前面。
      g_:跳转到行末尾单词位置(文本行)。
      g_命令与$类似,但是$会跳转到行末尾(无论行末尾是否有单词)。
      g^:跳转到行起始单词位置。
      g^命令与0类似,但是0会跳转到行起始位置(无论行起始是否有单词)。
      gm:移动到屏幕行中间位置。
      gM:移动到文本行中间位置。
      g;依次跳转到上次更改的位置(新到旧跳转)
      g,依次跳转到上次更改的位置(旧到新跳转)
      g<:回显上次命令行的输出内容。
      gJ拼接上下行,不插入空格分隔符
      J拼接上下行会插入一个空格分隔符。

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

    相关文章

      网友评论

          本文标题:读书笔记:《Vim实用技巧》第二版

          本文链接:https://www.haomeiwen.com/subject/dpxmactx.html