patch 9.2.1160: no way to get the definition of item from Vim script

Problem:  There is no way to get the definition of a builtin or a user
          defined function, of an Ex command, of an option or of a v:
          variable from Vim script.  A language server has to parse the
          help text or the output of ":function" and ":command" for it.
Solution: Add getinfo({kind}, {name} [, {opts}]), which returns a Dict
          with the definition of {name}: a function, an Ex command, an
          option or a v: variable.  Keep the names of the arguments of
          the builtin functions in their table and report them in
          "args" (Hirohito Higashi).

closes: #21293

Assisted-by: Claude
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Hirohito Higashi authored and Christian Brabandt committed 2026-09-30 20:57:50 +00:00
1 parent ae8837c19d
commit 89c0f36163
19 files changed
+2021 -31

No files matched your search

+217 -25
View File
@@ -1,4 +1,4 @@
*builtin.txt* For Vim version 9.2. Last change: 2026 Sep 25
*builtin.txt* For Vim version 9.2. Last change: 2026 Sep 30
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -133,7 +133,7 @@ charclass({string}) Number character class of {string}
charcol({expr} [, {winid}]) Number column number of cursor or mark
charidx({string}, {idx} [, {countcc} [, {utf16}]])
Number char index of byte {idx} in {string}
chdir({dir}) String change current working directory
chdir({dir} [, {scope}]) String change current working directory
cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
cmdcomplete_info() Dict get current cmdline completion
@@ -177,7 +177,7 @@ escape({string}, {chars}) String escape {chars} in {string} with '\'
eval({string}) any evaluate {string} into its value
eventhandler() Number |TRUE| if inside an event handler
executable({expr}) Number 1 if executable {expr} exists
execute({command}) String execute {command} and get the output
execute({command} [, {silent}]) String execute {command} and get the output
exepath({expr}) String full path of the command {expr}
exists({expr}) Number |TRUE| if {expr} exists
exists_compiled({expr}) Number |TRUE| if {expr} exists at compile time
@@ -214,11 +214,11 @@ fmod({expr1}, {expr2}) Float remainder of {expr1} / {expr2}
fnameescape({fname}) String escape special characters in {fname}
fnamemodify({fname}, {mods}) String modify file name
foldclosed({lnum} [, {winid}]) Number first line of fold at {lnum} if closed
foldclosedend({lnum} [, {winid}]))
foldclosedend({lnum} [, {winid}])
Number last line of fold at {lnum} if closed
foldlevel({lnum} [, {winid}])) Number fold level at {lnum}
foldlevel({lnum} [, {winid}]) Number fold level at {lnum}
foldtext() String line displayed for closed fold
foldtextresult({lnum} [, {winid}]))
foldtextresult({lnum} [, {winid}])
String text for closed fold at {lnum}
foreach({expr1}, {expr2}) List/Tuple/Dict/Blob/String
for each item in {expr1} call {expr2}
@@ -275,6 +275,8 @@ getfsize({fname}) Number size in bytes of file {fname}
getftime({fname}) Number last modification time of file
getftype({fname}) String description of type of file {fname}
getimstatus() Number |TRUE| if the IME status is active
getinfo({kind}, {name} [, {opts}])
Dict the definition of {name}
getjumplist([{winnr} [, {tabnr}]])
List list of jump list items
getline({lnum}) String line {lnum} of current buffer
@@ -341,7 +343,7 @@ id({item}) String get unique identity string of item
indent({lnum}) Number indent of line {lnum}
index({object}, {expr} [, {start} [, {ic}]])
Number index in {object} where {expr} appears
indexof({object}, {expr} [, {opts}]])
indexof({object}, {expr} [, {opts}])
Number index in {object} where {expr} is true
input({prompt} [, {text} [, {completion}]])
String get input from the user
@@ -355,7 +357,8 @@ inputsecret({prompt} [, {text}])
insert({object}, {item} [, {idx}])
List insert {item} in {object}
[before {idx}]
instanceof({object}, {class}) Bool |TRUE| if {object} is an instance of
instanceof({object}, {class} ...)
Bool |TRUE| if {object} is an instance of
{class}
interrupt() none interrupt script execution
invert({expr}) Number bitwise invert
@@ -412,6 +415,7 @@ mapnew({expr1}, {expr2}) List/Dict/Blob/String
like |map()| but creates a new List or
Dictionary
mapset({mode}, {abbr}, {dict}) none restore mapping from |maparg()| result
mapset({dict}) none restore mapping from |maparg()| result
match({expr}, {pat} [, {start} [, {count}]])
Number position where {pat} matches in {expr}
matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]])
@@ -419,7 +423,7 @@ matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]])
matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]])
Number highlight positions with {group}
matcharg({nr}) List arguments of `:match`
matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict})
matchbufline({buf}, {pat}, {lnum}, {end} [, {dict}])
List all the {pat} matches in buffer {buf}
matchdelete({id} [, {win}]) Number delete match identified by {id}
matchend({expr}, {pat} [, {start} [, {count}]])
@@ -433,7 +437,7 @@ matchlist({expr}, {pat} [, {start} [, {count}]])
{expr}
matchstr({expr}, {pat} [, {start} [, {count}]])
String {count}'th match of {pat} in {expr}
matchstrlist({list}, {pat} [, {dict})
matchstrlist({list}, {pat} [, {dict}])
List all the {pat} matches in {list}
matchstrpos({expr}, {pat} [, {start} [, {count}]])
List {count}'th match of {pat} in {expr}
@@ -445,7 +449,7 @@ mkdir({name} [, {flags} [, {prot}]])
mode([{expr}]) String current editing mode
mzeval({expr}) any evaluate |MzScheme| expression
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
ngettext({single}, {plural}, {number}[, {domain}])
ngettext({single}, {plural}, {number} [, {domain}])
String translate text based on {number}
nr2char({expr} [, {utf8}]) String single char with ASCII/UTF-8 value
{expr}
@@ -455,7 +459,7 @@ perleval({expr}) any evaluate |Perl| expression
popup_atcursor({what}, {options})
Number create popup window near the cursor
popup_beval({what}, {options}) Number create popup window for 'ballooneval'
popup_clear() none close all popup windows
popup_clear([{force}]) none close all popup windows
popup_close({id} [, {result}]) none close popup window {id}
popup_create({what}, {options}) Number create a popup window
popup_dialog({what}, {options}) Number create a popup window used as a dialog
@@ -482,7 +486,7 @@ popup_show({id}) Number unhide popup window {id}
pow({x}, {y}) Float {x} to the power of {y}
preinserted() Number whether text is inserted after cursor
prevnonblank({lnum}) Number line nr of non-blank line <= {lnum}
printf({fmt}, {expr1}...) String format text
printf({fmt} [, {expr1} ...]) String format text
prompt_getprompt({buf}) String get prompt text
prompt_setcallback({buf}, {expr})
none set prompt callback function
@@ -577,7 +581,7 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])
List search for {pattern}
server2client({clientid}, {string})
Number send reply string
serverlist() String get a list of available servers
serverlist([{dict}]) String get a list of available servers
setbufline({buf}, {lnum}, {text})
Number set line {lnum} to {text} in buffer
{buf}
@@ -588,6 +592,8 @@ setcharpos({expr}, {list}) Number set the {expr} position to {list}
setcharsearch({dict}) none set character search from {dict}
setcmdline({str} [, {pos}]) Number set command-line
setcmdpos({pos}) Number set cursor position in command-line
setcursorcharpos({lnum}, {col} [, {off}])
Number move cursor to {lnum}, {col}, {off}
setcursorcharpos({list}) Number move cursor to position in {list}
setenv({name}, {val}) none set environment variable
setfperm({fname}, {mode}) Number set {fname} file permissions to {mode}
@@ -646,7 +652,7 @@ sound_playfile({path} [, {callback}])
Number play sound file {path}
sound_stop({id}) none stop playing sound {id}
soundfold({word}) String sound-fold {word}
spellbadword() List badly spelled word at cursor
spellbadword([{sentence}]) List badly spelled word at cursor
spellsuggest({word} [, {max} [, {capital}]])
List spelling suggestions
split({expr} [, {pat} [, {keepempty}]])
@@ -731,7 +737,7 @@ term_getscrolled({buf}) Number get the scroll count of a terminal
term_getsize({buf}) List get the size of a terminal
term_getstatus({buf}) String get the status of a terminal
term_gettitle({buf}) String get the title of a terminal
term_gettty({buf}, [{input}]) String get the tty name of a terminal
term_gettty({buf} [, {input}]) String get the tty name of a terminal
term_list() List get the list of terminal buffers
term_scrape({buf}, {row}) List get row of a terminal screen
term_sendkeys({buf}, {keys}) none send keystrokes to a terminal
@@ -801,7 +807,7 @@ uri_encode({string}) String URI-encode a string
utf16idx({string}, {idx} [, {countcc} [, {charidx}]])
Number UTF-16 index of byte {idx} in {string}
values({dict}) List values in {dict}
virtcol({expr} [, {list} [, {winid}])
virtcol({expr} [, {list} [, {winid}]])
Number/List
screen column of cursor or mark
virtcol2col({winid}, {lnum}, {col})
@@ -817,8 +823,10 @@ win_gettype([{nr}]) String type of window {nr}
win_gotoid({expr}) Number go to window with ID {expr}
win_id2tabwin({expr}) List get tab and window nr from window ID
win_id2win({expr}) Number get window nr from window ID
win_move_separator({nr}) Number move window vertical separator
win_move_statusline({nr}) Number move window status line
win_move_separator({nr}, {offset})
Number move window vertical separator
win_move_statusline({nr}, {offset})
Number move window status line
win_screenpos({nr}) List get screen position of window {nr}
win_splitmove({nr}, {target} [, {options}])
Number move window {nr} to split of {target}
@@ -4725,6 +4733,190 @@ getimstatus() *getimstatus()*
Return type: |Number|
getinfo({kind}, {name} [, {opts}]) *getinfo()*
Return a Dict with the definition of {name}. {kind} tells
what {name} is looked up as: "function", "command", "option"
or "vimvar". Another {kind} is an error. The result is an
empty Dict when {name} is not found.
Every Dict has an "available" item, which is |FALSE| for a
function or an option that this Vim was built without and for
an Ex command that it does not implement. The other items
depend on {kind}, and so do the items of the optional Dict
{opts}; an item of {opts} for another {kind} is an error.
"function" a built-in function, or a user defined
function when there is no built-in one with
that name. "s:Func" finds a script-local
function in that script, its "<SNR>12_Func"
name from anywhere.
For a built-in function:
name the name of the function
kind "builtin"
minargs the number of arguments that are required
maxargs the maximum number of arguments, -1 when there
is no maximum
method the number of the argument that the base of a
|method| call fills in, zero when the function
cannot be used as a method
args a List with a Dict for each argument:
name the name of the argument as in the
help, missing when the help does not
name it
types the types the argument accepts, a List
of |vim9-types|; "any" for an argument
that is not checked or is checked
against another argument
For a function without a maximum the last item
is for all the remaining arguments.
returns the type of the returned value when the
arguments are of type "any", "void" when there
is none. Also "any" when the type depends on
the number of arguments.
Example: >
echo getinfo('function', 'strlen')
< {'name': 'strlen', 'kind': 'builtin',
'available': v:true, 'minargs': 1, 'maxargs': 1,
'method': 1,
'args': [{'name': 'string',
'types': ['string', 'number']}],
'returns': 'number'}
With the "argtypes" item of {opts}, a List of
|vim9-types| for the arguments, "returns" is the
type for arguments of those types, the same as what
a |:def| function is compiled with. Required
arguments that are not in the List are of type
"any". It is an error to give more types than the
maximum number of arguments or a type that is not
recognized. The types are not checked against
"args". Example: >
let opts = {'argtypes': ['list<number>']}
echo getinfo('function', 'sort', opts).returns
< list<number>
For a user defined function:
name the name of the function, "<SNR>12_Func" for a
script-local one
kind "function" for |:function|, "def" for |:def|
args a List with a Dict for each argument:
name the name of the argument
type the type for "def", "any" for
"function"
default the expression for the default value,
missing when there is none
varargs only when the function accepts more arguments:
a Dict with "name", empty for "...", and
"type", "list<any>" for "function"
returns the return type for "def", "any" for
"function"
abort |TRUE| when the function aborts on an error,
see |:func-abort|
range |TRUE| when the function takes a range, see
|:func-range|
dict |TRUE| for a |Dictionary-function|
closure |TRUE| for a closure, see |:func-closure|
sid the script ID where the function was defined
lnum the line number in that script
Example: >
def g:Twice(n: number): number
return n * 2
enddef
echo getinfo('function', 'g:Twice')
< {'name': 'Twice', 'kind': 'def', 'available': v:true,
'args': [{'name': 'n', 'type': 'number'}],
'returns': 'number', 'abort': v:false,
'range': v:false, 'dict': v:false,
'closure': v:false, 'sid': 1, 'lnum': 1}
"command" an Ex command, also with an abbreviation; a
user defined command is found first, as with
`exists()`. The Dict is empty for an
ambiguous abbreviation.
name the full name of the command
kind "builtin", "modifier" for a command modifier
like `:silent`, or "user" for a user defined
command
nargs the arguments as for |:command-nargs|: "0",
"1", "_", "*", "?" or "+"
range |FALSE| when a range is not allowed, otherwise
the default: "." for the cursor line, "%" for
the whole buffer or a Number as with
|:command-range|
count |FALSE| when a count is not allowed, otherwise
the default count
bang |TRUE| when a "!" after the name is allowed
bar |TRUE| when "|" ends the command, see |:bar|
register
|TRUE| when a register name is accepted
addr what a range refers to, as for
|:command-addr|; a builtin command can also
have "none", "tabs_relative", "quickfix_valid"
or "unsigned"
Only for "user":
buffer |TRUE| for a buffer-local command
complete
the completion as for |:command-complete|,
empty when there is none
definition
the replacement text
sid the script ID where the command was defined
lnum the line number in that script
With the "vim9" item of {opts}, |TRUE| finds the
command as in |Vim9| script, where an abbreviation is
not accepted and some commands do not exist, |FALSE|
as in legacy Vim script. Without it the context of
the caller is used.
Example: >
echo getinfo('command', 's')
< {'name': 'substitute', 'kind': 'builtin',
'available': v:true, 'nargs': '*', 'range': '.',
'count': v:false, 'bang': v:false, 'bar': v:false,
'register': v:false, 'addr': 'lines'}
"option" an option, also one that is not supported in
this Vim, see |hidden-options|
name the full name of the option
shortname
the abbreviation, empty when there is none
type "bool", "number" or "string"
scope "global", "window", "buffer", "global-window"
or "global-buffer", see |global-local|
default the default value
Example: >
echo getinfo('option', 'tw')
< {'name': 'textwidth', 'shortname': 'tw',
'available': v:true, 'type': 'number',
'scope': 'buffer', 'default': 0}
"vimvar" a predefined Vim variable, see |v:var|.
{name} includes the "v:".
name the name of the variable, with the "v:"
type the type of the variable, see |vim9-types|
readonly
|TRUE| when the variable cannot be set
compat |TRUE| when the variable can also be used
without the "v:", like "count" for v:count
Example: >
echo getinfo('vimvar', 'v:count')
< {'name': 'v:count', 'available': v:true,
'type': 'number', 'readonly': v:true,
'compat': v:true}
Can also be used as a |method|: >
GetName()->getinfo('function')
<
Return type: dict<any>
getjumplist([{winnr} [, {tabnr}]]) *getjumplist()*
Returns the |jumplist| for the specified window.
@@ -6370,7 +6562,7 @@ insert({object}, {item} [, {idx}]) *insert()*
Return type: |Number|
instanceof({object}, {class}) *instanceof()* *E616* *E693*
instanceof({object}, {class} ...) *instanceof()* *E616* *E693*
The result is a Number, which is |TRUE| when the {object}
argument is a direct or indirect instance of a |Class|,
|Interface|, or class |:type| alias specified by {class}.
@@ -7581,7 +7773,7 @@ matcharg({nr}) *matcharg()*
Return type: list<string>
matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}]) *matchbufline()*
matchbufline({buf}, {pat}, {lnum}, {end} [, {dict}]) *matchbufline()*
Returns the |List| of matches in lines from {lnum} to {end} in
buffer {buf} where {pat} matches.
@@ -7633,7 +7825,7 @@ matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}]) *matchbufline()*
Return type: list<dict<any>> or list<any>
matchdelete({id} [, {win}) *matchdelete()* *E802* *E803*
matchdelete({id} [, {win}]) *matchdelete()* *E802* *E803*
Deletes a match with ID {id} previously defined by |matchadd()|
or one of the |:match| commands. Returns 0 if successful,
otherwise -1. See example for |matchadd()|. All matches can
@@ -8129,7 +8321,7 @@ nextnonblank({lnum}) *nextnonblank()*
Return type: |Number|
ngettext({single}, {plural}, {number}[, {domain}) *ngettext()*
ngettext({single}, {plural}, {number} [, {domain}]) *ngettext()*
Return a string that contains the correct value for a
message based on the rules for plural form(s) in
a language. Examples: >
@@ -8270,7 +8462,7 @@ prevnonblank({lnum}) *prevnonblank()*
Return type: |Number|
printf({fmt}, {expr1} ...) *printf()*
printf({fmt} [, {expr1} ...]) *printf()*
Return a String with {fmt}, where "%" items are replaced by
the formatted form of their respective arguments. Example: >
printf("%4d: E%d %.30s", lnum, errno, msg)
@@ -9244,7 +9436,7 @@ remote_peek({serverid} [, {retvar}]) *remote_peek()*
Return type: |Number|
remote_read({serverid}, [{timeout}]) *remote_read()*
remote_read({serverid} [, {timeout}]) *remote_read()*
Return the oldest available reply from {serverid} and consume
it. Unless a {timeout} in seconds is given, it blocks until a
reply is available. Returns an empty string, if a reply is
+1
View File
@@ -8181,6 +8181,7 @@ getfsize() builtin.txt /*getfsize()*
getftime() builtin.txt /*getftime()*
getftype() builtin.txt /*getftype()*
getimstatus() builtin.txt /*getimstatus()*
getinfo() builtin.txt /*getinfo()*
getjumplist() builtin.txt /*getjumplist()*
getlatestvimscripts-install pi_getscript.txt /*getlatestvimscripts-install*
getline() builtin.txt /*getline()*
+3 -1
View File
@@ -1,4 +1,4 @@
*usr_41.txt* For Vim version 9.2. Last change: 2026 Aug 25
*usr_41.txt* For Vim version 9.2. Last change: 2026 Sep 30
VIM USER MANUAL by Bram Moolenaar
@@ -1461,6 +1461,8 @@ Various: *various-functions*
visualmode() last visual mode used
exists() check if a variable, function, etc. exists
exists_compiled() like exists() but check at compile time
getinfo() get the definition of a function, command,
option or Vim variable
has() check if a feature is supported in Vim
changenr() return number of most recent change
cscope_connection() check if a cscope connection exists
+1
View File
@@ -52731,6 +52731,7 @@ Functions: ~
|ch_listen()| listen on {address}
|getbgcolor()| return background color as RGB value
|getinfo()| get definition of argument
|tabpanel_getinfo()| get current state of the |tabpanel|
|tabpanel_scroll()| scroll the |tabpanel|
+4 -4
View File
@@ -2,7 +2,7 @@
" Language: Vim script
" Maintainer: Hirohito Higashi <h.east.727 ATMARK gmail.com>
" Doug Kearns <dougkearns@gmail.com>
" Last Change: 2026 Sep 20
" Last Change: 2026 Sep 30
" Former Maintainer: Charles E. Campbell
" DO NOT CHANGE DIRECTLY.
@@ -195,9 +195,9 @@ syn case match
" GEN_SYN_VIM: vimFuncName, START_STR='syn keyword vimFuncName contained', END_STR=''
syn keyword vimFuncName contained abs acos add and append appendbufline argc argidx arglistid argv asin assert_beeps assert_equal assert_equalfile assert_exception assert_fails assert_false assert_inrange assert_match assert_nobeep assert_notequal assert_notmatch assert_report assert_true atan atan2 autocmd_add autocmd_delete autocmd_get balloon_gettext balloon_show balloon_split base64_decode base64_encode bindtextdomain blob2list blob2str browse browsedir bufadd bufexists buflisted bufload bufloaded bufname bufnr bufwinid bufwinnr byte2line byteidx byteidxcomp call ceil ch_canread ch_close ch_close_in ch_evalexpr ch_evalraw ch_getbufnr ch_getjob ch_info ch_listen ch_log ch_logfile ch_open ch_read ch_readblob ch_readraw ch_sendexpr ch_sendraw ch_setoptions ch_status
syn keyword vimFuncName contained changenr char2nr charclass charcol charidx chdir cindent clearmatches cmdcomplete_info col complete complete_add complete_check complete_info confirm copy cos cosh count cscope_connection cursor debugbreak deepcopy delete deletebufline did_filetype diff diff_filler diff_hlID digraph_get digraph_getlist digraph_set digraph_setlist echoraw empty environ err_teapot escape eval eventhandler executable execute exepath exists exists_compiled exp expand expandcmd extend extendnew feedkeys filecopy filereadable filewritable filter finddir findfile flatten flattennew float2nr floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext foldtextresult foreach foreground fullcommand funcref function garbagecollect get getbgcolor
syn keyword vimFuncName contained getbufinfo getbufline getbufoneline getbufvar getcellpixels getcellwidths getchangelist getchar getcharmod getcharpos getcharsearch getcharstr getcmdcomplpat getcmdcompltype getcmdline getcmdpos getcmdprompt getcmdscreenpos getcmdtype getcmdwintype getcompletion getcompletiontype getcurpos getcursorcharpos getcwd getenv getfontname getfperm getfsize getftime getftype getimstatus getjumplist getline getloclist getmarklist getmatches getmousepos getmouseshape getpid getpos getqflist getreg getreginfo getregion getregionpos getregtype getscriptinfo getstacktrace gettabinfo gettabvar gettabwinvar gettagstack gettext getwininfo getwinpos getwinposx getwinposy getwinvar glob glob2regpat globpath has has_key haslocaldir hasmapto histadd
syn keyword vimFuncName contained histdel histget histnr hlID hlexists hlget hlset hostname iconv id indent index indexof input inputdialog inputlist inputrestore inputsave inputsecret insert instanceof interrupt invert isabsolutepath isdirectory isinf islocked isnan items job_getchannel job_info job_setoptions job_start job_status job_stop join js_decode js_encode json_decode json_encode keys keytrans len libcall libcallnr line line2byte lispindent list2blob list2str list2tuple listener_add listener_flush listener_remove localtime log log10 luaeval map maparg mapcheck maplist mapnew mapset match matchadd matchaddpos matcharg matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist matchstr matchstrlist matchstrpos max menu_info min mkdir mode mzeval
syn keyword vimFuncName contained nextnonblank ngettext nr2char or pathshorten perleval popup_atcursor popup_beval popup_clear popup_close popup_create popup_dialog popup_filter_menu popup_filter_yesno popup_findecho popup_findinfo popup_findpreview popup_getoptions popup_getpos popup_hide popup_list popup_locate popup_menu popup_move popup_notification popup_setbuf popup_setoptions popup_settext popup_show pow preinserted prevnonblank printf prompt_getprompt prompt_setcallback prompt_setinterrupt prompt_setprompt prop_add prop_add_list prop_clear prop_find prop_list prop_remove prop_type_add prop_type_change prop_type_delete prop_type_get prop_type_list pum_getpos pumvisible py3eval pyeval pyxeval rand range readblob readdir readdirex readfile redraw_listener_add
syn keyword vimFuncName contained getbufinfo getbufline getbufoneline getbufvar getcellpixels getcellwidths getchangelist getchar getcharmod getcharpos getcharsearch getcharstr getcmdcomplpat getcmdcompltype getcmdline getcmdpos getcmdprompt getcmdscreenpos getcmdtype getcmdwintype getcompletion getcompletiontype getcurpos getcursorcharpos getcwd getenv getfontname getfperm getfsize getftime getftype getimstatus getinfo getjumplist getline getloclist getmarklist getmatches getmousepos getmouseshape getpid getpos getqflist getreg getreginfo getregion getregionpos getregtype getscriptinfo getstacktrace gettabinfo gettabvar gettabwinvar gettagstack gettext getwininfo getwinpos getwinposx getwinposy getwinvar glob glob2regpat globpath has has_key haslocaldir hasmapto
syn keyword vimFuncName contained histadd histdel histget histnr hlID hlexists hlget hlset hostname iconv id indent index indexof input inputdialog inputlist inputrestore inputsave inputsecret insert instanceof interrupt invert isabsolutepath isdirectory isinf islocked isnan items job_getchannel job_info job_setoptions job_start job_status job_stop join js_decode js_encode json_decode json_encode keys keytrans len libcall libcallnr line line2byte lispindent list2blob list2str list2tuple listener_add listener_flush listener_remove localtime log log10 luaeval map maparg mapcheck maplist mapnew mapset match matchadd matchaddpos matcharg matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist matchstr matchstrlist matchstrpos max menu_info min mkdir mode
syn keyword vimFuncName contained mzeval nextnonblank ngettext nr2char or pathshorten perleval popup_atcursor popup_beval popup_clear popup_close popup_create popup_dialog popup_filter_menu popup_filter_yesno popup_findecho popup_findinfo popup_findpreview popup_getoptions popup_getpos popup_hide popup_list popup_locate popup_menu popup_move popup_notification popup_setbuf popup_setoptions popup_settext popup_show pow preinserted prevnonblank printf prompt_getprompt prompt_setcallback prompt_setinterrupt prompt_setprompt prop_add prop_add_list prop_clear prop_find prop_list prop_remove prop_type_add prop_type_change prop_type_delete prop_type_get prop_type_list pum_getpos pumvisible py3eval pyeval pyxeval rand range readblob readdir readdirex readfile redraw_listener_add
syn keyword vimFuncName contained redraw_listener_remove reduce reg_executing reg_recording reltime reltimefloat reltimestr remote_expr remote_foreground remote_peek remote_read remote_send remote_startserver remove rename repeat resolve reverse round rubyeval screenattr screenchar screenchars screencol screenpos screenrow screenstring search searchcount searchdecl searchpair searchpairpos searchpos server2client serverlist setbufline setbufvar setcellwidths setcharpos setcharsearch setcmdline setcmdpos setcursorcharpos setenv setfperm setline setloclist setmatches setpos setqflist setreg settabvar settabwinvar settagstack setwinvar sha256 shellescape shiftwidth sign_define sign_getdefined sign_getplaced sign_jump sign_place sign_placelist sign_undefine sign_unplace
syn keyword vimFuncName contained sign_unplacelist simplify sin sinh slice sort sound_clear sound_playevent sound_playfile sound_stop soundfold spellbadword spellsuggest split sqrt srand state str2blob str2float str2list str2nr strcharlen strcharpart strchars strdisplaywidth strftime strgetchar stridx string strlen strpart strptime strridx strtrans strutf16len strwidth submatch substitute swapfilelist swapinfo swapname synID synIDattr synIDtrans synconcealed synstack system systemlist tabpagebuflist tabpagenr tabpagewinnr tabpanel_getinfo tabpanel_scroll tagfiles taglist tan tanh tempname term_dumpdiff term_dumpload term_dumpwrite term_getaltscreen term_getansicolors term_getattr term_getcursor term_getjob term_getline term_getscrolled term_getsize term_getstatus
syn keyword vimFuncName contained term_gettitle term_gettty term_list term_scrape term_sendkeys term_setansicolors term_setapi term_setkill term_setrestore term_setsize term_start term_wait terminalprops test_alloc_fail test_autochdir test_feedinput test_garbagecollect_now test_garbagecollect_soon test_getvalue test_gui_event test_ignore_error test_mswin_event test_null_blob test_null_channel test_null_dict test_null_function test_null_job test_null_list test_null_partial test_null_string test_null_tuple test_option_not_set test_override test_refcount test_setmouse test_settime test_srand_seed test_unknown test_void timer_info timer_pause timer_start timer_stop timer_stopall tolower toupper tr trim trunc tuple2list type typename undofile undotree uniq uri_decode
+1003
View File
File diff suppressed because it is too large. Load diff
+27
View File
@@ -2816,6 +2816,33 @@ get_vim_var_type(int idx, garray_T *type_list)
return typval2type_vimvar(&vimvars[idx].vv_tv, type_list);
}
/*
* Add what getinfo() reports for the v: variable "name", without the "v:", to
* "d". Returns FAIL when there is no such variable.
*/
int
vim_var_info(char_u *name, dict_T *d)
{
int di_flags;
int idx = find_vim_var(name, &di_flags);
garray_T type_list;
char *tofree;
if (idx < 0)
return FAIL;
vim_snprintf((char *)IObuff, IOSIZE, "v:%s", vimvars[idx].vv_name);
dict_add_string(d, "name", IObuff);
dict_add_bool(d, "available", TRUE);
ga_init2(&type_list, sizeof(type_T *), 10);
char *type = type_name(get_vim_var_type(idx, &type_list), &tofree);
dict_add_string(d, "type", (char_u *)type);
vim_free(tofree);
clear_type_list(&type_list);
dict_add_bool(d, "readonly", (vimvars[idx].vv_flags & VV_RO) != 0);
dict_add_bool(d, "compat", (vimvars[idx].vv_flags & VV_COMPAT) != 0);
return OK;
}
/*
* Set v: variable to "tv". Only accepts the same type.
* Takes over the value of "tv".
+118
View File
@@ -4284,6 +4284,124 @@ cmd_exists(char_u *name)
return (ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1));
}
/*
* Add the attributes of an Ex command to "d" in the terms of ":command":
* "argt" are the EX_ flags, "addr_type" the address type and "def" the
* default for a range or count, -1 when there is none.
*/
void
ex_command_attrs(dict_T *d, long_u argt, cmd_addr_T addr_type, long def)
{
static char *addr_names[] = {
"lines", "windows", "arguments", "loaded_buffers", "buffers", "tabs",
"tabs_relative", "quickfix_valid", "quickfix", "unsigned", "other",
"none"
};
char *nargs;
if (!(argt & EX_EXTRA))
nargs = "0";
else if (!(argt & EX_NOSPC))
nargs = (argt & EX_NEEDARG) ? "+" : "*";
else if (!(argt & EX_NEEDARG))
nargs = "?";
else
nargs = (argt & EX_ARGSPACE) ? "_" : "1";
dict_add_string(d, "nargs", (char_u *)nargs);
if (!(argt & EX_RANGE))
dict_add_bool(d, "range", FALSE);
else if (argt & EX_DFLALL)
dict_add_string(d, "range", (char_u *)"%");
else if (def >= 0 && !(argt & EX_COUNT))
dict_add_number(d, "range", def);
else
dict_add_string(d, "range", (char_u *)".");
if (!(argt & EX_COUNT))
dict_add_bool(d, "count", FALSE);
else
dict_add_number(d, "count", def < 0 ? 0 : def);
dict_add_bool(d, "bang", (argt & EX_BANG) != 0);
dict_add_bool(d, "bar", (argt & EX_TRLBAR) != 0);
dict_add_bool(d, "register", (argt & EX_REGSTR) != 0);
dict_add_string(d, "addr", (char_u *)addr_names[addr_type]);
}
/*
* Add what getinfo() reports for the Ex command "name" to "d". "vim9" is TRUE
* to find the command as in Vim9 script, FALSE as in legacy script, -1 for the
* context of the caller.
* Returns FAIL when there is no such command or the abbreviation is
* ambiguous.
*/
int
ex_command_info(char_u *name, int vim9, dict_T *d)
{
exarg_T ea;
char_u *p;
int i;
int j;
int save_cmod_flags = cmdmod.cmod_flags;
int ret = OK;
if (vim9 != -1)
{
cmdmod.cmod_flags &= ~(CMOD_VIM9CMD | CMOD_LEGACY);
cmdmod.cmod_flags |= vim9 ? CMOD_VIM9CMD : CMOD_LEGACY;
}
// An abbreviation of a command modifier, like ":bo" for ":botright".
for (i = 0; i < (int)ARRAY_LENGTH(cmdmod_info_tab); ++i)
{
for (j = 0; name[j] != NUL; ++j)
if (name[j] != cmdmod_info_tab[i].name[j])
break;
if (name[j] == NUL && j >= cmdmod_info_tab[i].minlen)
{
name = (char_u *)cmdmod_info_tab[i].name;
break;
}
}
// For ":2match" and ":3match" we need to skip the number.
ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
ea.cmdidx = (cmdidx_T)0;
ea.flags = 0;
ea.addr_count = 0;
// Don't complain about using "en" or ":let" in Vim9 script.
++emsg_silent;
p = find_ex_command(&ea, NULL, NULL, NULL);
if (p == NULL || ea.cmdidx == CMD_SIZE
|| (vim_isdigit(*name) && ea.cmdidx != CMD_match)
|| *skipwhite(p) != NUL
|| not_in_vim9(&ea) == FAIL)
ret = FAIL;
--emsg_silent;
if (ret == FAIL)
;
else if (IS_USER_CMDIDX(ea.cmdidx))
ret = user_command_info(ea.useridx, ea.cmdidx, d);
else
{
int notimpl = cmdnames[ea.cmdidx].cmd_func == ex_ni
# ifdef HAVE_EX_SCRIPT_NI
|| cmdnames[ea.cmdidx].cmd_func == ex_script_ni
# endif
;
dict_add_string(d, "name", cmdnames[ea.cmdidx].cmd_name);
dict_add_string(d, "kind", (char_u *)(
cmdnames[ea.cmdidx].cmd_func == ex_wrongmodifier
? "modifier" : "builtin"));
dict_add_bool(d, "available", !notimpl);
ex_command_attrs(d, cmdnames[ea.cmdidx].cmd_argt,
cmdnames[ea.cmdidx].cmd_addr_type, -1);
}
cmdmod.cmod_flags = save_cmod_flags;
return ret;
}
/*
* "fullcommand" function
*/
+41
View File
@@ -7471,6 +7471,47 @@ get_option_fullname(int opt_idx)
{
return (char_u *)options[opt_idx].fullname;
}
/*
* Add what getinfo() reports for the option "name" to "d". "available" tells
* whether the option is supported in this Vim.
* Returns FAIL when there is no such option.
*/
int
option_info(char_u *name, dict_T *d)
{
int opt_idx;
struct vimoption *p;
int dvi;
// The "g:" or "l:" scope does not matter for the definition.
if ((name[0] == 'g' || name[0] == 'l') && name[1] == ':')
name += 2;
opt_idx = findoption(name);
if (opt_idx < 0)
return FAIL;
p = &options[opt_idx];
dict_add_string(d, "name", (char_u *)p->fullname);
dict_add_string(d, "shortname",
(char_u *)(p->shortname == NULL ? "" : p->shortname));
dict_add_bool(d, "available", p->var != NULL);
dict_add_string(d, "type", (char_u *)((p->flags & P_BOOL) ? "bool"
: (p->flags & P_NUM) ? "number" : "string"));
dict_add_string(d, "scope", (char_u *)(p->indir == PV_NONE ? "global"
: (p->indir & PV_BOTH)
? ((p->indir & PV_WIN) ? "global-window" : "global-buffer")
: ((p->indir & PV_WIN) ? "window" : "buffer")));
dvi = (p->flags & P_VI_DEF) ? VI_DEFAULT : VIM_DEFAULT;
if (p->flags & P_BOOL)
dict_add_bool(d, "default", (int)(long_i)p->def_val[dvi]);
else if (p->flags & P_NUM)
dict_add_number(d, "default", (long)(long_i)p->def_val[dvi]);
else
dict_add_string(d, "default", p->def_val[dvi] == NULL
? (char_u *)"" : p->def_val[dvi]);
return OK;
}
#endif
/*
+1
View File
@@ -41,6 +41,7 @@ void set_vim_var_nr(int idx, varnumber_T val);
char *get_vim_var_name(int idx);
typval_T *get_vim_var_tv(int idx);
type_T *get_vim_var_type(int idx, garray_T *type_list);
int vim_var_info(char_u *name, dict_T *d);
int set_vim_var_tv(int idx, typval_T *tv);
varnumber_T get_vim_var_nr(int idx);
char_u *get_vim_var_str(int idx);
+2
View File
@@ -22,6 +22,8 @@ int number_method(char_u *cmd);
char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, int cmd, cctx_T *), cctx_T *cctx);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
void ex_command_attrs(dict_T *d, long_u argt, cmd_addr_T addr_type, long def);
int ex_command_info(char_u *name, int vim9, dict_T *d);
void f_fullcommand(typval_T *argvars, typval_T *rettv);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
long excmd_get_argt(cmdidx_T idx);
+1
View File
@@ -123,6 +123,7 @@ void unset_global_local_option(char_u *name, void *from);
char_u *get_option_varp_scope(int opt_idx, int scope);
char_u *get_option_var(int opt_idx);
char_u *get_option_fullname(int opt_idx);
int option_info(char_u *name, dict_T *d);
opt_did_set_cb_T get_option_did_set_cb(int opt_idx);
char_u *get_equalprg(void);
char_u *get_findfunc(void);
+1
View File
@@ -5,6 +5,7 @@ char_u *set_context_in_user_cmdarg(char_u *cmd, char_u *arg, long argt, int cont
char_u *expand_user_command_name(int idx);
char_u *get_user_commands(expand_T *xp, int idx);
char_u *get_user_command_name(int idx, int cmdidx);
int user_command_info(int idx, int cmdidx, dict_T *d);
char_u *get_user_cmd_addr_type(expand_T *xp, int idx);
char_u *get_user_cmd_flags(expand_T *xp, int idx);
char_u *get_user_cmd_nargs(expand_T *xp, int idx);
+1
View File
@@ -56,6 +56,7 @@ int eval_fname_script(char_u *p);
int translated_function_exists(char_u *name, int is_global);
int has_varargs(ufunc_T *ufunc);
int function_exists(char_u *name, int no_deref);
int user_func_info(char_u *name, dict_T *d);
char_u *get_expanded_name(char_u *name, int check);
char_u *get_user_func_name(expand_T *xp, int idx);
ufunc_T *copy_function(ufunc_T *fp, int extra_namelen);
+222 -1
View File
@@ -5,7 +5,8 @@
" - *function-list* in runtime/doc/usr_41.txt
"
" contain the same functions and that the global_functions and
" ":help functions" lists are in ASCII order.
" ":help functions" lists are in ASCII order. Also that the arguments in
" global_functions[] are the ones in the help.
func Test_function_lists()
@@ -104,4 +105,224 @@ func Test_function_lists()
endfunc
" Parse the arguments of a help signature, "{lnum}, {col} [, {off}]", into a
" Dict: "names" (a value such as the 1 of getreg() and a List literal have
" ''), "min" (the required arguments), "max" (-1 for "..."), "rest" (TRUE
" for "[...]", the shorthand of the overview for more optional arguments) and
" "bad" (TRUE for unbalanced brackets or a stray parenthesis).
func s:ParseHelpArgs(args)
let r = #{names: [], min: 0, max: 0, rest: 0, bad: 0}
let optional = 0
let depth = 0
let s = a:args
while 1
let comma = s =~ '^\s*,'
let s = substitute(s, '^[ ,]*', '', '')
if s == ''
break
endif
if s =~ '^\[\.\.\.\]'
let r.rest = 1
let s = s[5:]
continue
endif
if s[0] == '['
if comma && !empty(r.names)
" A List literal after a comma is an argument without a name.
let nested = 0
let i = 0
while i < len(s)
let nested += s[i] == '[' ? 1 : s[i] == ']' ? -1 : 0
let i += 1
if nested == 0
break
endif
endwhile
let s = s[i:]
else
let optional = 1
let depth += 1
let s = s[1:]
continue
endif
elseif s[0] == ']'
let depth -= 1
let s = s[1:]
continue
elseif s =~ '^\.\.\.'
let r.max = -1
let s = s[3:]
continue
elseif s =~ '^{'
call add(r.names, substitute(matchstr(s, '{\zs[^}]*'), '-', '_', 'g'))
let s = substitute(s, '^{[^}]*}', '', '')
elseif s =~ '^[()]'
let r.bad = 1
let s = s[1:]
continue
else
call add(r.names, '')
let s = substitute(s, '^[^], ]*', '', '')
endif
if !optional
let r.min += 1
endif
if r.max >= 0
let r.max += 1
endif
endwhile
if depth != 0
let r.bad = 1
endif
" Two arguments with the same name are numbered: {expr}, {expr} is expr1
" and expr2.
for name in copy(r.names)
if name != '' && count(r.names, name) > 1
let n = 1
for i in range(len(r.names))
if r.names[i] == name
let r.names[i] = name .. n
let n += 1
endif
endfor
endif
endfor
return r
endfunc
" Combine the forms of one function, a List of what s:ParseHelpArgs()
" returned, into what the table must have: the names of the form with the
" most arguments, the least required arguments, the most arguments.
func s:CombineForms(forms)
let r = #{names: [], min: a:forms[0].min, max: 0, rest: 0, bad: 0}
for f in a:forms
if len(f.names) > len(r.names)
let r.names = f.names
endif
let r.min = min([r.min, f.min])
let r.max = f.max < 0 || r.max < 0 ? -1 : max([r.max, f.max])
let r.rest = r.rest || f.rest
let r.bad = r.bad || f.bad
endfor
return r
endfunc
" Compare the help forms "forms" of function "name" with what getinfo()
" reports in "info". "where" names the help in the messages. The overview
" abbreviates the names, they are only checked in the entry.
func s:CheckForms(name, forms, info, where)
let h = s:CombineForms(a:forms)
call assert_false(h.bad, a:name .. '() brackets in ' .. a:where)
if a:where != 'the overview'
let names = map(copy(a:info.args), {_, arg -> get(arg, 'name', '')})
call assert_equal(h.names, slice(names, 0, len(h.names)),
\ a:name .. '() argument names in ' .. a:where)
endif
call assert_equal(h.min, a:info.minargs,
\ a:name .. '() required arguments in ' .. a:where)
if h.rest
return
endif
if h.max < 0
call assert_true(a:info.maxargs < 0 || a:info.maxargs > len(h.names),
\ a:name .. '() takes more arguments in ' .. a:where)
else
call assert_equal(h.max, a:info.maxargs,
\ a:name .. '() maximum number of arguments in ' .. a:where)
endif
endfunc
" The lines of help file "fname".
func s:HelpLines(fname)
" "../../doc" is the unpacked MS-Windows zip archive.
let dir = filereadable('../../doc/tags') ? '../../doc/' : '../../runtime/doc/'
return readfile(dir .. a:fname)
\ ->map({_, line -> substitute(line, "\r$", '', '')})
endfunc
" Test that the arguments of global_functions[], as getinfo() reports
" them, are the ones in the help: how many are required and how many there
" are, in the overview and in the entry of the function, and the names in
" the entry.
func Test_function_arguments()
" The forms in the overview: the lines that start in the first column, up
" to the end of the section.
let overview = {}
let lines = s:HelpLines('builtin.txt')
let start = match(lines, '^USAGE')
let end = match(lines, '^==========', start)
for line in lines[start : end]
let m = matchlist(line, '^\(\l\w*\)(\(.*\)')
if !empty(m)
" The arguments end at the parenthesis that closes the first one; it
" must be followed by white space or the end of the line.
let args = matchstr(m[2], '^\%([^()]\|([^()]*)\)*')
if m[2][len(args) :] !~ '^)\%(\s\|$\)'
let args ..= ')'
endif
if !has_key(overview, m[1])
let overview[m[1]] = []
endif
call add(overview[m[1]], s:ParseHelpArgs(args))
endif
endfor
" The forms in the entry of each function: the line with the "*name()*"
" tag, or next to it, and every later line starting with "name(". The tags
" file gives the help file of the entry.
let files = {}
for line in s:HelpLines('tags')
let m = matchlist(line, '^\(\l\w*\)()\t\(\S*\)')
if !empty(m)
let files[m[1]] = m[2]
endif
endfor
let details = {}
for fname in uniq(sort(values(files)))
let lines = s:HelpLines(fname)
let i = 0
while i < len(lines)
let name = matchstr(lines[i], '^\l\w*\ze(')
if name == '' || get(files, name, '') != fname
let i += 1
continue
endif
let sig = lines[i]
let j = i
while count(sig, '(') > count(sig, ')') && j + 1 < len(lines)
let j += 1
let sig ..= ' ' .. trim(lines[j])
endwhile
let tag = '*' .. name .. '()*'
let tagged = has_key(details, name)
\ || stridx(sig, tag) >= 0
\ || (i > 0 && stridx(lines[i - 1], tag) >= 0)
\ || (j + 1 < len(lines) && stridx(lines[j + 1], tag) >= 0)
" After the arguments only tags may follow, not text.
let pat = '^\l\w*(\zs\%([^()]\|([^()]*)\)*\ze)\s*\%(\*\|$\)'
if tagged && sig =~ pat
if !has_key(details, name)
let details[name] = []
endif
call add(details[name], s:ParseHelpArgs(matchstr(sig, pat)))
endif
let i = j + 1
endwhile
endfor
for name in sort(keys(overview))
let info = getinfo('function', name)
if empty(info)
call assert_report(name .. '() is not a builtin function')
continue
endif
call s:CheckForms(name, overview[name], info, 'the overview')
if !has_key(details, name)
call assert_report(name .. '() has no help entry')
continue
endif
call s:CheckForms(name, details[name], info, files[name])
endfor
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+252
View File
@@ -2322,6 +2322,258 @@ func Test_hostname()
endif
endfunc
func Test_getinfo()
call assert_equal({'name': 'strlen', 'kind': 'builtin', 'available': v:true,
\ 'minargs': 1, 'maxargs': 1, 'method': 1,
\ 'args': [{'name': 'string', 'types': ['string', 'number']}],
\ 'returns': 'number'},
\ getinfo('function', 'strlen'))
" No arguments, no argument checks and not usable as a method.
call assert_equal({'name': 'argidx', 'kind': 'builtin', 'available': v:true,
\ 'minargs': 0, 'maxargs': 0, 'method': 0, 'args': [],
\ 'returns': 'number'},
\ getinfo('function', 'argidx'))
" An argument that accepts several types, and one that is checked against
" another argument.
let info = getinfo('function', 'get')
call assert_equal([2, 3, 1], [info.minargs, info.maxargs, info.method])
call assert_equal(['blob', 'list<any>', 'tuple<any>', 'dict<any>', 'func'],
\ info.args[0].types)
call assert_equal(['string', 'number'], info.args[1].types)
call assert_equal(['any'], info.args[2].types)
call assert_equal('any', info.returns)
call assert_equal(['any'], getinfo('function', 'extend').args[1].types)
" The base of a method call is the second argument.
call assert_equal(2, getinfo('function', 'append').method)
" No maximum number of arguments: the last item is for the rest.
let info = getinfo('function', 'instanceof')
call assert_equal(-1, info.maxargs)
call assert_equal([{'name': 'object', 'types': ['object<any>']},
\ {'name': 'class', 'types': ['class']}], info.args)
" The names of the arguments as in the help. An argument the help does not
" name has none, also the ones after the first value of printf().
call assert_equal(['lnum', 'col', 'off'],
\ getinfo('function', 'cursor').args->map('v:val.name'))
call assert_equal(['expr1', 'expr2'],
\ getinfo('function', 'and').args->map('v:val.name'))
let info = getinfo('function', 'getreg')
call assert_equal(['regname', 'list'], [info.args[0].name, info.args[2].name])
call assert_false(has_key(info.args[1], 'name'))
let info = getinfo('function', 'printf')
call assert_equal(['fmt', 'expr1'], [info.args[0].name, info.args[1].name])
call assert_false(has_key(info.args[2], 'name'))
call assert_false(has_key(info.args[18], 'name'))
" The type depends on the number of arguments, and no value at all.
call assert_equal('any', getinfo('function', 'getline').returns)
call assert_equal('void', getinfo('function', 'bufload').returns)
" The type returned for arguments of the given types, or that number of
" arguments. Missing required arguments are "any".
let Types = {types ->
\ getinfo('function', 'sort', {'argtypes': types}).returns}
call assert_equal('list<number>', Types(['list<number>']))
call assert_equal('any', Types([]))
let Types = {f, types -> getinfo('function', f, {'argtypes': types}).returns}
call assert_equal('list<string>', Types('values', ['dict<string>']))
call assert_equal('number', Types('remove', ['list<number>', 'number']))
call assert_equal('list<number>',
\ Types('remove', ['list<number>', 'number', 'number']))
call assert_equal('string', Types('getline', ['number']))
call assert_equal('list<string>', Types('getline', ['number', 'string']))
call assert_equal('number', Types('get', ['blob']))
call assert_equal('number', Types('strlen', ['string']))
call assert_equal(getinfo('function', 'strlen'),
\ getinfo('function', 'strlen', {'argtypes': ['string']}))
call assert_equal(getinfo('function', 'strlen'),
\ getinfo('function', 'strlen', {}))
call assert_equal({},
\ getinfo('function', 'nosuchfunction', {'argtypes': ['x']}))
call assert_fails("call Types('strlen', ['string', 'string'])", 'E118:')
call assert_fails("call Types('strlen', ['nosuchtype'])", 'E1010:')
call assert_fails("call Types('strlen', ['string x'])", 'E1010:')
call assert_fails("call getinfo('function', 'strlen', {'argtypes': 'x'})",
\ 'E475: Invalid argument: argtypes')
call assert_fails("call getinfo('function', 'strlen', {'vim9': 1})",
\ 'E475: Invalid argument: vim9')
call assert_fails("call getinfo('function', 'strlen', {'other': 1})",
\ 'E475: Invalid argument: other')
call assert_fails("call getinfo('vimvar', 'v:count', {'argtypes': []})",
\ 'E475: Invalid argument: argtypes')
call assert_fails("call getinfo('function', 'strlen', 'string')", 'E1206:')
" An unknown kind, and a name that is not a function.
call assert_fails("call getinfo('variable', 'v:count')",
\ 'E475: Invalid argument: variable')
call assert_fails("call getinfo('', 'strlen')", 'E475: Invalid argument:')
call assert_fails("call getinfo('function')", 'E119:')
call assert_equal({}, getinfo('function', 'nosuchfunction'))
call assert_equal({}, getinfo('function', ''))
" A user defined function, a script-local one also by its <SNR> name.
func s:Legacy(a, b = 2, ...) abort range dict
endfunc
let info = getinfo('function', 's:Legacy')
let sid = str2nr(matchstr(expand('<SID>'), '\d\+'))
call assert_equal({'name': printf('<SNR>%d_Legacy', sid), 'kind': 'function',
\ 'available': v:true,
\ 'args': [{'name': 'a', 'type': 'any'},
\ {'name': 'b', 'type': 'any', 'default': '2'}],
\ 'varargs': {'name': '', 'type': 'list<any>'}, 'returns': 'any',
\ 'abort': v:true, 'range': v:true, 'dict': v:true, 'closure': v:false,
\ 'sid': sid, 'lnum': info.lnum}, info)
call assert_true(info.lnum > 0)
call assert_equal(info,
\ getinfo('function', printf('<SNR>%d_Legacy', sid)))
def g:Def9(x: number, y: string = 'a', ...rest: list<string>): string
return ''
enddef
let info = getinfo('function', 'g:Def9')
call assert_equal(['Def9', 'def', 'string'],
\ [info.name, info.kind, info.returns])
call assert_equal([{'name': 'x', 'type': 'number'},
\ {'name': 'y', 'type': 'string', 'default': "'a'"}], info.args)
call assert_equal({'name': 'rest', 'type': 'list<string>'}, info.varargs)
call assert_equal([v:false, v:false], [info.abort, info.range])
call assert_false(has_key(getinfo('function', 'Test_getinfo'), 'varargs'))
call assert_equal('any', getinfo('function', 'Test_getinfo').returns)
" A builtin function with the same name is found first.
call assert_equal('builtin', getinfo('function', 'strlen').kind)
call assert_equal({}, getinfo('function', 'g:Def9 x'))
delfunc s:Legacy
delfunc g:Def9
" A builtin function that is not implemented in this Vim.
let info = getinfo('function', 'mzeval')
call assert_equal('mzeval', info.name)
call assert_equal(exists('*mzeval') ? v:true : v:false, info.available)
" A predefined Vim variable: its declared type and how it can be used.
call assert_equal({'name': 'v:count', 'available': v:true, 'type': 'number',
\ 'readonly': v:true, 'compat': v:true},
\ getinfo('vimvar', 'v:count'))
let info = getinfo('vimvar', 'v:lnum')
call assert_equal([v:false, v:false], [info.readonly, info.compat])
call assert_equal('list<string>', getinfo('vimvar', 'v:errors').type)
call assert_equal('bool', getinfo('vimvar', 'v:true').type)
call assert_equal('dict<any>', getinfo('vimvar', 'v:event').type)
call assert_equal({}, getinfo('vimvar', 'v:nosuchvariable'))
call assert_equal({}, getinfo('vimvar', 'v:'))
" The "v:" is required.
call assert_equal({}, getinfo('vimvar', 'count'))
" An Ex command, also by an abbreviation, with the attributes of :command.
call assert_equal({'name': 'substitute', 'kind': 'builtin',
\ 'available': v:true, 'nargs': '*', 'range': '.', 'count': v:false,
\ 'bang': v:false, 'bar': v:false, 'register': v:false,
\ 'addr': 'lines'}, getinfo('command', 's'))
let info = getinfo('command', 'write')
call assert_equal(['%', '?', v:true, v:true],
\ [info.range, info.nargs, info.bang, info.bar])
let info = getinfo('command', 'delete')
call assert_equal([0, v:true, 'none'],
\ [info.count, info.register, getinfo('command', 'echo').addr])
let info = getinfo('command', 'bo')
call assert_equal(['botright', 'modifier'], [info.name, info.kind])
let info = getinfo('command', '2match')
call assert_equal(['match', 'other'], [info.name, info.addr])
call assert_equal({}, getinfo('command', '3buffer'))
" A command that is not implemented in this Vim.
call assert_equal(has('printer') ? v:true : v:false,
\ getinfo('command', 'hardcopy').available)
" The context: an abbreviation is not accepted in Vim9 script and some
" commands do not exist there.
call assert_equal('while', getinfo('command', 'whi').name)
call assert_equal('while', getinfo('command', 'whi', {'vim9': v:false}).name)
call assert_equal({}, getinfo('command', 'whi', {'vim9': v:true}))
call assert_equal('while',
\ getinfo('command', 'while', {'vim9': v:true}).name)
call assert_equal('append', getinfo('command', 'append').name)
call assert_equal({}, getinfo('command', 'append', {'vim9': v:true}))
call assert_equal({}, getinfo('command', 'nosuchcommand'))
call assert_equal({}, getinfo('command', 's garbage'))
call assert_equal({}, getinfo('command', ''))
command! -nargs=1 -range=% -bang -bar -register -complete=file MyCmd echo 1
let info = getinfo('command', 'MyCmd')
let sid = str2nr(matchstr(expand('<SID>'), '\d\+'))
call assert_equal({'name': 'MyCmd', 'kind': 'user', 'available': v:true,
\ 'nargs': '1', 'range': '%', 'count': v:false, 'bang': v:true,
\ 'bar': v:true, 'register': v:true, 'addr': 'lines',
\ 'buffer': v:false, 'complete': 'file', 'definition': 'echo 1',
\ 'sid': sid, 'lnum': info.lnum}, info)
call assert_true(info.lnum > 0)
command! -buffer -nargs=* -count=5 -addr=buffers MyBufCmd echo 2
let info = getinfo('command', 'MyBufCmd')
call assert_equal(['*', 5, '.', 'buffers', v:true, ''],
\ [info.nargs, info.count, info.range, info.addr, info.buffer,
\ info.complete])
command! -nargs=+ -complete=custom,MyCompl MyCustom echo 3
call assert_equal('custom,MyCompl', getinfo('command', 'MyCustom').complete)
" An ambiguous abbreviation.
call assert_equal({}, getinfo('command', 'My'))
delcommand MyCmd
delcommand MyBufCmd
delcommand MyCustom
" An option, also by the short name and one that is hidden.
call assert_equal({'name': 'textwidth', 'shortname': 'tw',
\ 'available': v:true, 'type': 'number', 'scope': 'buffer',
\ 'default': 0}, getinfo('option', 'tw'))
let info = getinfo('option', 'number')
call assert_equal(['bool', 'window', v:false],
\ [info.type, info.scope, info.default])
call assert_equal('global-buffer', getinfo('option', 'autoread').scope)
call assert_equal('global-window', getinfo('option', 'scrolloff').scope)
let info = getinfo('option', 'shortmess')
call assert_equal(['string', 'global', &shortmess],
\ [info.type, info.scope, info.default])
call assert_equal('', getinfo('option', 'debug').shortname)
call assert_equal(getinfo('option', 'tw'),
\ getinfo('option', 'l:textwidth'))
let info = getinfo('option', 'autoprint')
call assert_equal(['autoprint', v:false], [info.name, info.available])
call assert_equal({}, getinfo('option', 'nonumber'))
call assert_equal({}, getinfo('option', 'tw-'))
call assert_equal({}, getinfo('option', ''))
call assert_equal('number', 'strlen'->getinfo('function').returns)
let lines =<< trim END
assert_equal('string', getinfo('function', 'printf').returns)
assert_equal(-1, getinfo('function', 'instanceof').maxargs)
assert_equal('list<string>',
getinfo('function', 'sort',
{argtypes: ['list<string>']}).returns)
assert_equal('substitute', getinfo('command', 's').name)
assert_equal({}, getinfo('command', 'whi'))
assert_equal('while', getinfo('command', 'whi', {vim9: false}).name)
assert_equal('number', getinfo('option', 'tw').type)
END
call v9.CheckDefAndScriptSuccess(lines)
" A function in Vim9 script is script-local.
let lines =<< trim END
vim9script
def Local(n: number): bool
return true
enddef
var info = getinfo('function', 'Local')
assert_equal(['def', 'bool'], [info.kind, info.returns])
assert_match('^<SNR>\d\+_Local$', info.name)
END
call v9.CheckScriptSuccess(lines)
call v9.CheckDefAndScriptFailure(['getinfo(1, "strlen")'],
\ ['E1013:', 'E1174:'])
call v9.CheckDefAndScriptFailure(['getinfo("function", 1)'],
\ ['E1013:', 'E1174:'])
call v9.CheckDefAndScriptFailure(['getinfo("function", "sort", "x")'],
\ ['E1013:', 'E1206:'])
endfunc
func Test_getpid()
" getpid() always returns the same value within a vim instance.
call assert_equal(getpid(), getpid())
+30
View File
@@ -429,6 +429,36 @@ get_user_command_name(int idx, int cmdidx)
}
return NULL;
}
/*
* Add what getinfo() reports for the user command "idx" to "d".
* "cmdidx" is CMD_USER or CMD_USER_BUF.
* Returns FAIL when the command is not found.
*/
int
user_command_info(int idx, int cmdidx, dict_T *d)
{
garray_T *gap = cmdidx == CMD_USER_BUF
? &prevwin_curwin()->w_buffer->b_ucmds : &ucmds;
ucmd_T *uc;
char_u *compl;
if (idx >= gap->ga_len)
return FAIL;
uc = USER_CMD_GA(gap, idx);
dict_add_string(d, "name", uc->uc_name);
dict_add_string(d, "kind", (char_u *)"user");
dict_add_bool(d, "available", TRUE);
ex_command_attrs(d, uc->uc_argt, uc->uc_addr_type, uc->uc_def);
dict_add_bool(d, "buffer", cmdidx == CMD_USER_BUF);
compl = cmdcomplete_type_to_str(uc->uc_compl, uc->uc_compl_arg);
dict_add_string(d, "complete", compl == NULL ? (char_u *)"" : compl);
vim_free(compl);
dict_add_string(d, "definition", uc->uc_rep);
dict_add_number(d, "sid", uc->uc_script_ctx.sc_sid);
dict_add_number(d, "lnum", uc->uc_script_ctx.sc_lnum);
return OK;
}
#endif
/*
+94
View File
@@ -6121,6 +6121,100 @@ function_exists(char_u *name, int no_deref)
return n;
}
/*
* Add what getinfo() reports for the user defined function "name" to "d".
* Returns FAIL when there is no such function.
*/
int
user_func_info(char_u *name, dict_T *d)
{
char_u *nm = name;
char_u *p;
int is_global = FALSE;
ufunc_T *fp = NULL;
list_T *args;
int is_def;
char *tofree;
if (*name == NUL)
return FAIL;
p = trans_function_name(&nm, &is_global, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF);
if (p != NULL && *skipwhite(nm) == NUL)
fp = find_func(p, is_global);
vim_free(p);
if (fp == NULL)
return FAIL;
is_def = fp->uf_def_status != UF_NOT_COMPILED;
dict_add_string(d, "name", printable_func_name(fp));
dict_add_string(d, "kind", (char_u *)(is_def ? "def" : "function"));
dict_add_bool(d, "available", TRUE);
args = list_alloc();
if (args == NULL || dict_add_list(d, "args", args) == FAIL)
{
list_unref(args);
return FAIL;
}
for (int j = 0; j < fp->uf_args.ga_len; ++j)
{
dict_T *arg = dict_alloc();
if (arg == NULL || list_append_dict(args, arg) == FAIL)
{
dict_unref(arg);
return FAIL;
}
dict_add_string(arg, "name", FUNCARG(fp, j));
if (fp->uf_arg_types == NULL)
dict_add_string(arg, "type", (char_u *)"any");
else
{
dict_add_string(arg, "type",
(char_u *)type_name(fp->uf_arg_types[j], &tofree));
vim_free(tofree);
}
if (j >= fp->uf_args.ga_len - fp->uf_def_args.ga_len)
dict_add_string(arg, "default", ((char_u **)fp->uf_def_args.ga_data)
[j - fp->uf_args.ga_len + fp->uf_def_args.ga_len]);
}
if (has_varargs(fp))
{
dict_T *va = dict_alloc();
if (va == NULL || dict_add_dict(d, "varargs", va) == FAIL)
{
dict_unref(va);
return FAIL;
}
dict_add_string(va, "name",
fp->uf_va_name == NULL ? (char_u *)"" : fp->uf_va_name);
if (fp->uf_va_type == NULL)
dict_add_string(va, "type", (char_u *)"list<any>");
else
{
dict_add_string(va, "type",
(char_u *)type_name(fp->uf_va_type, &tofree));
vim_free(tofree);
}
}
if (is_def)
{
dict_add_string(d, "returns",
(char_u *)type_name(fp->uf_ret_type, &tofree));
vim_free(tofree);
}
else
dict_add_string(d, "returns", (char_u *)"any");
dict_add_bool(d, "abort", (fp->uf_flags & FC_ABORT) != 0);
dict_add_bool(d, "range", (fp->uf_flags & FC_RANGE) != 0);
dict_add_bool(d, "dict", (fp->uf_flags & FC_DICT) != 0);
dict_add_bool(d, "closure", (fp->uf_flags & FC_CLOSURE) != 0);
dict_add_number(d, "sid", fp->uf_script_ctx.sc_sid);
dict_add_number(d, "lnum", fp->uf_script_ctx.sc_lnum);
return OK;
}
#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3)
char_u *
get_expanded_name(char_u *name, int check)
+2
View File
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1160,
/**/
1159,
/**/