写了一段用来“黑” Vim 脚本的代码。很小很强大。

37 views
Skip to first unread message

Jacky Liu

unread,
Nov 11, 2009, 12:25:23 PM11/11/09
to vim...@googlegroups.com
Alright。这是第一次用邮件方式发帖,因为要粘个附件。如果搞砸了不要见怪,请管理员删除就好了。

附件里的 Vim Script 代码可以帮你 debug 自己写的 Vim 脚本,也可以帮你 hack 别人写的脚本。里面一共有三个函数和两个自定义命令。把这段代码贴到你想研究的脚本最后,一起运行,然后你就可以使用这两个命令了。其中一个命令可以显示脚本内任意一个变量(函数内部变量除外)当前的值,不论变量的类型和内部结构如何。另一个命令让你可以调用脚本内的任意一个函数,然后把返回值以相似的格式显示出来。这两个命令是:
        :DumpValue
        :ExeFunCall

举例来说,如果你正在研究的脚本里有个变量:s:myVar,你想看它当前的值,就执行:
        :DumpValue s:myVar

假设如果执行成功,消息会提示你变量的值已经转换为字串形式放入 "a 寄存器,你可以贴到一个文本文件里去查看。

如果脚本里有个函数 s:MyFun(argA, argB),你想用指定的参数来调用它来看看返回值如何,可以先定义临时变量来存放要传给它的参数:
        :let g:toargA= 'xxx'
        :let g:toargB= 'yyy'

,注意一定要是 g: 开头的全局变量才可以。这里假设两个参数都是 string 类型,其他类型也可以。然后执行:
        :ExeFunCall s:MyFun(g:toargA,g:toargB)
注意 ExeFunCall 的参数中间不能有空格。如果执行成功,消息会提示你返回值已经转换为字串形式放入 "b 寄存器,同样可以贴到一个文本文件里去查看。

