Vimscript-技巧积累

切换 window

1
2
3
4
wincmd j
wincmd k
wincmd h
wincmd l

强制关闭所有缓冲区

1
:bufdo bd!

变量作用域

VimScript 中的变量作用域是通过 “前缀” 分隔的, 具体如下:

  • nothing, 即没有前缀, 如 let test = "test", 这样的变量若在函数中创建则为 local to function, 不然就是全局变量
  • b:, buffer variable, 对当前 buffer 有效 (似乎对新建的 buffer 无效?)
    1
    2
    3
    4
    let b:hello = "world"
    new
    echo b:hello
    " 会报错, 因为 new 创建了新 buffer
  • w:, window variable, 对当前 window 有效 (似乎对新建的 window 无效?)
    1
    2
    3
    4
    let w:hello = "world"
    split
    echo w:hello
    " 会报错, 因为 split 创建了新 window
  • t:, tabpage variable, 对当前 tab page 有效 (似乎对新建的 tabpage 无效?)
    1
    2
    3
    4
    let t:hello = "world"
    tabnew
    echo t:hello
    " 会报错, 因为 tabnew 创建了新 tab
  • g:, global, 全局变量
    1
    2
    3
    4
    5
    6
    7
    function! Test()
    let g:ll = "hello"
    endfunction

    call Test()
    echo g:ll
    " 输出 hello, 毕竟是全局变量
  • l:, local, 函数内局部变量 (在函数内不加前缀也默认是局部变量)
    1
    2
    3
    4
    5
    6
    7
    function! Test()
    let l:ll = "hello"
    endfunction

    call Test()
    echo l:ll
    " 会报错, 毕竟访问不了局部变量
  • s, script variable, 对当前 Vim Script 文件有效
    1
    2
    3
    4
    5
    6
    7
    8
    " in ~/.vim/plugin/01_test.vim
    let g:text1 = "global variable in 01_test.vim"
    let s:text1 = "script variable in 01_test.vim"

    " in ~/.vim/plugin/02_test.vim
    echo g:text1
    echo s:text1
    " 之后打开 vim 看
  • a:, function argument, 只在函数内有效
    1
    2
    3
    4
    function! Test(hello, world)
    echo a:hello . " " . a:world
    endfunction
    " 在函数内部调用函数形参需要用 a: 前缀
  • v:, vim variable, Vim 预定义的全局变量, 可以在 :h v: 中查看详细列表
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    " Vim 版本
    echo v:version
    " Vim 启用时的命令行参数
    echo v:argv

    " 最近捕获到的错误信息
    try
    throw "oops"
    catch /.*/
    echo "caught " .. v:exception
    endtry

    " viminfo 里的文件
    echo v:oldfiles

    " 用 ! 运行 shell 命令的报错信息
    echo v:shell_error

设置 statusline

可以先查看 :h laststatus:h statusline.

laststatus 选项设置是否显示 statusline, 可以设置三个值:
- 0: 不显示 statusline
- 1: 当开启至少两个 window 时显示
- 2: always 显示 statusline

statusline 选项定义了 status line 显示的内容, 默认为空, 模板字符串以 % 开头, 若想使用 % 字符, 应写为 %%, 总体的形式为:

  • %-0{minwid}.{maxwid}{item}, 这里除 %{item} 外都为可选
  • % 表明后面为模板字符串
  • - 表明 status line 左对齐
  • 0 表明会在数字项的显示前面添加零, 以确保它的总长度达到指定的宽度
  • minwid 指定 item 的最短宽度
  • maxwid 指定 item 的最大宽度
  • item 可以在 :h statusline 中详细查看, 这里给出几个常见的
    • f, 当前 buffer 中的文件名
    • F, 当前 buffer 中文件的绝对路径
    • m, 用 [+][-] 显示当前 buffer 是否更改
    • y, 显示文件类型

示例:

1
2
set laststatus=2
set statusline=%F---%y---%m

若想用一个函数的返回值 (返回模板字符串), 前缀应为: %!, 如:

  • :set statusline=%!MyStatusLine()

可以设置 g:statusline_winid 变量指定该 status line 作用于哪个 window.

1
2
set laststatus=2  " 始终显示状态行
set statusline=%f " 文件名

如果设置后 status line 没更新, 可手动更新:

1
2
:set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P
:redrawstatus

跳转到 help manual 中的标签

Ctrl+].

v:statusmsg 变量

其会保存上一次的 status message. 比如在 hello.sh 文件中进行保存操作, 其会输出如 “hello.sh 117L, 4700B written” 这个信息, 其就会被保存在 v:statusmsg 这个变量中.

