zoukankan      html  css  js  c++  java
  • vim的script、function及command

    一、脚本

    和大部分Unix工具一样,vim也提供了内置的脚本功能,通过脚本可以完成定制化设置。脚本的优点在于正如它名字所暗示的:可以存储在文件中。而文件可以持久化,也就是下次打开的时候依然存在。脚本中通常还可以定制函数以实现复用。
    例如,在常用的CtrlP插件中,大部分功能都是使用vim内置命令完成的,这样基本上不存在移植性问题。和这个对应的是YouCompletMe,它虽然也是通过vim的插件完成,但是它依赖的外部工具就多很多了。

    二、脚本的内置命令

    脚本中可以执行的命令和Ex-mode下内置功能集合相同,事实上,在vim代码内部,对于脚本的解析和执行(source)与ex的区别只是在于文件的读取方式不同:
    do_source==>>do_cmdline
    do_exmode==>>do_cmdline
    两者的区别在于前者传入的fgetline函数是getsourceline,而后者使用的是getexmodeline
    vim-8.1-usingsrcex_docmd.c
    int
    do_cmdline(
    char_u *cmdline,
    char_u *(*fgetline)(int, void *, int),
    void *cookie, /* argument for fgetline() */
    int flags)
    {
    ……
    }

    三、function的执行

    在vim中,function是一个重要的概念(当然,其它语言中function也是)。在vim的源代码中,userfunc.c用来处理用户自定义/扩展的函数,通俗的说,就是通过function定义的函数;而evalfunc.c文件中包含的则是系统内置的(builtin)函数,这些内置函数和其它系统中一样,是用户自定义函数的基础。例如

    /*
    * Array with names and number of arguments of all internal functions
    * MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH!
    */
    static struct fst
    {
    char *f_name; /* function name */
    char f_min_argc; /* minimal number of arguments */
    char f_max_argc; /* maximal number of arguments */
    void (*f_func)(typval_T *args, typval_T *rvar);
    /* implementation of function */
    } functions[] =
    {
    #ifdef FEAT_FLOAT
    {"abs", 1, 1, f_abs},
    {"acos", 1, 1, f_acos}, /* WJMc */
    #endif
    ……
    {"keys", 1, 1, f_keys},
    {"last_buffer_nr", 0, 0, f_last_buffer_nr},/* obsolete */
    {"len", 1, 1, f_len},
    ……
    }

    四、function名字中的#符号

    可以看到是从前面添加上"autoload/"文件夹,后面加上".vim"后缀,然后将中间的"#"替换为路径分隔符"/"。
    /* Character used as separated in autoload function/variable names. */
    #define AUTOLOAD_CHAR '#'
    vim-8.1-usingsrceval.c
    /*
    * Return the autoload script name for a function or variable name.
    * Returns NULL when out of memory.
    */
    char_u *
    autoload_name(char_u *name)
    {
    char_u *p;
    char_u *scriptname;

    /* Get the script file name: replace '#' with '/', append ".vim". */
    scriptname = alloc((unsigned)(STRLEN(name) + 14));
    if (scriptname == NULL)
    return FALSE;
    STRCPY(scriptname, "autoload/");
    STRCAT(scriptname, name);
    *vim_strrchr(scriptname, AUTOLOAD_CHAR) = NUL;
    STRCAT(scriptname, ".vim");
    while ((p = vim_strchr(scriptname, AUTOLOAD_CHAR)) != NULL)
    *p = '/';
    return scriptname;
    }

    从vim的代码看,在读取这种函数声明的时候,会检测定义的函数是否和当前sourcing的路径名相同。如果不相同,在定义的时候就会报错:
    /*
    * ":function"
    */
    void
    ex_function(exarg_T *eap)
    {
    ……
    if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL)
    {
    int slen, plen;
    char_u *scriptname;

    /* Check that the autoload name matches the script name. */
    j = FAIL;
    if (sourcing_name != NULL)
    {
    scriptname = autoload_name(name);
    if (scriptname != NULL)
    {
    p = vim_strchr(scriptname, '/');
    plen = (int)STRLEN(p);
    slen = (int)STRLEN(sourcing_name);
    if (slen > plen && fnamecmp(p,
    sourcing_name + slen - plen) == 0)
    j = OK;
    vim_free(scriptname);
    }
    }
    if (j == FAIL)
    {
    EMSG2(_("E746: Function name does not match script file name: %s"), name);
    goto erret;
    }
    }
    ……
    }
    例如
    call plug#begin('~/.vim/plugged')
    表示执行"autoload/plug.vim"文件中的begin函数,并且参数为'~/.vim/plugged'。

    五、command命令

    这种命令也被称为是user command,它们更多的类似于一个alias,这里定义的内容在执行时会被替换为command定义的字符串。例如,vim中流行的插件管理工具vundle就将Plugin定义为一个command。它和function的区别在于function的调用需要添加call前缀,而command不需要,并且command可以支持自动补全。
    :verbose command Plugin
    Name Args Address Complete Definition
    Plugin + call vundle#config#bundle(<args>)
    最近修改于 ~/.vim/bundle/Vundle.vim/autoload/vundle.vim

    而~/.vim/bundle/Vundle.vim/autoload/vundle.vim文件中Plugin的命令定义为
    1 " Vundle is a shortcut for Vim Bundle and Is a simple plugin manager for Vim
    2 " Author: gmarik
    3 " HomePage: http://github.com/gmarik/Vundle.vim
    4 " Readme: http://github.com/gmarik/Vundle.vim/blob/master/README.md
    5 " Version: 0.10.2
    6
    7 " Plugin Commands
    8 com! -nargs=+ -bar Plugin
    9 call vundle#config#bundle(<args>)
    10
    11 com! -nargs=? -bang -complete=custom,vundle#scripts#complete PluginInstall
    12 call vundle#installer#new('!' == '<bang>', <q-args>)

    六、command展开时特殊变量的替换

    总起来说,主要集中在尖括号("<>")中的内容会被尝试展开

    ex_docmd.c

    /*
    * Check for a <> code in a user command.
    * "code" points to the '<'. "len" the length of the <> (inclusive).
    * "buf" is where the result is to be added.
    * "split_buf" points to a buffer used for splitting, caller should free it.
    * "split_len" is the length of what "split_buf" contains.
    * Returns the length of the replacement, which has been added to "buf".
    * Returns -1 if there was no match, and only the "<" has been copied.
    */
    static size_t
    uc_check_code(
    char_u *code,
    size_t len,
    char_u *buf,
    ucmd_T *cmd, /* the user command we're expanding */
    exarg_T *eap, /* ex arguments */
    char_u **split_buf,
    size_t *split_len)
    {
    size_t result = 0;
    char_u *p = code + 1;
    size_t l = len - 2;
    int quote = 0;
    enum {
    ct_ARGS,
    ct_BANG,
    ct_COUNT,
    ct_LINE1,
    ct_LINE2,
    ct_RANGE,
    ct_MODS,
    ct_REGISTER,
    ct_LT,
    ct_NONE
    } type = ct_NONE;

    if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-')
    {
    quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
    p += 2;
    l -= 2;
    }

    ++l;
    if (l <= 1)
    type = ct_NONE;
    else if (STRNICMP(p, "args>", l) == 0)
    type = ct_ARGS;
    else if (STRNICMP(p, "bang>", l) == 0)
    type = ct_BANG;
    else if (STRNICMP(p, "count>", l) == 0)
    type = ct_COUNT;
    else if (STRNICMP(p, "line1>", l) == 0)
    type = ct_LINE1;
    else if (STRNICMP(p, "line2>", l) == 0)
    type = ct_LINE2;
    else if (STRNICMP(p, "range>", l) == 0)
    type = ct_RANGE;
    else if (STRNICMP(p, "lt>", l) == 0)
    type = ct_LT;
    else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0)
    type = ct_REGISTER;
    else if (STRNICMP(p, "mods>", l) == 0)
    type = ct_MODS;

    ……

    }

     七、vundle插件对插件安装源的查找

    可见是通过拼凑github地址完成的安装源查找

    "~/.vim/bundle/Vundle.vim/autoload/vundle/config.vim

    135 func! s:parse_name(arg)
    136 let arg = a:arg
    137 let git_proto = exists('g:vundle_default_git_proto') ? g:vundle_default_git_proto : 'https'
    138
    139 if arg =~? '^s*(gh|github):S+'
    140 || arg =~? '^[a-z0-9][a-z0-9-]*/[^/]+$'
    141 let uri = git_proto.'://github.com/'.split(arg, ':')[-1]
    142 if uri !~? '.git$'
    143 let uri .= '.git'
    144 endif
    145 let name = substitute(split(uri,'/')[-1], '.gits*$','','i')
    146 elseif arg =~? '^s*(git@|git://)S+'
    147 || arg =~? '(file|https?)://'
    148 || arg =~? '.gits*$'
    149 let uri = arg
    150 let name = split( substitute(uri,'/?.gits*$','','i') ,'/')[-1]
    151 else
    152 let name = arg
    153 let uri = git_proto.'://github.com/vim-scripts/'.name.'.git'
    154 endif
    155 return {'name': name, 'uri': uri, 'name_spec': arg }
    156 endf

     七、在命令中使用内置变量

    <cword>可以获得当前光标下单词,所以可以定义下面命令,将光标放在指定单词之后执行OpenFile打开光标处文件。

    command! OpenFile :  e <cword>

    其它的内置变量可以通过help <cword>获得:

  • 相关阅读:
    条件语句的用法
    PHP取得当前文档所在的目录
    郁闷,一个语句调试很久
    PHP图片上传加水印(转)
    PHP多行多列分页
    ASP得到当前文件所在目录
    “树人杯”暨第三届辽宁科技大学校园程序设计竞赛正赛D IP检测(绿)
    “树人杯”暨第三届辽宁科技大学校园程序设计竞赛正赛E 成绩统计图(红)
    [面试备] 暴搜 or 二分图的经典升级 : hdu 1045 Fire Net 示例 [ 讲解之用 ]
    《C++ Primer》 第04章 [ 数组和指针 ]
  • 原文地址:https://www.cnblogs.com/tsecer/p/14778394.html
Copyright © 2011-2022 走看看