对于 number,float,string 类型也可以不用定义变量,直接传递:
        :ExeFunCall s:MyFun(123,123.456)
        :ExeFunCall s:MyFun(''string1'',''string2'') 或者:
        :ExeFunCall s:MyFun(\"string1\",\"string2\")
对 string 类型需要加 quote,因为 string 会先以 Vim Script 的方式被解释一遍。

被 Dump 出来的值会以如下格式显示:
        s:myVar :        123                     (number)
        s:myVar :        123.45                (float)
        s:myVar :        'str_value'           (string)
        s:myVar -->    function('name')   (FuncRef)
        s:myVar[3]              (List)
                [0] :   123
                [1] :   123.45
                [2] :   'str_value'
        s:myVar{3}            (Dict)
                key_0 :    123
                key_1 :    123.45
                key_2 :    'str_value'

也就是说,对 number,float 类型直接显示,string 类型两边加上单引号,FuncRef 中间是 --> ,对 List 和 Dict 会以缩进的方式显示里面的每一项。当然,成员仍可以是 Dict 或 List,级数不限。

目前我正在用这段代码来 “黑” 一个比较流行的插件:NERD_tree.vim。这个插件写的很好(我认为),我执行下面的命令:
        ExeFunCall s:Bookmark.New(''bm01'',''/usr/home/bluegene8210'')

新建了一个 Bookmark 类的对象,得到的返回值是:
returned{24}
    AddBookmark -->        function('2')
    BookmarkExistsFor -->    function('4')
    BookmarkFor -->        function('5')
    BookmarkNames -->    function('6')
    Bookmarks -->        function('3')
    CacheBookmarks -->    function('7')
    ClearAll -->        function('9')
    GetNodeForName -->    function('12')
    InvalidBookmarks -->    function('13')
    New -->            function('15')
    Sort -->        function('17')
    ToRoot -->        function('20')
    Write -->        function('22')
    activate -->        function('1')
    compareTo -->        function('8')
    delete -->        function('10')
    getNode -->        function('11')
    mustExist -->        function('14')
    name :            'bm02'
    path :            '/usr/home/mememe'
    setPath -->        function('16')
    str -->            function('18')
    toRoot -->        function('19')
    validate -->        function('21')

可以看到,绝大部分都是成员函数,只有两个 string 类型的数据成员。

有趣的是,ExeFunCall 命令也可以用来调用这段代码自身的一个函数:s:StoreDataStructure(objName, objValue, objLevel)。这个函数的第一个参数是 string,表示变量名。第二个参数是个变量,类型不限。第三个参数是 number,表示变量的等级。这个函数是嵌套的,所以可以用来处理内部结构复杂的变量。执行下面的命令:
        :let g:myVar=['x', 'y', 'z']
        :ExeFunCall s:StoreDataStructure(''g:myVar'',g:myVar,0)
之后,得到的结果是:
        returned[4]
                [0] :    'g:myVar[3]'
                [1] :    '       [0] :    'x''
                [2] :    '       [1] :    'y''
                [3] :    '       [2] :    'z''

这段代码是为了 “黑” 别人的脚本而临时写的,很可能有缺陷。比如变量的值为空的时候,这些情况都没仔细考虑。写完后自己觉得思路比较有趣,就贴在这里。

还有,一次只能运行一个加了这段代码的脚本。别忘了最后把这段去掉。






hack.vim

蓝色基因

unread,
Nov 11, 2009, 3:03:06 PM11/11/09
to Vim.cn

这段里的命令贴错了,更正一下。这个命令我有执行两遍,与这段的结果对应的是:
ExeFunCall s:Bookmark.New(''bm02'',''/usr/home/mememe'')

另:好像用邮件发帖,用户名显示得不一样了。有人知道是怎么回事否?

闲耘(tm)

unread,
Nov 11, 2009, 8:07:10 PM11/11/09
to vim...@googlegroups.com
显示的是你Gmail里使用的名字(Name)

--
闲耘™ (@hotoo, xianyun.org)



2009/11/12 蓝色基因 <bluege...@gmail.com>

tocer

unread,
Nov 11, 2009, 9:06:06 PM11/11/09
to vim...@googlegroups.com
2009/11/12 Jacky Liu <bluege...@gmail.com>:

很酷,很强大

c9s

unread,
Nov 12, 2009, 4:31:24 AM11/12/09
to Vim.cn
其實我都只用 echo 耶

:echo Var
就會把各種型態的 data structure 理投的東西 print 出來了。只是沒有 well-formated. :p

On 11月12日, 上午10时06分, tocer <tocer.d...@gmail.com> wrote:
> 2009/11/12 Jacky Liu <bluegene8...@gmail.com>:


>
>
>
>
>
> > Alright。这是第一次用邮件方式发帖,因为要粘个附件。如果搞砸了不要见怪,请管理员删除就好了。
>
> > 附件里的 Vim Script 代码可以帮你 debug 自己写的 Vim 脚本,也可以帮你 hack

> > 别人写的脚本。里面一共有三个函数和两个自定义命令。把这段代码贴到你想研究的脚本最后,一起运行,然后你就可以使用这两个命令了。其中一个命令可以显示脚本内 任意一个变量(函数内部变量除外)当前的值,不论变量的类型和内部结构如何。另一个命令让你可以调用脚本内的任意一个函数,然后把返回值以相似的格式显示出来。 这两个命令是:

Ricky

unread,
Nov 12, 2009, 7:00:38 AM11/12/09
to vim...@googlegroups.com
我也要看谁就echo谁,配合BreakPts就可以debug脚本了

--------------------------------------------------
From: "c9s" <corneli...@gmail.com>
Sent: Thursday, November 12, 2009 5:31 PM
To: "Vim.cn" <vim...@googlegroups.com>
Subject: [Vim-cn:3124] Re: 写了一段用来“黑” Vim 脚本的代码。很小很强大。

蓝色基因

unread,
Nov 12, 2009, 7:23:51 AM11/12/09
to Vim.cn

On Nov 12, 5:31 pm, c9s <cornelius.h...@gmail.com> wrote:
> 其實我都只用 echo 耶
>
> :echo Var
> 就會把各種型態的 data structure 理投的東西 print 出來了。只是沒有 well-formated. :p


是的,string() 函数也是个选择。但是这个嵌套函数还可以有一个用途。如果你的脚本要在退出运行时把内部变量保存到一个文件,下次运行时通过读
取文件来重建上次的运行状态,那么只要有一个对应的读取函数:s:LoadDataStructure()(写起来稍微麻烦点但也不复杂)就可以实现
了。分行显示的格式除了看起来比较舒服之外,也比较利于线性处理,读取时 parse 回去比较简单。

如果 eval() 函数能支持高级类型(List 与 Dict)的话,用 string() + eval() 是个完美组合,遗憾的是目前还不
行。

闲耘(tm)

unread,
Nov 12, 2009, 7:28:52 AM11/12/09
to vim...@googlegroups.com
eval is evil  :-D
--
闲耘™ (@hotoo, xianyun.org)



2009/11/12 蓝色基因 <bluege...@gmail.com>
是的,string() 函数也是个选择。但是这个嵌套函数还可以有一个用途。如果你的脚本要在退出运行时把内部变量保存到一个文件,下次

蓝色基因

unread,
Nov 12, 2009, 7:47:14 AM11/12/09
to Vim.cn

On Nov 12, 8:28 pm, 闲耘(tm) <hotoo...@gmail.com> wrote:
> eval is evil :-D

right :) 我也相信不把 eval() 弄得像别的语言那样复杂是有道理的。

Reply all
Reply to author
Forward
0 new messages