[译] Vimscript 有路勤为径 —— 创建完整插件

目录: vim | 标签: | 发布时间:

创建完整插件

在前面四十来章中,我们已经涉及了很多的内容。 在本书的最后部分中,我们将会从头开始,一步步地为一个语言创建一个 Vim 插件。

这不适合心生畏惧的人。因为这将花费很大的努力。

如果你想现在终止,完全没问题! 你学到的已经足够在 ~/.vimrc 文件中添加增加功能,或是找到其他人的插件中的错误。

说出“这些已经足够了,我并不想花更多的时间来创建插件,我也不太会用到”并不可耻。 实事求是就行。如果你没有考虑过想要创建什么样的插件,那现在就停止,并回去做你该做的。

如果确实想继续,那请确保你准备好了献出一些时间。 本书剩余的部分会很花精力,而我会假设你确实想要学习,而不只是在沙发上匆匆浏览一下。

Potion

我们将要创建的插件是要支持 Potion 语言。

Potion 是一个由名叫“_why the lucky stiff”的人在他消失之前创建的玩具语言。 这个一个极小的语言,这正符合我们的目的。

Potion 感觉很像 Io,并混合了 Ruby、Lua、以及其他的语言中的一些思想。 如果你从来没用过 Io,那可能看上去会有点古怪。我强烈建议先用 Potion 玩一到两个小时。 你不会在实际生活中用到它,但是它可能会改变你的思维,展示给你一些新的想法。

Potion 当前的实现有很多不完善的地方。例如:如果你弄错了语法,它就会出现段错误。 不过不用太担心。我会提供很多正确的示例代码,这样就可以集中注意力在 Vimscript 上,而不是 Potion。

我们的目标不是学习 Potion(虽然这也很有趣)。 我们的目标是把 Potion 作为一个小例子,这样就可以在写 Vim 插件时接触到很多不同方面的内容。

阅读更多>>


[译] Vimscript 有路勤为径 —— 路径

目录: vim | 标签: | 发布时间:

路径

Vim 是一个文本编辑器,而文本编辑器(通常)是用来处理文本文件的。 文本文件存在于文件系统中,而路径就是用来表述文件的。 Vimscript 有一些内置工具,能在你需要处理路径时发挥巨大的作用。

绝对路径

有时候,用一个外部脚本就能很方便地获取一个文件的绝对路径。运行以下命令:

:echom expand('%')
:echom expand('%:p')
:echom fnamemodify('foo.txt', ':p')

第一个命令显示你正在编辑的文件的相对地址。% 表示“当前文件”。 Vim 还支持很多其他的字符串和 expand() 一起使用。

第二个命令显示文件完整的绝对路径。字符串中的 :p 告诉 Vim 我想要的是绝对路径。 同样,也有很多其他的修饰符可以使用。

第三个命令显示当前目录下 foo.txt 文件的绝对地址,不管文件是不是实际存在。 fnamemodify() 是一个比 expand() 更复杂的 Vim 函数,它可以让你指定任何文件名, 而不只是 expand() 中的一个特殊字符串。

阅读更多>>


[译] Vimscript 有路勤为径 —— 函数式编程

目录: vim | 标签: | 发布时间:

函数式编程

我们现在休息片刻来讲讲一种编程风格,你也许听说过,那就是:函数式编程

如果你用过 Python、Ruby、Javascript、特别是 Lisp、Scheme、Clojure 或 Haskell, 那很可能已经熟悉把函数作为变量和使用不可变的数据结构。 如果从来没使用过,你可以放心的跳过这个章节,但是我建议你无论如何都试试,这可以开阔你的视野!

Vimscript 拥有所有函数式编程所必需的元素,但是有点臃肿。 但我们可以创建一些辅助函数来减少痛苦。

先去创建一个 functional.vim 文件,这样你就不需要反复键入所有的内容。这个文件将用于本章。

不可变数据结构

遗憾的是 Vim 并没有不可变类型,类似 Clojure 内置的向量和映射,但是通过创建一些辅助函数, 我们可以一定程度的进行伪造。

把以下函数添加到文件中:

function! Sorted(l)
    let new_list = deepcopy(a:l)
    call sort(new_list)
    return new_list
endfunction

保存并载入执行文件,然后试试运行 :echo Sorted([3, 2, 4, 1])。Vim 显示了 [1, 2, 3, 4]

这个和单单调用内置的 sort() 函数有什么不同呢?关键点在于第一行:let new_list = deepcopy(a:l)。 Vim 的 sort() 会把列表排序并替换,所以我们先创建一份列表的完全拷贝,然后对进行排序,这样初始的列表就不会被更改。

这防止出现副作用,并帮助我们写代码时更轻松的推导和测试。

阅读更多>>


[译] Vimscript 有路勤为径 —— 开关

