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-using\src\ex_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-using\src\eval.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], '\.git\s*$','','i')
146 elseif arg =~? '^\s*\(git@\|git://\)\S\+'
147 \ || arg =~? '\(file\|https\?\)://'
148 \ || arg =~? '\.git\s*$'
149 let uri = arg
150 let name = split( substitute(uri,'/\?\.git\s*$','','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>獲得:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM