×

vi 命令手册

vi/vim 命令大全

vim 命令手册

Vim 关于Vim 安装Vim 用法Vim 配置vim 配置.vimrc方案vim 配置.vimrc方案1vim 配置.vimrc方案2Vim 文件合并Vim 技巧和建议Vim 插件Vim 参阅

Vimscript 编程参考

Vimscript 前言Vimscript 鸣谢Vimscript 预备知识Vimscript 打印信息Vimscript 设置选项Vimscript 基本映射Vimscript 模式映射Vimscript 精确映射Vimscript LeadersVimscript 编辑你的Vimrc文件Vimscript AbbreviationsVimscript 更多的MappingsVimscript 锻炼你的手指本地缓冲区的设置和映射Vimscript 自动命令Vimscript 本地缓冲区缩写Vimscript 自动命令组Operator-Pending 映射Operator-Pending 更多映射Vimscript 状态条Vimscript 负责任的编码Vimscript 变量Vimscript 变量作用域Vimscript 条件语句Vimscript 比较Vimscript 函数Vimscript 函数参数Vimscript 数字Vimscript 字符串Vimscript 字符串函数Vimscript Execute命令Vimscript Normal命令Vimscript 执行normal!Vimscript 基本的正则表达式Grep 运算符Operator 第1部分Grep 运算符Operator 第2部分Grep 运算符Operator 第3部分Vimscript 列表Vimscript 循环Vimscript 字典Vimscript 切换Vimscript 函数式编程Vimscript 路径Vimscript 创建一个完整的插件Vimscript 基本插件配置方式用Pathogen配置插件Vimscript 检测文件类型Vimscript 基本语法高亮Vimscript 高级语法高亮Vimscript 基本折叠Vimscript 高级折叠Vimscript 段移动原理Vimscript Potion段移动Vimscript 外部命令Vimscript 自动加载Vimscript 文档Vimscript 发布Vimscript 还剩下什么?

Vimscript 实例研究:Grep运算符(Operator),第二部分


目前为止,我们已经完成了一个原型,是时候扩充它,让它更加强大。

记住:我们初始目标是创建"grep运算符"。我们还需要做一大堆新的东西来达成目标, 但要像前一章的过程一样:从简单的东西开始,并逐步改进直到它满足我们的需求。

在开始之前,注释掉~/.vimrc中在前一章创建的映射。我们还要用同样的快捷键来映射新的运算符。

新建一个文件

创建一个新的运算符需要许多命令,把它们手工打出来将很快变成一种折磨。 你可以把它附加到~/.vimrc,但让我们为这个运算符创建一个独立的文件。我们有足够的必要这么做。