目录: vim | 标签: | 发布时间:

开关

在最早的某个章节中,我们讨论过在 Vim 中如何设置选项。 针对布尔值的选项,我们可以用 set someoption! 来“开关”选项。 这对于我们创建映射是特别有用的。

运行以下命令:

:nnoremap <leader>N :setlocal number!<cr>

试试在普通模式下按下 <leader>N。Vim 会开关当前窗口的的行号。 创建这样的“开关”映射确实很方便,因为我们不需要分别用两个按键来开和关了。

遗憾的是这只对布尔值选项有效。如果我们想开关一个非布尔值选项,那就要更复杂一些了。

开关选项

让我们从创建一个可以开关选项的函数开始,并且创建一个映射来调用它。 把以下代码放进你的 ~/.vimrc 文件(或者也可以是 ~/.vim/plugin 中的一个独立文件):

nnoremap <leader>f :call FoldColumnToggle()<cr>

function! FoldColumnToggle()
    echom &foldcolumn
endfunction

保存并载入执行文件,然后试试按下 <leader>f,Vim 会显示 foldcolumn 选项当前的值。 如果你不熟悉这个选项,就去阅读 :help foldcolumn

阅读更多>>


[译] Vimscript 有路勤为径 —— 字典

目录: vim | 标签: | 发布时间:

字典

我们要讲的最后一种 Vimscript 变量类型是字典。 Vimscript 字典类似于 Python 的字典、Ruby 的哈希、以及 Javascript 的对象。

字典可以用花括号来创建。值可以是各种类型,但是键总是会被强转为字符串。 你不会期待凡事都是完全一样的,对吧?

运行以下命令:

:echo {'a': 1, 100: 'foo'}

Vim 显示 {'a': 1, '100': 'foo'},这表示 Vimscript 确实把键强转为了字符串,而保留值的类型不动。

Vimscript 不像 Javascript 标准那么愚钝,它允许在字典的最后一个元素后面添加逗号。 运行以下命令:

:echo {'a': 1, 100: 'foo',}

Vim 还是显示 {'a': 1, '100': 'foo'}。你应该总是在字典末尾添加逗号, 特别是使用多行定义的时候,这让你在添加新条目时不容易出错。

索引

你可以像大多数语言那样在字典中检索某个键。运行命令:

:echo {'a': 1, 100: 'foo',}['a']

Vim 显示 1。试试使用一个非字符串索引:

:echo {'a': 1, 100: 'foo',}[100]

Vim 在检索之前会先把索引强转为字符串,这很有必要,因为键也只能是字符串。

Vimscript 也支持 Javascript 风格的 “.” ,但是键只能由字母、数字、以及下划线组成。 试试以下命令:

:echo {'a': 1, 100: 'foo',}.a
:echo {'a': 1, 100: 'foo',}.100

Vim 都会显示正确的元素。你可以根据自己的喜好和风格来选择使用哪种方式。

阅读更多>>


[译] Vimscript 有路勤为径 —— 循环

目录: vim | 标签: | 发布时间:

循环

你可能会惊讶的发现,虽然我们已经完成了这本编程语言书籍的35个章节,但是还没有涉及到循环! Vimscript 提供了如此多其他的选项来实现文本上的操作(例如 normal!),所以循环不如其他语言中显得那么重要。

虽然如此,可能某一天你确实需要用到,所以,我们现在来看看 Vim 支持的两种主要循环。

for 循环

第一种循环是 for 循环。如果你使用过 Java、C、或是 Javascript 的 for 循环, 可能会觉得有点奇怪,但是也足够优雅。运行以下命令:

:let c = 0

:for i in [1, 2, 3, 4]
:  let c += i
:endfor

:echom c

Vim 显示 10,这是把列表中的元素加起来的结果。 Vimscript 的 for 循环会遍历列表(或者字典,我们会在后面讲到)。

在 Vimscript 中,没有等效于 C 风格 for (int i = 0; i < foo; i++) 的循环形式。 乍看起来可能很糟,但是实际上,你不会想念它的。

阅读更多>>


[译] Vimscript 有路勤为径 —— 列表

目录: vim | 标签: | 发布时间:

列表

到目前为止,我们用过很多次变量了,但还没有讨论过聚合! Vim 主要有两种聚合类型,我们现在就来看看第一种:列表。

Vimscript 列表是有序的、多类型的元素集合。运行以下命令:

:echo ['foo', 3, 'bar']

Vim 显示了这个列表。当然,列表是可以嵌套的。运行以下命令:

:echo ['foo', [3, 'bar']]

Vim 正确的显示了这个列表。

索引

Vimscript 列表的索引是从0开始的,你可以像平常一样获取元素。运行命令:

:echo [0, [1, 2]][1]

