Vim使用笔记

Posted by wsxq2 on 2018-11-25
TAGS:  Vim

本文最后一次编辑时间:2021-08-22 23:05:39 +0800

TODO: h tips.txt <2019-01-14, wsxq2>

vim/vim: The official Vim repository

概述

主要配置文件为~/.vimrc(也就是每次启动vim都会执行的vim脚本,也是在命令行模式下输入的命令的集合),我的配置文件可在 GitHub: wsxq2/MyProfile 处获得

主要配置目录为~/.vim(里面有安装的插件,还有自己写的 UltiSnips 代码片段):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/root/.vim
├── autoload/
│   └── plug.vim
├── bundle/
│   ├── dbext.vim/
│   ├── delimitMate/
│   ├── emmet-vim/
│   ├── indentLine/
│   ├── ultisnips/
│   ├── vim-autoformat/
│   ├── vim-markdown-toc/
│   ├── vim-plug/
│   ├── vim-snippets/
│   ├── vim-table-mode/
│   └── YouCompleteMe/
├── UltiSnips/
│   ├── all.snippets
│   ├── javascript.snippets
│   ├── markdown.snippets
│   └── python.snippets
├── .netrwhist
└── .ycm_extra_conf_offical.py*

14 directories, 7 files

我的 Vim 中主要安装了下列插件:

