feat(docs): generate builtin.txt (#24493)

- eval.lua is now the source of truth.

- Formatting is much more consistent.

- Fixed Lua type generation for polymorphic functions (get(), etc).

- Removed "Overview" section from builtin.txt
  - Can generate this if we really want it.

- Moved functions from sign.txt and testing.txt into builtin.txt.

- Removed the *timer* *timers* tags since libuv timers via vim.uv should be preferred.

- Removed the temp-file-name tag from tempname()

- Moved lueval() from lua.txt to builtin.txt.

* Fix indent

* fixup!

* fixup! fixup!

* fixup! better tag formatting

* fixup: revert changes no longer needed

* fixup! CI

---------

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
This commit is contained in:
Lewis Russell 2023-07-28 14:48:41 +01:00 committed by GitHub
parent c1c2a1b5dd
commit 42333ea98d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 15221 additions and 2466 deletions

1
.gitattributes vendored
View File

@ -3,6 +3,7 @@
*CMakeLists.txt linguist-language=CMake
runtime/doc/* linguist-documentation
runtime/doc/builtin.txt linguist-generated
runtime/lua/vim/_meta/vimfn.lua linguist-generated

View File

@ -29,7 +29,6 @@ jobs:
id: docs
run: |
python3 scripts/gen_vimdoc.py
./scripts/gen_vimfn_types.lua
printf 'UPDATED_DOCS=%s\n' $([ -z "$(git diff)" ]; echo $?) >> $GITHUB_OUTPUT
- name: FAIL, PR has not committed doc changes

View File

@ -46,4 +46,5 @@ exclude_files = {
'runtime/lua/vim/treesitter/_meta.lua',
'runtime/lua/vim/_meta/vimfn.lua',
'runtime/lua/vim/re.lua',
'src/nvim/eval.lua',
}

1674
runtime/doc/builtin.txt generated

File diff suppressed because it is too large Load Diff

View File

@ -296,7 +296,7 @@ arguments separated by " " (space) instead of "\t" (tab).
<
==============================================================================
luaeval() *lua-eval* *luaeval()*
luaeval() *lua-eval*
The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
"luaeval". "luaeval" takes an expression string and an optional argument used

View File

@ -369,390 +369,15 @@ See |sign_jump()| for the equivalent Vim script function.
==============================================================================
3. Functions *sign-functions-details*
sign_define({name} [, {dict}]) *sign_define()*
sign_define({list})
Define a new sign named {name} or modify the attributes of an
existing sign. This is similar to the |:sign-define| command.
Prefix {name} with a unique text to avoid name collisions.
There is no {group} like with placing signs.
The {name} can be a String or a Number. The optional {dict}
argument specifies the sign attributes. The following values
are supported:
icon full path to the bitmap file for the sign.
linehl highlight group used for the whole line the
sign is placed in.
numhl highlight group used for the line number where
the sign is placed.
text text that is displayed when there is no icon
or the GUI is not being used.
texthl highlight group used for the text item
culhl highlight group used for the text item when
the cursor is on the same line as the sign and
'cursorline' is enabled.
If the sign named {name} already exists, then the attributes
of the sign are updated.
The one argument {list} can be used to define a list of signs.
Each list item is a dictionary with the above items in {dict}
and a "name" item for the sign name.
Returns 0 on success and -1 on failure. When the one argument
{list} is used, then returns a List of values one for each
defined sign.
Examples: >
call sign_define("mySign", {
\ "text" : "=>",
\ "texthl" : "Error",
\ "linehl" : "Search"})
call sign_define([
\ {'name' : 'sign1',
\ 'text' : '=>'},
\ {'name' : 'sign2',
\ 'text' : '!!'}
\ ])
<
Can also be used as a |method|: >
GetSignList()->sign_define()
sign_getdefined([{name}]) *sign_getdefined()*
Get a list of defined signs and their attributes.
This is similar to the |:sign-list| command.
If the {name} is not supplied, then a list of all the defined
signs is returned. Otherwise the attribute of the specified
sign is returned.
Each list item in the returned value is a dictionary with the
following entries:
icon full path to the bitmap file of the sign
linehl highlight group used for the whole line the
sign is placed in; not present if not set.
name name of the sign
numhl highlight group used for the line number where
the sign is placed; not present if not set.
text text that is displayed when there is no icon
or the GUI is not being used.
texthl highlight group used for the text item; not
present if not set.
culhl highlight group used for the text item when
the cursor is on the same line as the sign and
'cursorline' is enabled; not present if not
set.
Returns an empty List if there are no signs and when {name} is
not found.
Examples: >
" Get a list of all the defined signs
echo sign_getdefined()
" Get the attribute of the sign named mySign
echo sign_getdefined("mySign")
<
Can also be used as a |method|: >
GetSignList()->sign_getdefined()
sign_getplaced([{buf} [, {dict}]]) *sign_getplaced()*
Return a list of signs placed in a buffer or all the buffers.
This is similar to the |:sign-place-list| command.
If the optional buffer name {buf} is specified, then only the
list of signs placed in that buffer is returned. For the use
of {buf}, see |bufname()|. The optional {dict} can contain
the following entries:
group select only signs in this group
id select sign with this identifier
lnum select signs placed in this line. For the use
of {lnum}, see |line()|.
If {group} is "*", then signs in all the groups including the
global group are returned. If {group} is not supplied or is an
empty string, then only signs in the global group are
returned. If no arguments are supplied, then signs in the
global group placed in all the buffers are returned.
See |sign-group|.
Each list item in the returned value is a dictionary with the
following entries:
bufnr number of the buffer with the sign
signs list of signs placed in {bufnr}. Each list
item is a dictionary with the below listed
entries
The dictionary for each sign contains the following entries:
group sign group. Set to '' for the global group.
id identifier of the sign
lnum line number where the sign is placed
name name of the defined sign
priority sign priority
The returned signs in a buffer are ordered by their line
number and priority.
Returns an empty list on failure or if there are no placed
signs.
Examples: >
" Get a List of signs placed in eval.c in the
" global group
echo sign_getplaced("eval.c")
" Get a List of signs in group 'g1' placed in eval.c
echo sign_getplaced("eval.c", {'group' : 'g1'})
" Get a List of signs placed at line 10 in eval.c
echo sign_getplaced("eval.c", {'lnum' : 10})
" Get sign with identifier 10 placed in a.py
echo sign_getplaced("a.py", {'id' : 10})
" Get sign with id 20 in group 'g1' placed in a.py
echo sign_getplaced("a.py", {'group' : 'g1',
\ 'id' : 20})
" Get a List of all the placed signs
echo sign_getplaced()
<
Can also be used as a |method|: >
GetBufname()->sign_getplaced()
<
*sign_jump()*
sign_jump({id}, {group}, {buf})
Open the buffer {buf} or jump to the window that contains
{buf} and position the cursor at sign {id} in group {group}.
This is similar to the |:sign-jump| command.
If {group} is an empty string, then the global group is used.
For the use of {buf}, see |bufname()|.
Returns the line number of the sign. Returns -1 if the
arguments are invalid.
Example: >
" Jump to sign 10 in the current buffer
call sign_jump(10, '', '')
<
Can also be used as a |method|: >
GetSignid()->sign_jump()
<
*sign_place()*
sign_place({id}, {group}, {name}, {buf} [, {dict}])
Place the sign defined as {name} at line {lnum} in file or
buffer {buf} and assign {id} and {group} to sign. This is
similar to the |:sign-place| command.
If the sign identifier {id} is zero, then a new identifier is
allocated. Otherwise the specified number is used. {group} is
the sign group name. To use the global sign group, use an
empty string. {group} functions as a namespace for {id}, thus
two groups can use the same IDs. Refer to |sign-identifier|
and |sign-group| for more information.
{name} refers to a defined sign.
{buf} refers to a buffer name or number. For the accepted
values, see |bufname()|.
The optional {dict} argument supports the following entries:
lnum line number in the file or buffer
{buf} where the sign is to be placed.
For the accepted values, see |line()|.
priority priority of the sign. See
|sign-priority| for more information.
If the optional {dict} is not specified, then it modifies the
placed sign {id} in group {group} to use the defined sign
{name}.
Returns the sign identifier on success and -1 on failure.
Examples: >
" Place a sign named sign1 with id 5 at line 20 in
" buffer json.c
call sign_place(5, '', 'sign1', 'json.c',
\ {'lnum' : 20})
" Updates sign 5 in buffer json.c to use sign2
call sign_place(5, '', 'sign2', 'json.c')
" Place a sign named sign3 at line 30 in
" buffer json.c with a new identifier
let id = sign_place(0, '', 'sign3', 'json.c',
\ {'lnum' : 30})
" Place a sign named sign4 with id 10 in group 'g3'
" at line 40 in buffer json.c with priority 90
call sign_place(10, 'g3', 'sign4', 'json.c',
\ {'lnum' : 40, 'priority' : 90})
<
Can also be used as a |method|: >
GetSignid()->sign_place(group, name, expr)
<
*sign_placelist()*
sign_placelist({list})
Place one or more signs. This is similar to the
|sign_place()| function. The {list} argument specifies the
List of signs to place. Each list item is a dict with the
following sign attributes:
buffer Buffer name or number. For the accepted
values, see |bufname()|.
group Sign group. {group} functions as a namespace
for {id}, thus two groups can use the same
IDs. If not specified or set to an empty
string, then the global group is used. See
|sign-group| for more information.
id Sign identifier. If not specified or zero,
then a new unique identifier is allocated.
Otherwise the specified number is used. See
|sign-identifier| for more information.
lnum Line number in the buffer where the sign is to
be placed. For the accepted values, see
|line()|.
name Name of the sign to place. See |sign_define()|
for more information.
priority Priority of the sign. When multiple signs are
placed on a line, the sign with the highest
priority is used. If not specified, the
default value of 10 is used. See
|sign-priority| for more information.
If {id} refers to an existing sign, then the existing sign is
modified to use the specified {name} and/or {priority}.
Returns a List of sign identifiers. If failed to place a
sign, the corresponding list item is set to -1.
Examples: >
" Place sign s1 with id 5 at line 20 and id 10 at line
" 30 in buffer a.c
let [n1, n2] = sign_placelist([
\ {'id' : 5,
\ 'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 20},
\ {'id' : 10,
\ 'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 30}
\ ])
" Place sign s1 in buffer a.c at line 40 and 50
" with auto-generated identifiers
let [n1, n2] = sign_placelist([
\ {'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 40},
\ {'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 50}
\ ])
<
Can also be used as a |method|: >
GetSignlist()->sign_placelist()
sign_undefine([{name}]) *sign_undefine()*
sign_undefine({list})
Deletes a previously defined sign {name}. This is similar to
the |:sign-undefine| command. If {name} is not supplied, then
deletes all the defined signs.
The one argument {list} can be used to undefine a list of
signs. Each list item is the name of a sign.
Returns 0 on success and -1 on failure. For the one argument
{list} call, returns a list of values one for each undefined
sign.
Examples: >
" Delete a sign named mySign
call sign_undefine("mySign")
" Delete signs 'sign1' and 'sign2'
call sign_undefine(["sign1", "sign2"])
" Delete all the signs
call sign_undefine()
<
Can also be used as a |method|: >
GetSignlist()->sign_undefine()
sign_unplace({group} [, {dict}]) *sign_unplace()*
Remove a previously placed sign in one or more buffers. This
is similar to the |:sign-unplace| command.
{group} is the sign group name. To use the global sign group,
use an empty string. If {group} is set to "*", then all the
groups including the global group are used.
The signs in {group} are selected based on the entries in
{dict}. The following optional entries in {dict} are
supported:
buffer buffer name or number. See |bufname()|.
id sign identifier
If {dict} is not supplied, then all the signs in {group} are
removed.
Returns 0 on success and -1 on failure.
Examples: >
" Remove sign 10 from buffer a.vim
call sign_unplace('', {'buffer' : "a.vim", 'id' : 10})
" Remove sign 20 in group 'g1' from buffer 3
call sign_unplace('g1', {'buffer' : 3, 'id' : 20})
" Remove all the signs in group 'g2' from buffer 10
call sign_unplace('g2', {'buffer' : 10})
" Remove sign 30 in group 'g3' from all the buffers
call sign_unplace('g3', {'id' : 30})
" Remove all the signs placed in buffer 5
call sign_unplace('*', {'buffer' : 5})
" Remove the signs in group 'g4' from all the buffers
call sign_unplace('g4')
" Remove sign 40 from all the buffers
call sign_unplace('*', {'id' : 40})
" Remove all the placed signs from all the buffers
call sign_unplace('*')
< Can also be used as a |method|: >
GetSigngroup()->sign_unplace()
<
sign_unplacelist({list}) *sign_unplacelist()*
Remove previously placed signs from one or more buffers. This
is similar to the |sign_unplace()| function.
The {list} argument specifies the List of signs to remove.
Each list item is a dict with the following sign attributes:
buffer buffer name or number. For the accepted
values, see |bufname()|. If not specified,
then the specified sign is removed from all
the buffers.
group sign group name. If not specified or set to an
empty string, then the global sign group is
used. If set to "*", then all the groups
including the global group are used.
id sign identifier. If not specified, then all
the signs in the specified group are removed.
Returns a List where an entry is set to 0 if the corresponding
sign was successfully removed or -1 on failure.
Example: >
" Remove sign with id 10 from buffer a.vim and sign
" with id 20 from buffer b.vim
call sign_unplacelist([
\ {'id' : 10, 'buffer' : "a.vim"},
\ {'id' : 20, 'buffer' : 'b.vim'},
\ ])
<
Can also be used as a |method|: >
GetSignlist()->sign_unplacelist()
<
See:
- |sign_define()|
- |sign_getdefined()|
- |sign_getplaced()|
- |sign_jump()|
- |sign_place()|
- |sign_placelist()|
- |sign_undefine()|
- |sign_unplace()|
- |sign_unplacelist()|
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -40,187 +40,19 @@ test_garbagecollect_now() *test_garbagecollect_now()*
==============================================================================
3. Assert functions *assert-functions-details*
assert_beeps({cmd}) *assert_beeps()*
Run {cmd} and add an error message to |v:errors| if it does
NOT produce a beep or visual bell.
Also see |assert_fails()|, |assert_nobeep()| and
|assert-return|.
Can also be used as a |method|: >
GetCmd()->assert_beeps()
<
*assert_equal()*
assert_equal({expected}, {actual} [, {msg}])
When {expected} and {actual} are not equal an error message is
added to |v:errors| and 1 is returned. Otherwise zero is
returned. |assert-return|
The error is in the form "Expected {expected} but got
{actual}". When {msg} is present it is prefixed to that.
There is no automatic conversion, the String "4" is different
from the Number 4. And the number 4 is different from the
Float 4.0. The value of 'ignorecase' is not used here, case
always matters.
Example: >
assert_equal('foo', 'bar')
< Will result in a string to be added to |v:errors|:
test.vim line 12: Expected 'foo' but got 'bar' ~
Can also be used as a |method|: >
mylist->assert_equal([1, 2, 3])
< *assert_equalfile()*
assert_equalfile({fname-one}, {fname-two})
When the files {fname-one} and {fname-two} do not contain
exactly the same text an error message is added to |v:errors|.
Also see |assert-return|.
When {fname-one} or {fname-two} does not exist the error will
mention that.
Can also be used as a |method|: >
GetLog()->assert_equalfile('expected.log')
assert_exception({error} [, {msg}]) *assert_exception()*
When v:exception does not contain the string {error} an error
message is added to |v:errors|. Also see |assert-return|.
This can be used to assert that a command throws an exception.
Using the error number, followed by a colon, avoids problems
with translations: >
try
commandthatfails
call assert_false(1, 'command should have failed')
catch
call assert_exception('E492:')
endtry
<
*assert_fails()*
assert_fails({cmd} [, {error} [, {msg} [, {lnum} [, {context}]]]])
Run {cmd} and add an error message to |v:errors| if it does
NOT produce an error or when {error} is not found in the
error message. Also see |assert-return|.
When {error} is a string it must be found literally in the
first reported error. Most often this will be the error code,
including the colon, e.g. "E123:". >
assert_fails('bad cmd', 'E987:')
<
When {error} is a |List| with one or two strings, these are
used as patterns. The first pattern is matched against the
first reported error: >
assert_fails('cmd', ['E987:.*expected bool'])
< The second pattern, if present, is matched against the last
reported error. To only match the last error use an empty
string for the first error: >
assert_fails('cmd', ['', 'E987:'])
<
If {msg} is empty then it is not used. Do this to get the
default message when passing the {lnum} argument.
When {lnum} is present and not negative, and the {error}
argument is present and matches, then this is compared with
the line number at which the error was reported. That can be
the line number in a function or in a script.
When {context} is present it is used as a pattern and matched
against the context (script name or function name) where
{lnum} is located in.
Note that beeping is not considered an error, and some failing
commands only beep. Use |assert_beeps()| for those.
Can also be used as a |method|: >
GetCmd()->assert_fails('E99:')
assert_false({actual} [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to
|v:errors|, like with |assert_equal()|.
The error is in the form "Expected False but got {actual}".
When {msg} is present it is prepended to that.
Also see |assert-return|.
A value is false when it is zero. When {actual} is not a
number the assert fails.
Can also be used as a |method|: >
GetResult()->assert_false()
assert_inrange({lower}, {upper}, {actual} [, {msg}]) *assert_inrange()*
This asserts number and |Float| values. When {actual} is lower
than {lower} or higher than {upper} an error message is added
to |v:errors|. Also see |assert-return|.
The error is in the form "Expected range {lower} - {upper},
but got {actual}". When {msg} is present it is prefixed to
that.
*assert_match()*
assert_match({pattern}, {actual} [, {msg}])
When {pattern} does not match {actual} an error message is
added to |v:errors|. Also see |assert-return|.
The error is in the form "Pattern {pattern} does not match
{actual}". When {msg} is present it is prefixed to that.
{pattern} is used as with |expr-=~|: The matching is always done
like 'magic' was set and 'cpoptions' is empty, no matter what
the actual value of 'magic' or 'cpoptions' is.
{actual} is used as a string, automatic conversion applies.
Use "^" and "$" to match with the start and end of the text.
Use both to match the whole text.
Example: >
assert_match('^f.*o$', 'foobar')
< Will result in a string to be added to |v:errors|:
test.vim line 12: Pattern '^f.*o$' does not match 'foobar' ~
Can also be used as a |method|: >
getFile()->assert_match('foo.*')
<
assert_nobeep({cmd}) *assert_nobeep()*
Run {cmd} and add an error message to |v:errors| if it
produces a beep or visual bell.
Also see |assert_beeps()|.
Can also be used as a |method|: >
GetCmd()->assert_nobeep()
<
*assert_notequal()*
assert_notequal({expected}, {actual} [, {msg}])
The opposite of `assert_equal()`: add an error message to
|v:errors| when {expected} and {actual} are equal.
Also see |assert-return|.
Can also be used as a |method|: >
mylist->assert_notequal([1, 2, 3])
< *assert_notmatch()*
assert_notmatch({pattern}, {actual} [, {msg}])
The opposite of `assert_match()`: add an error message to
|v:errors| when {pattern} matches {actual}.
Also see |assert-return|.
Can also be used as a |method|: >
getFile()->assert_notmatch('bar.*')
assert_report({msg}) *assert_report()*
Report a test failure directly, using String {msg}.
Always returns one.
Can also be used as a |method|: >
GetMessage()->assert_report()
assert_true({actual} [, {msg}]) *assert_true()*
When {actual} is not true an error message is added to
|v:errors|, like with |assert_equal()|.
Also see |assert-return|.
A value is |TRUE| when it is a non-zero number or |v:true|.
When {actual} is not a number or |v:true| the assert fails.
When {msg} is given it precedes the default message.
Can also be used as a |method|: >
GetResult()->assert_true()
<
See:
- |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()|
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -588,7 +588,7 @@ after the substitute() call.
FUNCTIONS *function-list*
There are many functions. We will mention them here, grouped by what they are
used for. You can find an alphabetical list here: |builtin-function-list|.
used for. You can find an alphabetical list here: |builtin-function-details|.
Use CTRL-] on the function name to jump to detailed help on it.
String manipulation: *string-functions*

File diff suppressed because it is too large Load Diff

274
scripts/gen_eval_files.lua Executable file
View File

@ -0,0 +1,274 @@
#!/usr/bin/env -S nvim -l
-- Generator for src/nvim/eval.lua
local funcs = require('src/nvim/eval').funcs
local LUA_KEYWORDS = {
['and'] = true,
['end'] = true,
['function'] = true,
['or'] = true,
['if'] = true,
['while'] = true,
['repeat'] = true,
}
--- @param f string
--- @param params {[1]:string,[2]:string}[]|true
local function render_fun_sig(f, params)
local param_str --- @type string
if params == true then
param_str = '...'
else
param_str = table.concat(
vim.tbl_map(
--- @param v {[1]:string,[2]:string}
function(v)
return v[1]
end,
params
),
', '
)
end
if LUA_KEYWORDS[f] then
return string.format("vim.fn['%s'] = function(%s) end", f, param_str)
else
return string.format('function vim.fn.%s(%s) end', f, param_str)
end
end
--- Uniquify names
--- Fix any names that are lua keywords
--- @param params {[1]:string,[2]:string}[]
--- @return {[1]:string,[2]:string}[]
local function process_params(params)
local seen = {} --- @type table<string,true>
local sfx = 1
for _, p in ipairs(params) do
if LUA_KEYWORDS[p[1]] then
p[1] = p[1] .. '_'
end
if seen[p[1]] then
p[1] = p[1] .. sfx
sfx = sfx + 1
else
seen[p[1]] = true
end
end
return params
end
--- @param f string
--- @param fun vim.EvalFn
--- @param write fun(line: string)
local function render_vimfn(f, fun, write)
if fun.lua == false then
return
end
local funname = fun.name or f
local params = process_params(fun.params)
if fun.signature then
write('')
if fun.deprecated then
write('--- @deprecated')
end
local desc = fun.desc
if desc then
desc = desc:gsub('\n%s*\n%s*$', '\n')
for _, l in ipairs(vim.split(desc, '\n', { plain = true })) do
l = l:gsub('^ ', ''):gsub('\t', ' '):gsub('@', '\\@')
write('--- ' .. l)
end
end
local req_args = type(fun.args) == 'table' and fun.args[1] or fun.args or 0
for i, param in ipairs(params) do
local pname, ptype = param[1], param[2]
local optional = (pname ~= '...' and i > req_args) and '?' or ''
write(string.format('--- @param %s%s %s', pname, optional, ptype))
end
if fun.returns ~= false then
write('--- @return ' .. (fun.returns or 'any'))
end
write(render_fun_sig(funname, params))
return
end
print('no doc for', funname)
end
--- @type table<string,true>
local rendered_tags = {}
--- @param f string
--- @param fun vim.EvalFn
--- @param write fun(line: string)
local function render_eval_doc(f, fun, write)
if fun.deprecated then
return
end
if not fun.signature then
return
end
local desc = fun.desc
if not desc then
write(fun.signature)
return
end
local name = fun.name or f
local tags = { '*' .. name .. '()*' }
if fun.tags then
for _, t in ipairs(fun.tags) do
tags[#tags + 1] = '*' .. t .. '*'
end
end
local tag = table.concat(tags, ' ')
local siglen = #fun.signature
if rendered_tags[name] then
write(fun.signature)
else
if siglen + #tag > 80 then
write(string.rep('\t', 6) .. tag)
write(fun.signature)
else
local tt = math.max(1, (76 - siglen - #tag) / 8)
write(string.format('%s%s%s', fun.signature, string.rep('\t', tt), tag))
end
end
rendered_tags[name] = true
desc = vim.trim(desc)
local desc_l = vim.split(desc, '\n', { plain = true })
for _, l in ipairs(desc_l) do
l = l:gsub('^ ', '')
if vim.startswith(l, '<') and not l:match('^<[A-Z][A-Z]') then
write('<\t\t' .. l:sub(2))
else
write('\t\t' .. l)
end
end
if #desc_l > 0 and not desc_l[#desc_l]:match('^<?$') then
write('')
end
end
--- @class nvim.gen_eval_files.elem
--- @field path string
--- @field render fun(f:string,fun:vim.EvalFn,write:fun(line:string))
--- @field header? string[]
--- @field footer? string[]
--- @type nvim.gen_eval_files.elem[]
local CONFIG = {
{
path = 'runtime/lua/vim/_meta/vimfn.lua',
render = render_vimfn,
header = {
'--- @meta',
'-- THIS FILE IS GENERATED',
'-- DO NOT EDIT',
},
},
{
path = 'runtime/doc/builtin.txt',
render = render_eval_doc,
header = {
'*builtin.txt* Nvim',
'',
'',
'\t\t VIM REFERENCE MANUAL\t by Bram Moolenaar',
'',
'',
'Builtin functions\t\t*vimscript-functions* *builtin-functions*',
'',
'For functions grouped by what they are used for see |function-list|.',
'',
'\t\t\t\t Type |gO| to see the table of contents.',
'==============================================================================',
'1. Details *builtin-function-details*',
'',
},
footer = {
'==============================================================================',
'2. Matching a pattern in a String *string-match*',
'',
'This is common between several functions. A regexp pattern as explained at',
'|pattern| is normally used to find a match in the buffer lines. When a',
'pattern is used to find a match in a String, almost everything works in the',
'same way. The difference is that a String is handled like it is one line.',
'When it contains a "\\n" character, this is not seen as a line break for the',
'pattern. It can be matched with a "\\n" in the pattern, or with ".". Example:',
'>',
'\t:let a = "aaaa\\nxxxx"',
'\t:echo matchstr(a, "..\\n..")',
'\taa',
'\txx',
'\t:echo matchstr(a, "a.x")',
'\ta',
'\tx',
'',
'Don\'t forget that "^" will only match at the first character of the String and',
'"$" at the last character of the string. They don\'t match after or before a',
'"\\n".',
'',
' vim:tw=78:ts=8:noet:ft=help:norl:',
},
},
}
--- @param elem nvim.gen_eval_files.elem
local function render(elem)
local o = assert(io.open(elem.path, 'w'))
--- @param l string
local function write(l)
local l1 = l:gsub('%s+$', '')
o:write(l1)
o:write('\n')
end
for _, l in ipairs(elem.header or {}) do
write(l)
end
--- @type string[]
local fnames = vim.tbl_keys(funcs)
table.sort(fnames)
for _, f in ipairs(fnames) do
elem.render(f, funcs[f], write)
end
for _, l in ipairs(elem.footer or {}) do
write(l)
end
o:close()
end
local function main()
for _, c in ipairs(CONFIG) do
render(c)
end
end
main()

View File

@ -1359,4 +1359,7 @@ if __name__ == "__main__":
else:
main(Doxyfile, args)
print('Running ./scripts/gen_eval_files.lua')
subprocess.call(['./scripts/gen_eval_files.lua'])
# vim: set ft=python ts=4 sw=4 tw=79 et :

View File

@ -1,249 +0,0 @@
#!/usr/bin/env -S nvim -l
--- @class vim.EvalFn2 : vim.EvalFn
--- @field signature string
--- @field desc string[]
--- @field params {[1]: string, [2]: string}[]
--- @param filename string
--- @return string
local function safe_read(filename)
local file, err = io.open(filename, 'r')
if not file then
error(err)
end
local content = file:read('*a')
io.close(file)
return content
end
local nvim_eval = require'src/nvim/eval'
local funcs = nvim_eval.funcs --[[@as table<string,vim.EvalFn2>]]
local LUA_KEYWORDS = {
['and'] = true,
['end'] = true,
['function'] = true,
['or'] = true,
['if'] = true,
['while'] = true,
['repeat'] = true
}
local SOURCES = {
{
path = 'runtime/doc/builtin.txt',
from = '^2. Details',
to = '==========',
},
{
path = 'runtime/doc/sign.txt',
from = '^3. Functions',
to = 'vim:'
},
{
path = 'runtime/doc/testing.txt',
from = '^3. Assert functions',
to = 'vim:'
}
}
local ARG_NAME_TYPES = {
col = 'integer',
nosuf = 'boolean',
dir = 'string',
mode = 'string',
width = 'integer',
height = 'integer',
timeout = 'integer',
libname = 'string',
funcname = 'string',
end_ = 'integer',
file = 'string',
flags = 'string',
fname = 'integer',
idx = 'integer',
lnum = 'integer',
mods = 'string',
name = 'string',
nr = 'integer',
options = 'table',
opts = 'table',
path = 'string',
regname = 'string',
silent = 'boolean',
string = 'string',
tabnr = 'integer',
varname = 'string',
winid = 'integer',
winnr = 'integer',
}
local function process_source(source)
local src_txt = safe_read(source.path)
--- @type string[]
local src_lines = vim.split(src_txt, '\n', { plain = true })
local s = 0
for i, l in ipairs(src_lines) do
if l:match(source.from) then
s = i+1
end
end
local lines = {} --- @type string[]
local last_f --- @type string?
local last_l --- @type string?
for i = s, #src_lines do
local l = src_lines[i]
if not l or l:match(source.to) then
break
end
local f = l:match('^([a-z][a-zA-Z0-9_]*)%(')
if f then
if last_f then
if last_l and last_l:find('*' .. f .. '()*', 1, true) then
lines[#lines] = nil
end
funcs[last_f].desc = lines
end
last_f = f
local sig = l:match('[^)]+%)')
local params = {} --- @type table[]
if sig then
for param in string.gmatch(sig, '{([a-z][a-zA-Z0-9_]*)}') do
local t = ARG_NAME_TYPES[param] or 'any'
params[#params+1] = {param, t}
end
else
print('error parsing', l)
end
funcs[last_f].signature = sig
funcs[last_f].params = params
lines = {}
else
lines[#lines+1] = l:gsub('^(<?)\t\t', '%1'):gsub('\t', ' ')
end
last_l = l
end
if last_f then
funcs[last_f].desc = lines
end
end
local function render_fun_sig(f, params)
local param_str --- @type string
if params == true then
param_str = '...'
else
param_str = table.concat(vim.tbl_map(function(v)
return v[1]
end, params), ', ')
end
if LUA_KEYWORDS[f] then
return string.format('vim.fn[\'%s\'] = function(%s) end', f, param_str)
else
return string.format('function vim.fn.%s(%s) end', f, param_str)
end
end
--- Uniquify names
--- Fix any names that are lua keywords
--- @param fun vim.EvalFn2
local function process_params(fun)
if not fun.params then
return
end
local seen = {} --- @type table<string,true>
local sfx = 1
for _, p in ipairs(fun.params) do
if LUA_KEYWORDS[p[1]] then
p[1] = p[1]..'_'
end
if seen[p[1]] then
p[1] = p[1]..sfx
sfx = sfx + 1
else
seen[p[1]] = true
end
end
end
--- @param funname string
--- @param fun vim.EvalFn2
--- @param write fun(line: string)
local function render_fun(funname, fun, write)
if fun.deprecated then
write('')
write('--- @deprecated')
for _, l in ipairs(fun.deprecated) do
write('--- '.. l)
end
write(render_fun_sig(funname, true))
return
end
if fun.desc and fun.signature then
write('')
for _, l in ipairs(fun.desc) do
write('--- '.. l:gsub('@', '\\@'))
end
local req_args = type(fun.args) == 'table' and fun.args[1] or fun.args or 0
for i, param in ipairs(fun.params) do
if i <= req_args then
write('--- @param '..param[1]..' '..param[2])
else
write('--- @param '..param[1]..'? '..param[2])
end
end
if fun.returns ~= false then
write('--- @return '..(fun.returns or 'any'))
end
write(render_fun_sig(funname, fun.params))
return
end
print('no doc for', funname)
end
local function main(outfile)
local o = assert(io.open(outfile, 'w'))
local function write(l)
local l1 = l:gsub('%s+$', '')
o:write(l1)
o:write('\n')
end
for _, source in ipairs(SOURCES) do
process_source(source)
end
--- @type string[]
local fnames = vim.tbl_keys(funcs)
table.sort(fnames)
write('--- @meta')
write('-- THIS FILE IS GENERATED')
write('-- DO NOT EDIT')
for _, f in ipairs(fnames) do
local fun = funcs[f]
process_params(fun)
render_fun(f, fun, write)
end
end
main('runtime/lua/vim/_meta/vimfn.lua')

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ local funcsfname = autodir .. '/funcs.generated.h'
local hashy = require'generators.hashy'
local hashpipe = io.open(funcsfname, 'wb')
local hashpipe = assert(io.open(funcsfname, 'wb'))
hashpipe:write([[
#include "nvim/arglist.h"
@ -62,16 +62,22 @@ for _,fun in ipairs(metadata) do
end
end
local func_names = vim.tbl_keys(funcs)
local func_names = vim.tbl_filter(function(name)
return name:match('__%d*$') == nil
end, vim.tbl_keys(funcs))
table.sort(func_names)
local funcsdata = io.open(funcs_file, 'w')
local funcsdata = assert(io.open(funcs_file, 'w'))
funcsdata:write(mpack.encode(func_names))
funcsdata:close()
local neworder, hashfun = hashy.hashy_hash("find_internal_func", func_names, function (idx)
return "functions["..idx.."].name"
end)
hashpipe:write("static const EvalFuncDef functions[] = {\n")
for _, name in ipairs(neworder) do
local def = funcs[name]
local args = def.args or 0