verbose 命令

:verbose 命令用于显示当前正在执行的某个命令的详细信息,包括该命令是如何被触发的.

可以通过设置 verbose 这个 option 来开启,

在设置 verbosefile 选项后, verbose 的输出内容会输出到指定文件中 (默认关闭文件时才会写入).

跳转

回到上一次跳转的位置:使用 Ctrl + O 或者 :jumplist - 命令。

回到下一次跳转的位置:使用 Ctrl + I 或者 :jumplist + 命令。

Ctrl + ] 可以跳转到光标所在位置的定义或引用,而 Ctrl + T 则用于返回到之前的位置

使用技巧

change list 和 jump list

g; 可以回到上次被修改的位置.

g, 到下一处被修改的位置.

同时都可以加量词, 如 2g;

查看 change list: :changes

查看 jump list: :jumps

ctrl+o 跳转到前一个 jump.

ctrl+i 跳转到后一个 jump.

应用正则表达式

参考

使用 match(), matchstr() 等函数.

光标的位置

使用 getcurpos().

用 Vimscript 插入文本

结合 execute, normal 命令, 如:

1
2
let l:text = "Hello World"
execute "normal! a" . l:text

将一个字符串重复 N 次

参考

使用 repeat(), 如:

1
let foo = repeat("abc", 3)

使用正则表达式的捕获

如:

1
:%s/\(test\)/\1/ 

捕获用的括号需要转义为 \( \).

对一个变量使用 substitute

参考

使用 substitute(), 具体看 :h substitute().

这里面的捕获型要写成 \\1

编写统计汉字

将一串字符分隔为一个一个的字符

1
split(string, '\zs')

其返回值是列表, 每一个元素是一个字符.

求一个字符的 utf-8 的 number

1
char2nr(char)

其返回值是一个数字.

总流程

1
2
3
4
5
for char in split(text, '\zs')
if char2nr(char) >= 0x2000
let cc += 1
endif
endfor

这里定义 unicode 大于 0x2000 的为汉字.

获得一个目录下的所有文件

使用 globpath() 函数。注意该函数返回的是 string 而不是 list.

如获得当前目录下的所有文件:

1
let list = split(globpath('.', '*'), '\n')

且为全路径.

关于 normal d

单纯的:

1
exec 'normal d'

并不会删除文本,只是将其剪切到 " 寄存器中,需要删除文本,用:

1
exec 'normal gvd'

用 source 命令加载一个存储在变量中的文件名

参考网页

结合 exec 命令和 expand() 函数, 如:

1
2
let mypath = '/tmp/'
exec "source " . expand(mypath) . "test.vim"

即可.

关于函数的参数

使用如:

1
2
3
4
5
6
7
let mypath = '/tmp/'

function LoadFile(path, file) abort
execute "source " . expand(a:path) . expand(a:file)
endfunction

call LoadFile(mypath, "print.vim")

注意, 在定义函数时没有加 a:, 而在使用函数时用了 a:.

关于 list 列表赋值

如:

1
2
3
4
5
6
7
8
9
10
let b:files = [
\ "user/markdown-preview.vim",
\ "user/vim-table-mode.vim",
\ "user/vim-mundo.vim",
\ "user/filetype.vim",
\ "user/run.vim",
\ "user/chineseCount.vim",
\ "user/mark.vim",
\ "user/mdtitle.vim",
\ ]

注意这里的 \ , 不加反斜线就会报错.

获取列表的长度

使用 len()

命令行参数

位于列表 v:argv 中.

打开光标下的 url

使用 gx

选中行的范围表示

'< 表示选中的第一行.

'> 表示选中的最后一行.

'<'> 表示选中的所有行.

判断是否位于折叠之中

在 Vim 中,可以使用 foldclosed() 函数和 foldclosedend() 函数来判断光标是否位于一个折叠中。

foldclosed() 函数返回当前行所在的折叠的开始行号(如果当前行不在任何折叠中,则返回 -1)。
foldclosedend() 函数返回当前行所在的折叠的结束行号(如果当前行不在任何折叠中,则返回 -1)。

可以将这两个函数结合使用,来判断光标是否位于一个折叠中。如果 foldclosed()foldclosedend() 函数都返回大于等于 0 的值,则说明当前行位于一个折叠中。


Vimscript-技巧积累
http://example.com/2022/12/06/Vimscript-技巧积累/
作者
Jie
发布于
2022年12月6日
许可协议