首先,找到你的Vimplugin文件夹。在Linux或OS X,这将会是~/.vim/plugin。 如果你是Windows用户,它将位于你的主目录下的vimfiles文件夹。(如果你找不到,在Vim里使用`:echo $HOME命令) 如果这个文件夹不存在,创建一个。

plugin/下新建文件grep-operator.vim。这就是你放置新运算符的代码的地方。 一旦文件被修改,你可以执行:source %来重新加载代码。 每次你打开Vim,这个文件也会被重新加载,就像~/.vimrc

不要忘了,在你source之前,你_必须_先保存文件,这样才能看到变化!

骨架(Skeleton)

要创建一个新的Vim运算符,你需要从两个组件开始:一个函数还有一个映射。 先添加下面的代码到grep-operator.vim:

nnoremap g :set operatorfunc=GrepOperatorg@

function! GrepOperator(type)
    echom "Test"
endfunction

保存文件并用:source %source它。尝试通过按下giw来执行"grep整个词"。 Vim将在接受iw动作(motion)后,输出Test,意味着我们已经搭起了骨架。

函数部分是简单的,没有什么是我们没讲过的。不过映射部分比较复杂。 我们首先对函数设置了operatorfunc选项,然后执行g@来以运算符的方式调用这个函数。 看起来这有点绕,不过这就是Vim工作的原理。

暂时把这个映射看作黑魔法吧。稍后你可以到文档里一探究竟。

可视模式

我们已经在normal模式下加入了这个运算符,但还想要在visual模式下用到它。 在之前的映射下面添加多一个:

vnoremap g :call GrepOperator(visualmode())

保存并source文件。现在在visual模式下选择一些东西并按下g。 什么也没发生,但Vim确实输出了Test,所以我们的函数已经运行了。

之前我们就见过,但是还没有解释它是做什么的。试一下在可视模式下选中一些文本并按下:。 Vim将打开一个命令行就像平时按下了:一样,但是命令行的开头自动添加了'<,'>

Vim为了提高效率,插入了这些文本来让你的命令在被选择的范围内执行。 但是这次,我们不需要它添倒忙。我们用来执行"从光标所在处删除到行首的内容",移除多余文本。 最后剩下一个孤零零的:,为调用call命令作准备。

我们传递过去的visualMode()参数还没有讲过呢。 这个函数是Vim的内置函数,它返回一个单字符的字符串来表示visual模式的类型: "v"代表字符宽度(characterwise),"V"代表行宽度(linewise),Ctrl-v代表块宽度(blockwise)。

动作类型

我们定义的函数接受一个type参数。我们知道在visual模式下它将会是visualmode()的返回值, 但是在normal模式下呢?

编辑函数体部分,让代码像这样:

nnoremap g :set operatorfunc=GrepOperatorg@
vnoremap g :call GrepOperator(visualmode())

function! GrepOperator(type)
    echom a:type
endfunction

Source文件,然后继续并用多种的方式测试它。你可能会得到类似下面的结果:

  • 按下viwg显示v,因为我们处于字符宽度的visual模式。
  • 按下Vjjg显示V,因为我们处于行宽度的visual模式。
  • 按下giw显示char,因为我们在字符宽度的动作(characterwise motion)中使用该运算符。
  • 按下gG显示line,因为我们在行宽度的动作(linewise motion)中使用该运算符。

现在我们已经知道怎么区分不同种类的动作,这对于我们选择需要搜索的词是很重要的。

复制文本

我们的函数将需要获取用户想要搜索的文本,而这样做最简单的方法就是复制它。 把函数修改成这样:

nnoremap g :set operatorfunc=GrepOperatorg@
vnoremap g :call GrepOperator(visualmode())

function! GrepOperator(type)
    if a:type ==# 'v'
        execute "normal! `y"
    elseif a:type ==# 'char'
        execute "normal! `[v`]y"
    else
        return
    endif

    echom @@
endfunction

哇。好多新的东西啊。试试按下giwg2evi(g看看。 每次Vim都会输出动作所包括的文本,显然我们已经走上正道了!

让我们把这段代码一步步分开来看。首先我们用if语句检查a:type参数。如果是'v', 它就是使用在字符宽度的visual模式下,所以我们复制了可视模式下的选中文本。

注意我们使用大小写敏感比较==#。如果我们只用了==而用户设置ignorecase, "V"也会是匹配的,结果_不会_如我们所愿。重视防御性编程!

if语句的第二个分支则会拦住normal模式下使用字符宽度的动作。

剩下的情况只是默默地退出。我们直接忽略行宽度/块宽度的visual模式和对应的动作类型。 Grep默认情况下不会搜索多行文本,所以在搜索内容中夹杂着换行符是毫无意义的。

我们每一个if分支都会执行normal!命令来做两件事:

  • 在可视状态下选中我们想要的文本范围:
    • 先移动到范围开头,并标记
    • 进入字符宽度的visual模式
    • 移动到范围结尾的标记
  • 复制可视状态下选中的文本。

先不要纠结于特殊标记方式。你将会在完成本章结尾的练习时学到为什么它们会不一样。

函数的最后一行输出变量@@。不要忘了以@开头的变量是寄存器。@@是"未命名"(unnamed)寄存器: 如果你在删除或复制文本时没有指定一个寄存器,Vim就会把文本放在这里。

简明扼要地说:我们选中要搜索的文本,复制它,然后输出被复制的文本。

转义搜索文本

既然得到了Vim字符串形式的需要的文本,我们可以像前一章一样将它转义。修改echom命令成这样:

nnoremap g :set operatorfunc=GrepOperatorg@
vnoremap g :call GrepOperator(visualmode())

function! GrepOperator(type)
    if a:type ==# 'v'
        normal! `y
    elseif a:type ==# 'char'
        normal! `[v`]y
    else
        return
    endif

    echom shellescape(@@)
endfunction

保存并source文件,然后在可视模式下选中带特殊字符的文本,按下g。 Vim显示一个被转义了的能安全地传递给shell命令的文本。

执行Grep

我们终于可以加上grep!命令来实现真正的搜索。替换掉echom那一行,代码看起来就像这样:

nnoremap g :set operatorfunc=GrepOperatorg@
vnoremap g :call GrepOperator(visualmode())

function! GrepOperator(type)
    if a:type ==# 'v'
        normal! `y
    elseif a:type ==# 'char'
        normal! `[v`]y
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen
endfunction

看起来眼熟吧。我们简单地执行上一章得到的silent execute "grep! ..."命令。 由于我们不再把所有的代码塞进单个nnoremap命令里,现在代码甚至更加清晰易懂了!

保存并source文件,然后尝试一下,享受自己辛勤劳动的成果吧!

因为定义了一个全新的Vim运算符,现在我们可以在许多场景下使用它了,比如:

  • viwg: 可视模式下选中一个词,然后grep它。
  • g4w: Grep接下来的四个词。
  • gt;: Grep到分号为止的文本。
  • gi[: Grep方括号里的文本.

这里彰显了Vim的优越性:它的编辑命令就像一门语言。当你加入新的动词,它会自动地跟(大多数)现存的名词和形容词搭配起来。

练习

阅读:help visualmode()

阅读:help c_ctrl-u

阅读:help operatorfunc

阅读:help map-operator


分类导航

关注微信下载离线手册

bootwiki移动版 bootwiki
(群号:472910771)