Vimscript 显示 [1, 2]。你也可以反方向使用索引,就如 Python 一样。试试命令:

:echo [0, [1, 2]][-2]

Vim 显示 0。索引 -1 表示列表的最后一个元素,-2 表示倒数第二个元素,以此类推。

阅读更多>>


[译] Vimscript 有路勤为径 —— 案例分析:grep操作符,第三部分

目录: vim | 标签: | 发布时间:

案例分析:grep操作符,第三部分

我们新的“grep操作符”运行的很好,但还是要想办法让你的用户在编写 Vimscript 时感觉轻松惬意。 我们可以再优化两点,使这操作符可以更好的在 Vim 生态中发挥作用。

保存寄存器

如果复制文本到无名寄存器,那我们就把之前寄存在那的内容给清除了。 更进一步的,使用我们的操作符并移动,再可视化选择文本并复制,这样也会清除上次可视化选择的文本。

这对我们的用户不是很友好,所以在这种情况下需要避免使用可视化选择,并且在任何需要复制文本的时候, 把无名寄存器中的内容保存起来,这样在处理完成之后,就可以恢复了。 修改代码成如下:

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

function! GrepOperator(type)
    let saved_unnamed_register = @@

    if a:type ==# 'v'
        normal! `<v`>y
    elseif a:type ==# 'char'
        normal! `[y`]
    else
        return
    endif

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

    let @@ = saved_unnamed_register
endfunction

我们在函数的顶部和底部分别添加了两行 let 语句。 第一行,把 @@ 的内容保存到一个变量中,然后第二行恢复它。 此外,在我们的操作符应用到一个移动命令时,我们用复制替换可视化选择。

保存并载入执行文件。为了确保它能正常运行,我们先试着复制一些问文本,然后按下 <leader>giw 来运行我们的操作符, 然后按下 p 粘贴出来,并检查是不是之前复制的文本。

当编写 Vim 插件是,你应该总是力求保存和恢复任何代码会修改的设置或寄存器,这样才能不让你的用户感到诧异和困惑。

阅读更多>>


[译] Vimscript 有路勤为径 —— 案例分析:grep操作符,第二部分

目录: vim | 标签: | 发布时间:

案例分析:grep操作符,第二部分

现在我们已经得到了一个解决方案的初步框架,是时候给它填充更多能量了。

记住:我们最初的目标是创建一个“grep操作符”。为了实现这个,还有一大堆的新东西要讲。 但是像上一章节里一样:我们先从一些简单的内容开始,然后不停的改造,直到成为你想要的。

在我们开始之前,先把 ~/.vimrc 文件中上一章节刚创建的映射注释掉 —— 我们会为新建的操作符绑定同样的按键。

创建一个文件

创建一个操作符需要若干命令,徒手键入这些很快就会令人乏味。 你可以添加到 ~/.vimrc 文件中,但我们还是为这个操作符单独新建一个文件。 它已经足够丰富来独自支撑一个文件了。

首先,找到你 Vim 的 plugin 目录。Linux 或者 OS X 系统里,是在 ~/.vim/plugin。 如果是 Windows 系统,会在用户目录下的 vimrfiles 目录里。 (如果你不确认用户目录在哪,在 Vim 中使用 :echo $HOME 命令查看)。 如果这个目录不存在,那创建一个。

plugin/ 中创建一个名为 grep-operator.vim 的文件。你将在这里编写新的操作符代码。 在编辑文件的同时,你可以随时运行 :source % 来重载代码。 这个文件也会和 ~/.vimrc 一样,在每次打开 Vim 时载入。

记住,你必须先保存文件,再载入执行,这样才能看到更改!

阅读更多>>


[译] Vimscript 有路勤为径 —— 案例分析:grep操作符,第一部分

目录: vim | 标签: | 发布时间:

案例分析:grep操作符,第一部分

在本章节和后面的章节中,我们会完整地创建一段足够复杂的 Vimscript 代码。 我们会讨论几种之前没看过的东西,以及如何把之前学过的东西在实战中组合在一起。

在你完成本次案例分析的过程中,碰到了任何不熟悉的,都应该用 :help 查阅。 如果似懂非懂地完成了所有的东西,你将学不到多少东西。

grep 命令

如果从来没用过 :grep,你现在就应该花上一分钟来阅读 :help :grep:help :make。 如果之前从来没有用过 quickfix 窗口,那就阅读 :help quickfix-window

简而言之::grep ... 会运行一个外部grep程序来处理输入的任何参数,解析结果, 然后填充到quickfix列表,这样你就可以在 Vim 中跳到那些结果上了。

我们的例子就是要让 :grep 更容易使用,通过添加一个“grep操作符”,可以用任何 Vim 内置(或自定义)的移动来选择想要搜索的文本。

阅读更多>>

加载更多