举报投诉联系我们 手机版 热门标签 编程学
您的位置:编程学 > Vimscript 实例研究:Grep运算符(Operator),第二部分

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

2023-04-25 07:18 Vim教程

 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 <leader>g :set operatorfunc=GrepOperator<cr>g@

function! GrepOperator(type)
    echom "Test"
endfunction

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

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

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

可视模式

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

vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

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

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

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

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

动作类型

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

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

nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

function! GrepOperator(type)
    echom a:type
endfunction

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

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

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

复制文本

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

nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

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

    echom @@
endfunction

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

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

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

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

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

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

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

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

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

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

转义搜索文本

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

nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

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

    echom shellescape(@@)
endfunction

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

执行Grep

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

nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

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

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

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

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

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

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

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

练习

阅读:help visualmode()

阅读:help c_ctrl-u

阅读:help operatorfunc

阅读:help map-operator

阅读全文
以上是编程学为你收集整理的 Vimscript 实例研究:Grep运算符(Operator),第二部分全部内容。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。
相关文章
  •  GoFrame 模型关联-With特性

    GoFrame 模型关联-With特性

    2023-05-08 GoFrame教程

    一、设计背景大家都知道易用性和易维护性一直是​goframe​一直努力建设的,也是​goframe​有别其他框架和组件比较大的一点差异...

  • svg滤镜是什么 SVG 滤镜

    svg滤镜是什么 SVG 滤镜

    2023-04-20 SVG教程

    SVG 滤镜 本节介绍了 SVG 滤镜。SVG滤镜用来增加对SVG图形的特殊效果。SVG 滤镜 在本教程中,我们将仅展示一个可能采用的特殊效...

  •  XSL-FO region-start 对象

    XSL-FO region-start 对象

    2023-04-13 XSL-FO教程

    XSL-FO region-start 对象XSL-FO 参考手册XSL-FO 中,页面的左侧栏由 region-start对象定义。 定义和用法 fo:region-start 对象...

  •  XSL-FO table 对象

    XSL-FO table 对象

    2023-05-09 XSL-FO教程

    XSL-FO table 对象XSL-FO 参考手册定义和用法 fo:table 对象用于格式化表格的表格式材料。 fo:table 对象包含了可选的 fo:table-...

  •  XSL-FO table-row 对象

    XSL-FO table-row 对象

    2023-05-18 XSL-FO教程

    XSL-FO table-row 对象XSL-FO 参考手册XSL-FO 中表格的行通过 table-row对象定义。 定义和用法 fo:table-row 对象定义表格行。语...

© 2024 编程学 bianchengxue.com 版权所有 联系我们
桂ICP备19012293号-7 返回底部