Vim五种常用模式:普通模式、插入模式、命令行模式、选择模式(Visual Mode)、搜索模式。下面简述各模式的进入方法:

  • 普通模式:在任意模式中按EscCtrl+[进入(可能需要多次)普通模式
  • 插入模式:在普通模式中使用插入相关的命令(如i)进入插入模式
  • 命令行模式:在普通模式中按:进入命令行模式
  • 选择模式:在普通模式中按v进入选择模式
  • 搜索模式:在普通模式中按/?进入搜索模式

普通模式

Bash 的 vi 模式

默认情况下处于插入模式,按EscCtrl+[进入普通模式,普通模式下的常用命令如下:

  • i a I A: 均用于进入插入模式。i(insert)在当前字符前插入,a(append)在当前字符后插入,I在当前行的第一个字符前插入,A在当前行的最后一个字符后插入

  • j k: 回溯历史的命令。如同方向键向上键和向下键。注意,这和VIM中的行为不同,VIM中j用于移动到下一行,k用于移动到上一行

  • h l:移动命令。h向左移动一个字符;l向右移动一个字符
  • 0 ^ $: 移动命令。0移动到行首;^0类似,只是移动到行首的第一个非空白字符(如空格);$移动到行尾
  • w e b W E B: 移动命令。w右移一个单词;ew类似,只是停在末尾处;b左移一个单词;W移动到下一个空格后的第一个字符;EW类似,只是停在末尾处;B移动到上一个空格后的第一个字符
  • f F t T , ;: 移动命令。f向后移动至某个字符,如fa向后移动至a字符;F向前移动至某个字符;t向后移动至某个字符的前一个字符;T向前移动至某个字符的后一个字符;,重复上一个和前述命令相同的移动命令;;,类似,不过方向相反
  • , ;:

  • c(change): 改变文本并进入插入模式。该命令就配合移动命令使用,如cw改变一个单词,cb反向改变一个单词,cl改变一个字符等等
  • cc: 改变当前行的所有内容,相当于0c$
  • C: 改变当前光标所在位置至行尾的内容,相当于c$
  • s(substitute): cl的简写,用于修改一个字符
  • r(replace): 和s类似,只是不会进入插入模式

  • d(delete): 删除文本。该命令同样配合移动命令使用
  • dd: 删除当前行的所有内容,相当于0d$
  • D: 删除当前光标所在位置至行尾的内容,相当于d$
  • x: dl的简写,用于删除一个字符

  • p P: 粘贴命令。p(paste)在当前字符后粘贴,P在当前字符前粘贴。注意,该命令亦可粘贴已经删除的内容

  • y(yank): 复制命令。和移动命令配合使用,如yl复制一个字符,yw复制一个单词
  • yy: 复制整行内容。相当于0y$

  • v: 进入vim中编辑(编辑多行命令或长命令时非常有用)。注意,这和VIM中的行为不同,VIM中v用于选择,以进入Visual模式

其它命令

  • * #: 搜索命令。*搜索当前单词并跳转到下一处;#搜索当前单词并跳转到上一处
  • K \K: 查询帮助命令。K查询当前单词的man手册(也可以先选择后再查询,即vWK);\KK一样,因为我设置的缘故,详见vim内置帮助文档:h :Man
  • gg G: 跳转命令。gg跳至整个文件的第一行;G跳到整个文件的最后一行
  • 50% %: 跳转命令。50%跳到文件的50%处(即一半的地方,同理,25%则跳到 1/4 处);%(即前面不加数字)则完全不同,它用于跳转到当前括号的匹配括号处(如当前是(,则跳到)
  • <Space>: 折叠命令。本来和l的作用相同(即向右移动一个字符),但我将其绑定到了za,即自动折叠或反折叠
  • gd gD gf: 跳转命令。gd跳到当前光标所指局部变量的定义位置;gD跳到当前光标所指全局变量的定义位置;gf跳到当前光标所指路径的文件
  • qa<一堆操作>q @a: 宏录制命令。宏是操作的集合,
  • @::
  • Ctrl+6:
  • Ctrl+h Ctrl+j Ctrl+k Ctrl+l:
  • ma 'a:
  • .
  • u Ctrl+r:
  • =:
  • &:
  • !:
  • >> <<:
  • Ctrl+c:
  • Ctrl+g 1<C-g> 2<C-g>

插入模式

  • Ctrl+P:
  • Ctrl+N:
  • Ctrl+X Ctrl+L:
  • Ctrl+X Ctrl+F:
  • Ctrl+O u:
  • <C-r>":
  • ``

Bash 的 vi 模式

  • Ctrl+W: 向前清除一个单词
  • Ctrl+U: 向前清除到行首
  • Ctrl+P: 向上查找命令,注意,因为我在bash默认的启动文件~/.bashrc(MacOS是~/.bash_profile)进行了如下设置
    1
    2
    3
    4
    
    bind '\C-p: history-search-backward'
    bind '\C-n: history-search-forward'
    bind '"\e[A":history-search-backward'
    bind '"\e[B":history-search-backward'
    

    所以Ctrl+P将会向上查找与当前行带有相同前缀的命令,如:

    1
    2
    3
    
    vim ~/.vimrc
    vim<Ctrl+P> #将会得到:
    vim ~/.vimrc
    

    这个功能使得输入曾经输入过的命令变得异常方便,再也不需要傻乎乎地先用history查看一下命令历史,再傻乎乎地用!<history结果前的数字>来执行输入过的命令了

  • Ctrl+N: 和Ctrl+P类似,只是向下查找

命令行模式

  • Ctrl+D: 显示所有可能的结果
  • Tab: 按可能的结果依次补全
  • h<CR>: 输入h<CR>(其中<CR>是回车)可查看帮助。如下述例子:
    • h gd: 输入h gd可查看普通模式下gd命令的帮助
    • h :h: 输入h :h可查看命令模式下h命令的帮助
    • h i_CTRL_N: 输入h i_CTRL_N可查看插入模式下Ctrl+N的帮助
    • h 'nu: 输入h 'nu可查看选项nu的帮助(选项通过命令模式下的set命令来设置,如set nu

选择模式

  • //:
  • K:

搜索模式

使用正则表达式,注意和标准的(Perl)的有点不同:

  • /abc:
  • /\<abc\>
  • /^abc:

nvim

时代在发展,vim也在进步。nvim 是 vim 的一个重构,现已开始普遍应用。它有许多优点,更多信息参见 neovim/neovim: Vim-fork focused on extensibility and usability (其 star 数量竟然是官方 vim 的 2 倍???)

LSP

发行版

vim 有很多发行版,适用于新手:

遇到过的问题

详情参见 VIM 文件编码识别与乱码处理

在 Vim 中,有四个与编码有关的选项,它们是:fileencodings、fileencoding、encoding 和 termencoding。在实际使用中,任何一个选项出现错误,都会导致出现乱码:

  • encoding: VIM 内部字符编码

    建议设置:

    1
    
    set encoding=utf-8 "这通常是 VIM 的默认值
    
  • termencoding: VIM 用于屏幕显示的编码

    建议设置:

    1
    2
    
    set termencoding=utf-8 "for Linux
    set termencoding=gbk/cp936 "for Windows
    
  • fileencoding: VIM 探测到的文件的编码

    建议设置:

    1
    
    set fileencoding? "通常不建议设置该选项,除非你确定你知道文件本身正确的编码
    
  • fileencodings: VIM 自动识别编码的顺序

    建议设置:

    1
    
    set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1
    

以上的fileencodings选项的建议设置推荐放到~/.vimrc,避免 VIM 识别不到 GBK(CP936) 编码(这是使用 VIM 最容易遇到的编码错误的原因)。如果 VIM 确实没有识别正确,你也知道文件本身正确的编码,那么你可以使用如下命令以正确的编码重新打开本文件:

1
:e ++enc=gbk

详情参见:h :e

encoding为例,简要说下 VIM 中选项的查看值、设置值的方法和获取 VIM 自带帮助的方法:

1
2
3
:set encoding? "查看 VIM 内部字符编码
:set encoding=utf8 "设置 VIM 内部字符编码
:help 'encoding "查看 VIM encoding 选项的相关帮助

另外还可使用如下命令查看文件的编码:

1
2
$ file quicker.em
quicker.em: ISO-8859 text, with CRLF line terminators

不小心按了Ctrl+S后卡死?

使用Ctrl+QCtrl+C即可

详情参见 keyboard - How to unfreeze after accidentally pressing Ctrl-S in a terminal? - Unix & Linux Stack Exchange

在 Vim 脚本中忽略’Pattern not found’错误信息

  1. 使用:silent[!]前缀::h :silent
  2. :s替换命令后添加e选项::h :s_flags

插入模式中从右往左书写

:set ri

详情参见::h 'ri

对齐文本

  • :ri: 右对齐
  • :ce: 居中对齐
  • :le: 左对齐

详情参见::h :ri

删除二进制文件末尾的EOF

1
:set binary noendofline

详情参见: Vim show and be able to delete 0x0a at end of file - Stack Overflow

实践记录

Markdown文件

  1. 添加_post的头
    1
    
    :.!head -n 6 $bl/2018-08-08-template.md
    

    或者使用 UltiSnips:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    snippet bl "blog" b
    ---
    layout: post
    tags: [$1]
    categories: blog
    ---
       
    <!-- vim-markdown-toc GFM -->
       
    <!-- vim-markdown-toc -->
       
    ## $0
       
    ## 链接
    <!-- link start -->
       
    <!-- link end -->
       
    <!-- abbreviations start -->
       
    <!-- abbreviations end -->
    endsnippet
    

    然后在 .md 文件中(我将 UltiSnips 的快捷键设置为了<C-J>):

    1
    
    bl<C-J>
    
  2. 添加*

    使用强大的替换功能:

    1
    
    :%s/^\( *\)\([\u4e00-\u9fa5]\)/\1* \2/ 
    

    或者使用列编辑:

    1
    
    <C-V>jjjI* <C-[>
    
  3. 为某个单词添加`
    1
    2
    
    qe
    i`ea`
    

    类似地,为某个单词添加(空格):

    1
    2
    
    qs
    i ea 
    

    为选定行的中文中的所有英文单词添加空格:

    1
    2
    3
    
    :s/[\u4e00-\u9fa5]\zs[a-zA-Z0-9]\+\ze[\u4e00-\u9fa5]/ & /ge
    :s/\([\uff0c\u3002\uff08\uff09]\|^\)\zs[a-zA-Z0-9]\+\ze[\u4e00-\u9fa5]/& /ge
    :s/[\u4e00-\u9fa5]\zs[a-zA-Z0-9]\+\ze\([\uff0c\u3002\uff08\uff09]\|$\)/ &/ge
    
  4. 将含有链接的行复制到末尾,将含有链接的行替换为链接本身
    1. 1
      2
      3
      4
      5
      6
      
      ql "(Get all Links)
      :/^<!-- link start -->/+1,/^<!-- link end -->/-1 g!/^$/d
      :/^<!-- vim-markdown-toc -->/,/^<!-- link start -->/ g/[^!]\[.\+\]([^#].\+)/copy /<!-- link start -->/
      :/^<!-- link start -->/,/^<!-- link end -->/ s/.*\(\[.\+\](.\+)\).*/* \1/e
      :/^<!-- vim-markdown-toc -->/,/^<!-- link start -->/ g/\[[^]]\+\]\[[^]]\+\]/copy /<!-- link start -->/
      :/^<!-- link start -->/,/^<!-- link end -->/ s/^.*\(\[[^]]\+\]\[[^]]\+\]\).*$/* \1/e
      

      2.(推荐)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      ql
      :/^<!-- link start -->/+1,/^<!-- link end -->/-1 g!/^$/d
      :let @t=''
      :1,/^<!-- vim-markdown-toc GFM -->/ s/[^!]\(\[.\{-1,}\](http[^[\]]\{-1,})\)/\=setreg('T','* '.submatch(1),'l')/egn
      :/^<!-- vim-markdown-toc -->/,/^<!-- link start -->/ s/[^!]\(\[.\{-1,}\](http[^[\]]\{-1,})\)/\=setreg('T','* '.submatch(1),'l')/egn
      :1,/^<!-- vim-markdown-toc GFM -->/ s/[^!]\?\(\[[^]]\+\]\[[^]]\{-1,}\]\)/\=setreg('T','* '.submatch(1),'l')/egn
      :/^<!-- vim-markdown-toc -->/,/^<!-- link start -->/ s/[^!]\?\(\[[^]]\+\]\[[^]]\{-1,}\]\)/\=setreg('T','* '.submatch(1),'l')/egn
      /<!-- link start -->
      "tp
      :/^<!-- link start -->/+1,/^<!-- link end -->/-1 sort u
      
  5. 给一行首尾添加`

    录制宏:

    1
    2
    
    qq
    I`<kEnd>`<Esc>
    
  6. 缩进所有不是由 1 或 # 开头的行:
    1
    
    :.,93 s/^\([^1#]\)/   \1/
    
  7. Plug 'junegunn/vim-plug'变为* [vim-plug](https://github.com/junegunn/vim-plug)::
    1
    
    :'<,'>s#^Plug '\([^/]\+\/\([^']\+\)\)'.*$#* [\2](https://github.com/\1):#
    
  8. 2018-11-25-Vim安装管理插件的插件——Vim-Plug.md变为[Vim安装管理插件的插件——Vim-Plug](https://wsxq2.55555.io/blog/2018/11/25/Vim安装管理插件的插件——Vim-Plug):
    1
    2
    
    qm "(My Blog)
    :.s/\(\d\+\)-\(\d\+\)-\(\d\+\)-\([^.]\+\)\.md/[\4](https:\/\/wsxq2.55555.io\/blog\/\1\/\2\/\3\/\4)/
    

    录制为宏m

  9. 插入图片:
    1. 使用Ultisnips覆盖vim-snippets默认的snippet
      1
      2
      3
      4
      
      snippet img "Image" b
      <!--picture $1-->
      $0
      endsnippet
      
    2. 然后录制宏将其转换为真正的链接
      1
      2
      
      qp
      :%s/<!--picture \([^ ]\+\) -->/![\1](http:\/\/wsxq12.55555.io\/\1)/g
      
    3. 控制图片大小和位置
      1
      2
      
      qo
      :s/^!\[\([^]]\+\)\](\([^)]\+\))$/<img src="\2" alt="\1" style="width:750px;display:block;margin:auto">/
      
  10. 收集所有的英文缩写(连续的大写字母)

    1.

    1
    2
    
    qa
    :/<!-- abbreviations start -->/+1,/<!-- abbreviations end -->/-1 ! sed -nr -e 's/^.*\b([A-Z]{2,})\b.*$/*[\1]: /p' 2018-12-02-16位汇编程序设计.md | sort | uniq
    

    2.

    1
    2
    3
    4
    5
    6
    7
    8
    
      qa
    :/^<!-- abbreviations end -->/+1,$ g!/^$/d
    :let @t=''
    :/^<!-- vim-markdown-toc -->/,/^<!-- link start -->/ s/\C[^%]\([A-Z]\{2,}\)\>/\=setreg('T',submatch(1),'l')/egn
    /^<!-- abbreviations end -->
    "tp
    :/^<!-- abbreviations end -->/+1,$ sort u
    :/^<!-- abbreviations end -->/+1,$ !$bl/handle-abbreviations.sh
    

    校正后:

    1
    2
    3
    4
    
      qb
    :/^<!-- abbreviations start -->/+1,/^<!-- abbreviations end -->/-1 d
    :/^<!-- abbreviations end -->/+1,$ co /^<!-- abbreviations start -->/
    :/^<!-- abbreviations start -->/+1,/^<!-- abbreviations end -->/-1 s/\*\[\([^]]\+\)]:\(.\+\)$/* **\1**:\2/
    
  11. 在每个标题前面添加新行:
    1
    
    %s/^\(##\+\)/\r\1/
    
  12. 1.3.2 ARP报文数据前面添加####
    1
    
    %s/^\(\d\.\d\.\d \)/#### \1/
    
  13. 给引用的段落加上两个中文空格以缩进:
    1
    2
    
    qr
    :%s/^> \([a-zA-Z\u4e00-\u9fa5]\)/> \&emsp;\&emsp;\1/
    
  14. 将如下形式的内容:
    1
    
    【转】关于Bootmgfw.efi、Bootx64.efi的详解 - 天南海北 - 萝卜头IT论坛 - Powered by Discuz!: https://bbs.luobotou.org/forum.php?mod=viewthread&tid=11856
    

    转换为如下形式的链接:

    1
    
    [【转】关于Bootmgfw.efi、Bootx64.efi的详解 - 天南海北 - 萝卜头IT论坛 - Powered by Discuz!](https://bbs.luobotou.org/forum.php?mod=viewthread&tid=11856)
    

    可使用如下宏:

    1
    2
    3
    
    qk
    :s/\(^[^[].\+\): \(http.*\)$/[\1](\2)/
    :s/\(\[.*[^\\]\)|\(.*\](http.*)\)/\1\\|\2/e
    

    或(推荐):

    1
    2
    3
    
    qk
    :s/\(^[^[].\+\): \(http.*\)$/[\1](\2)/
    :s/|/|/ge
    

    其中的是编码为ff5c的 Unicode 字符,在 Vim 中可通过<C-V>uff5c输入

  15. 将如下内容:
    ```
    <p id="markdown-toc"></p>
    ```
    

    转换为如下内容:

    1
    2
    3
    
    <pre>
    &lt;p id="markdown-toc"&gt;&lt;/p&gt;
    </pre>
    

    使用:command命令自定义一个命令Rh

    1
    
    command! -range Rh <line1>,<line2>s/&/\&amp;/ge | <line1>,<line2>s/</\&lt;/ge | <line1>,<line2>s/>/\&gt;/ge | <line1>,<line2>s/^\(\s*\)```\(\_.\{-1,}\)```$/\1<pre>\2<\/pre>/e
    

    参见 HTML转义字符对照表regex - Substitute the n-th occurrence of a word in vim - Stack OverflowPassing visual range to a :command as its argument - Vi and Vim Stack Exchange

  16. 在引用前添加两个中文(Chinese)空格以便阅读:
    1
    2
    
    qc
    :s/^\(\s*> \)\([^ >&*#|0-9`]\)/\1\&emsp;\&emsp;\2/
    
  17. 将博客格式转换为 bbs 格式(例如 2019-07-07-科学上网.md):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    qy
    :%s/^\s*$\n^|/\r```\r|/
    :%s/^\(|.*\)$\n^\s*$/\1\r```\r/
    :%s/^\(\s*>\)\s*\n^\(\s*> |\)/\1\r\1 ```\r\2/
    :%s/^\(\s*> |.*\)$\n^\(\s*>\)\s*$/\1\r\2 ```\r\2/
    :%s/&emsp;&emsp;//
    5Gdgg
    O这是我个人博客上的文章,原文地址为:
    
    :/<!-- vim-markdown-toc GFM -->/,/<!-- vim-markdown-toc -->/s/  /   /g
    :g/^<p id=.*$/s//请不要点击下面的目录,因为论坛中的标题默认没有设置`id`
    :g/^<!-- .* -->/d
    :g/^\*\[.*\]: /d
    

vim自带帮助文件

  1. 复制代码行(Tab开头的行)到”a寄存器
    1
    
    qw"ayy:'a,'b g/^\t.\+$/normal "Ayy<Enter>
    
  2. |之间的内容转换为标题
    1
    
    :522,$s/|\(\d\+\.\d\+\)|/##\1/
    

asm 文件中

  1. sing.asm时需要将曲谱转换为相应的数组(使用了Python)
    1
    2
    
    :/;data start/+1,/;data end/-1 !./convert_music.py
    :w
    

Python 文件

  1. 保存并执行当前文件:
    1
    2
    
    :w
    :pyf %
    

所有文件

  1. 输出匹配的正则表达式的个数:
    1
    
    :s/^#//n " 输出 Python 文件中注释的行数"
    

二进制(.exe等)

  1. h hex-editing
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
     	 " vim -b : edit binary using xxd-format!
      augroup Binary
        au!
        au BufReadPre  *.bin let &bin=1
        au BufReadPost *.bin if &bin | %!xxd
        au BufReadPost *.bin set ft=xxd | endif
        au BufWritePre *.bin if &bin | %!xxd -r
        au BufWritePre *.bin endif
        au BufWritePost *.bin if &bin | %!xxd
        au BufWritePost *.bin set nomod | endif
      augroup END
    
  2. h usr_23.txt(23.4)