mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 11:15:14 -07:00
39d70fcafd
This is the first PR featuring a conversion of an upstream vim9script file into a Lua file. The generated file can be found in `runtime/autoload/ccomplete.vim` in the vim repository. Below is a limited history of the changes of that file at the time of conversion. ``` ❯ git log --format=oneline runtime/autoload/ccomplete.vim c4573eb12dba6a062af28ee0b8938d1521934ce4 Update runtime files a4d131d11052cafcc5baad2273ef48e0dd4d09c5 Update runtime files 4466ad6baa22485abb1147aca3340cced4778a66 Update runtime files d1caa941d876181aae0ebebc6ea954045bf0da24 Update runtime files 20aac6c1126988339611576d425965a25a777658 Update runtime files. 30b658179962cc3c9f0a98f071b36b09a36c2b94 Updated runtime files. b6b046b281fac168a78b3eafdea9274bef06882f Updated runtime files. 00a927d62b68a3523cb1c4f9aa3f7683345c8182 Updated runtime files. 8c8de839325eda0bed68917d18179d2003b344d1 (tag: v7.2a) updated for version 7.2a ... ``` The file runtime/lua/_vim9script.lua only needs to be updated when vim9jit is updated (for any bug fixes or new features, like implementing class and interface, the latest in vim9script). Further PRs will improve the DX of generated the converted lua and tracking which files in the neovim's code base have been generated.
858 lines
26 KiB
Lua
858 lines
26 KiB
Lua
----------------------------------------
|
|
-- This file is generated via github.com/tjdevries/vim9jit
|
|
-- For any bugs, please first consider reporting there.
|
|
----------------------------------------
|
|
|
|
-- Ignore "value assigned to a local variable is unused" because
|
|
-- we can't guarantee that local variables will be used by plugins
|
|
-- luacheck: ignore 311
|
|
|
|
local vim9 = require('_vim9script')
|
|
local M = {}
|
|
local prepended = nil
|
|
local grepCache = nil
|
|
local Complete = nil
|
|
local GetAddition = nil
|
|
local Tag2item = nil
|
|
local Dict2info = nil
|
|
local ParseTagline = nil
|
|
local Tagline2item = nil
|
|
local Tagcmd2extra = nil
|
|
local Nextitem = nil
|
|
local StructMembers = nil
|
|
local SearchMembers = nil
|
|
-- vim9script
|
|
|
|
-- # Vim completion script
|
|
-- # Language: C
|
|
-- # Maintainer: Bram Moolenaar <Bram@vim.org>
|
|
-- # Rewritten in Vim9 script by github user lacygoill
|
|
-- # Last Change: 2022 Jan 31
|
|
|
|
prepended = ''
|
|
grepCache = vim.empty_dict()
|
|
|
|
-- # This function is used for the 'omnifunc' option.
|
|
|
|
Complete = function(findstart, abase)
|
|
findstart = vim9.bool(findstart)
|
|
if vim9.bool(findstart) then
|
|
-- # Locate the start of the item, including ".", "->" and "[...]".
|
|
local line = vim9.fn.getline('.')
|
|
local start = vim9.fn.charcol('.') - 1
|
|
local lastword = -1
|
|
while start > 0 do
|
|
if vim9.ops.RegexpMatches(vim9.index(line, vim9.ops.Minus(start, 1)), '\\w') then
|
|
start = start - 1
|
|
elseif
|
|
vim9.bool(vim9.ops.RegexpMatches(vim9.index(line, vim9.ops.Minus(start, 1)), '\\.'))
|
|
then
|
|
if lastword == -1 then
|
|
lastword = start
|
|
end
|
|
start = start - 1
|
|
elseif
|
|
vim9.bool(
|
|
start > 1
|
|
and vim9.index(line, vim9.ops.Minus(start, 2)) == '-'
|
|
and vim9.index(line, vim9.ops.Minus(start, 1)) == '>'
|
|
)
|
|
then
|
|
if lastword == -1 then
|
|
lastword = start
|
|
end
|
|
start = vim9.ops.Minus(start, 2)
|
|
elseif vim9.bool(vim9.index(line, vim9.ops.Minus(start, 1)) == ']') then
|
|
-- # Skip over [...].
|
|
local n = 0
|
|
start = start - 1
|
|
while start > 0 do
|
|
start = start - 1
|
|
if vim9.index(line, start) == '[' then
|
|
if n == 0 then
|
|
break
|
|
end
|
|
n = n - 1
|
|
elseif vim9.bool(vim9.index(line, start) == ']') then
|
|
n = n + 1
|
|
end
|
|
end
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
-- # Return the column of the last word, which is going to be changed.
|
|
-- # Remember the text that comes before it in prepended.
|
|
if lastword == -1 then
|
|
prepended = ''
|
|
return vim9.fn.byteidx(line, start)
|
|
end
|
|
prepended = vim9.slice(line, start, vim9.ops.Minus(lastword, 1))
|
|
return vim9.fn.byteidx(line, lastword)
|
|
end
|
|
|
|
-- # Return list of matches.
|
|
|
|
local base = prepended .. abase
|
|
|
|
-- # Don't do anything for an empty base, would result in all the tags in the
|
|
-- # tags file.
|
|
if base == '' then
|
|
return {}
|
|
end
|
|
|
|
-- # init cache for vimgrep to empty
|
|
grepCache = {}
|
|
|
|
-- # Split item in words, keep empty word after "." or "->".
|
|
-- # "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
|
|
-- # We can't use split, because we need to skip nested [...].
|
|
-- # "aa[...]" -> ['aa', '[...]'], "aa.bb[...]" -> ['aa', 'bb', '[...]'], etc.
|
|
local items = {}
|
|
local s = 0
|
|
local arrays = 0
|
|
while 1 do
|
|
local e = vim9.fn.charidx(base, vim9.fn.match(base, '\\.\\|->\\|\\[', s))
|
|
if e < 0 then
|
|
if s == 0 or vim9.index(base, vim9.ops.Minus(s, 1)) ~= ']' then
|
|
vim9.fn.add(items, vim9.slice(base, s, nil))
|
|
end
|
|
break
|
|
end
|
|
if s == 0 or vim9.index(base, vim9.ops.Minus(s, 1)) ~= ']' then
|
|
vim9.fn.add(items, vim9.slice(base, s, vim9.ops.Minus(e, 1)))
|
|
end
|
|
if vim9.index(base, e) == '.' then
|
|
-- # skip over '.'
|
|
s = vim9.ops.Plus(e, 1)
|
|
elseif vim9.bool(vim9.index(base, e) == '-') then
|
|
-- # skip over '->'
|
|
s = vim9.ops.Plus(e, 2)
|
|
else
|
|
-- # Skip over [...].
|
|
local n = 0
|
|
s = e
|
|
e = e + 1
|
|
while e < vim9.fn.strcharlen(base) do
|
|
if vim9.index(base, e) == ']' then
|
|
if n == 0 then
|
|
break
|
|
end
|
|
n = n - 1
|
|
elseif vim9.bool(vim9.index(base, e) == '[') then
|
|
n = n + 1
|
|
end
|
|
e = e + 1
|
|
end
|
|
e = e + 1
|
|
vim9.fn.add(items, vim9.slice(base, s, vim9.ops.Minus(e, 1)))
|
|
arrays = arrays + 1
|
|
s = e
|
|
end
|
|
end
|
|
|
|
-- # Find the variable items[0].
|
|
-- # 1. in current function (like with "gd")
|
|
-- # 2. in tags file(s) (like with ":tag")
|
|
-- # 3. in current file (like with "gD")
|
|
local res = {}
|
|
if vim9.fn.searchdecl(vim9.index(items, 0), false, true) == 0 then
|
|
-- # Found, now figure out the type.
|
|
-- # TODO: join previous line if it makes sense
|
|
local line = vim9.fn.getline('.')
|
|
local col = vim9.fn.charcol('.')
|
|
if vim9.fn.stridx(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), ';') >= 0 then
|
|
-- # Handle multiple declarations on the same line.
|
|
local col2 = vim9.ops.Minus(col, 1)
|
|
while vim9.index(line, col2) ~= ';' do
|
|
col2 = col2 - 1
|
|
end
|
|
line = vim9.slice(line, vim9.ops.Plus(col2, 1), nil)
|
|
col = vim9.ops.Minus(col, col2)
|
|
end
|
|
if vim9.fn.stridx(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), ',') >= 0 then
|
|
-- # Handle multiple declarations on the same line in a function
|
|
-- # declaration.
|
|
local col2 = vim9.ops.Minus(col, 1)
|
|
while vim9.index(line, col2) ~= ',' do
|
|
col2 = col2 - 1
|
|
end
|
|
if
|
|
vim9.ops.RegexpMatches(
|
|
vim9.slice(line, vim9.ops.Plus(col2, 1), vim9.ops.Minus(col, 1)),
|
|
' *[^ ][^ ]* *[^ ]'
|
|
)
|
|
then
|
|
line = vim9.slice(line, vim9.ops.Plus(col2, 1), nil)
|
|
col = vim9.ops.Minus(col, col2)
|
|
end
|
|
end
|
|
if vim9.fn.len(items) == 1 then
|
|
-- # Completing one word and it's a local variable: May add '[', '.' or
|
|
-- # '->'.
|
|
local match = vim9.index(items, 0)
|
|
local kind = 'v'
|
|
if vim9.fn.match(line, '\\<' .. match .. '\\s*\\[') > 0 then
|
|
match = match .. '['
|
|
else
|
|
res = Nextitem(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), { '' }, 0, true)
|
|
if vim9.fn.len(res) > 0 then
|
|
-- # There are members, thus add "." or "->".
|
|
if vim9.fn.match(line, '\\*[ \\t(]*' .. match .. '\\>') > 0 then
|
|
match = match .. '->'
|
|
else
|
|
match = match .. '.'
|
|
end
|
|
end
|
|
end
|
|
res = { { ['match'] = match, ['tagline'] = '', ['kind'] = kind, ['info'] = line } }
|
|
elseif vim9.bool(vim9.fn.len(items) == vim9.ops.Plus(arrays, 1)) then
|
|
-- # Completing one word and it's a local array variable: build tagline
|
|
-- # from declaration line
|
|
local match = vim9.index(items, 0)
|
|
local kind = 'v'
|
|
local tagline = '\t/^' .. line .. '$/'
|
|
res = { { ['match'] = match, ['tagline'] = tagline, ['kind'] = kind, ['info'] = line } }
|
|
else
|
|
-- # Completing "var.", "var.something", etc.
|
|
res =
|
|
Nextitem(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), vim9.slice(items, 1, nil), 0, true)
|
|
end
|
|
end
|
|
|
|
if vim9.fn.len(items) == 1 or vim9.fn.len(items) == vim9.ops.Plus(arrays, 1) then
|
|
-- # Only one part, no "." or "->": complete from tags file.
|
|
local tags = {}
|
|
if vim9.fn.len(items) == 1 then
|
|
tags = vim9.fn.taglist('^' .. base)
|
|
else
|
|
tags = vim9.fn.taglist('^' .. vim9.index(items, 0) .. '$')
|
|
end
|
|
|
|
vim9.fn_mut('filter', {
|
|
vim9.fn_mut('filter', {
|
|
tags,
|
|
function(_, v)
|
|
return vim9.ternary(vim9.fn.has_key(v, 'kind'), function()
|
|
return v.kind ~= 'm'
|
|
end, true)
|
|
end,
|
|
}, { replace = 0 }),
|
|
function(_, v)
|
|
return vim9.ops.Or(
|
|
vim9.ops.Or(
|
|
vim9.prefix['Bang'](vim9.fn.has_key(v, 'static')),
|
|
vim9.prefix['Bang'](vim9.index(v, 'static'))
|
|
),
|
|
vim9.fn.bufnr('%') == vim9.fn.bufnr(vim9.index(v, 'filename'))
|
|
)
|
|
end,
|
|
}, { replace = 0 })
|
|
|
|
res = vim9.fn.extend(
|
|
res,
|
|
vim9.fn.map(tags, function(_, v)
|
|
return Tag2item(v)
|
|
end)
|
|
)
|
|
end
|
|
|
|
if vim9.fn.len(res) == 0 then
|
|
-- # Find the variable in the tags file(s)
|
|
local diclist = vim9.fn.filter(
|
|
vim9.fn.taglist('^' .. vim9.index(items, 0) .. '$'),
|
|
function(_, v)
|
|
return vim9.ternary(vim9.fn.has_key(v, 'kind'), function()
|
|
return v.kind ~= 'm'
|
|
end, true)
|
|
end
|
|
)
|
|
|
|
res = {}
|
|
|
|
for _, i in vim9.iter(vim9.fn.range(vim9.fn.len(diclist))) do
|
|
-- # New ctags has the "typeref" field. Patched version has "typename".
|
|
if vim9.bool(vim9.fn.has_key(vim9.index(diclist, i), 'typename')) then
|
|
res = vim9.fn.extend(
|
|
res,
|
|
StructMembers(
|
|
vim9.index(vim9.index(diclist, i), 'typename'),
|
|
vim9.slice(items, 1, nil),
|
|
true
|
|
)
|
|
)
|
|
elseif vim9.bool(vim9.fn.has_key(vim9.index(diclist, i), 'typeref')) then
|
|
res = vim9.fn.extend(
|
|
res,
|
|
StructMembers(
|
|
vim9.index(vim9.index(diclist, i), 'typeref'),
|
|
vim9.slice(items, 1, nil),
|
|
true
|
|
)
|
|
)
|
|
end
|
|
|
|
-- # For a variable use the command, which must be a search pattern that
|
|
-- # shows the declaration of the variable.
|
|
if vim9.index(vim9.index(diclist, i), 'kind') == 'v' then
|
|
local line = vim9.index(vim9.index(diclist, i), 'cmd')
|
|
if vim9.slice(line, nil, 1) == '/^' then
|
|
local col =
|
|
vim9.fn.charidx(line, vim9.fn.match(line, '\\<' .. vim9.index(items, 0) .. '\\>'))
|
|
res = vim9.fn.extend(
|
|
res,
|
|
Nextitem(
|
|
vim9.slice(line, 2, vim9.ops.Minus(col, 1)),
|
|
vim9.slice(items, 1, nil),
|
|
0,
|
|
true
|
|
)
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if vim9.fn.len(res) == 0 and vim9.fn.searchdecl(vim9.index(items, 0), true) == 0 then
|
|
-- # Found, now figure out the type.
|
|
-- # TODO: join previous line if it makes sense
|
|
local line = vim9.fn.getline('.')
|
|
local col = vim9.fn.charcol('.')
|
|
res =
|
|
Nextitem(vim9.slice(line, nil, vim9.ops.Minus(col, 1)), vim9.slice(items, 1, nil), 0, true)
|
|
end
|
|
|
|
-- # If the last item(s) are [...] they need to be added to the matches.
|
|
local last = vim9.fn.len(items) - 1
|
|
local brackets = ''
|
|
while last >= 0 do
|
|
if vim9.index(vim9.index(items, last), 0) ~= '[' then
|
|
break
|
|
end
|
|
brackets = vim9.index(items, last) .. brackets
|
|
last = last - 1
|
|
end
|
|
|
|
return vim9.fn.map(res, function(_, v)
|
|
return Tagline2item(v, brackets)
|
|
end)
|
|
end
|
|
M['Complete'] = Complete
|
|
|
|
GetAddition = function(line, match, memarg, bracket)
|
|
bracket = vim9.bool(bracket)
|
|
-- # Guess if the item is an array.
|
|
if vim9.bool(vim9.ops.And(bracket, vim9.fn.match(line, match .. '\\s*\\[') > 0)) then
|
|
return '['
|
|
end
|
|
|
|
-- # Check if the item has members.
|
|
if vim9.fn.len(SearchMembers(memarg, { '' }, false)) > 0 then
|
|
-- # If there is a '*' before the name use "->".
|
|
if vim9.fn.match(line, '\\*[ \\t(]*' .. match .. '\\>') > 0 then
|
|
return '->'
|
|
else
|
|
return '.'
|
|
end
|
|
end
|
|
return ''
|
|
end
|
|
|
|
Tag2item = function(val)
|
|
-- # Turn the tag info "val" into an item for completion.
|
|
-- # "val" is is an item in the list returned by taglist().
|
|
-- # If it is a variable we may add "." or "->". Don't do it for other types,
|
|
-- # such as a typedef, by not including the info that GetAddition() uses.
|
|
local res = vim9.convert.decl_dict({ ['match'] = vim9.index(val, 'name') })
|
|
|
|
res[vim9.index_expr('extra')] =
|
|
Tagcmd2extra(vim9.index(val, 'cmd'), vim9.index(val, 'name'), vim9.index(val, 'filename'))
|
|
|
|
local s = Dict2info(val)
|
|
if s ~= '' then
|
|
res[vim9.index_expr('info')] = s
|
|
end
|
|
|
|
res[vim9.index_expr('tagline')] = ''
|
|
if vim9.bool(vim9.fn.has_key(val, 'kind')) then
|
|
local kind = vim9.index(val, 'kind')
|
|
res[vim9.index_expr('kind')] = kind
|
|
if kind == 'v' then
|
|
res[vim9.index_expr('tagline')] = '\t' .. vim9.index(val, 'cmd')
|
|
res[vim9.index_expr('dict')] = val
|
|
elseif vim9.bool(kind == 'f') then
|
|
res[vim9.index_expr('match')] = vim9.index(val, 'name') .. '('
|
|
end
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
Dict2info = function(dict)
|
|
-- # Use all the items in dictionary for the "info" entry.
|
|
local info = ''
|
|
|
|
for _, k in vim9.iter(vim9.fn_mut('sort', { vim9.fn.keys(dict) }, { replace = 0 })) do
|
|
info = info .. k .. vim9.fn['repeat'](' ', 10 - vim9.fn.strlen(k))
|
|
if k == 'cmd' then
|
|
info = info
|
|
.. vim9.fn.substitute(
|
|
vim9.fn.matchstr(vim9.index(dict, 'cmd'), '/^\\s*\\zs.*\\ze$/'),
|
|
'\\\\\\(.\\)',
|
|
'\\1',
|
|
'g'
|
|
)
|
|
else
|
|
local dictk = vim9.index(dict, k)
|
|
if vim9.fn.typename(dictk) ~= 'string' then
|
|
info = info .. vim9.fn.string(dictk)
|
|
else
|
|
info = info .. dictk
|
|
end
|
|
end
|
|
info = info .. '\n'
|
|
end
|
|
|
|
return info
|
|
end
|
|
|
|
ParseTagline = function(line)
|
|
-- # Parse a tag line and return a dictionary with items like taglist()
|
|
local l = vim9.fn.split(line, '\t')
|
|
local d = vim.empty_dict()
|
|
if vim9.fn.len(l) >= 3 then
|
|
d[vim9.index_expr('name')] = vim9.index(l, 0)
|
|
d[vim9.index_expr('filename')] = vim9.index(l, 1)
|
|
d[vim9.index_expr('cmd')] = vim9.index(l, 2)
|
|
local n = 2
|
|
if vim9.ops.RegexpMatches(vim9.index(l, 2), '^/') then
|
|
-- # Find end of cmd, it may contain Tabs.
|
|
while n < vim9.fn.len(l) and vim9.ops.NotRegexpMatches(vim9.index(l, n), '/;"$') do
|
|
n = n + 1
|
|
d[vim9.index_expr('cmd')] = vim9.index(d, 'cmd') .. ' ' .. vim9.index(l, n)
|
|
end
|
|
end
|
|
|
|
for _, i in vim9.iter(vim9.fn.range(vim9.ops.Plus(n, 1), vim9.fn.len(l) - 1)) do
|
|
if vim9.index(l, i) == 'file:' then
|
|
d[vim9.index_expr('static')] = 1
|
|
elseif vim9.bool(vim9.ops.NotRegexpMatches(vim9.index(l, i), ':')) then
|
|
d[vim9.index_expr('kind')] = vim9.index(l, i)
|
|
else
|
|
d[vim9.index_expr(vim9.fn.matchstr(vim9.index(l, i), '[^:]*'))] =
|
|
vim9.fn.matchstr(vim9.index(l, i), ':\\zs.*')
|
|
end
|
|
end
|
|
end
|
|
|
|
return d
|
|
end
|
|
|
|
Tagline2item = function(val, brackets)
|
|
-- # Turn a match item "val" into an item for completion.
|
|
-- # "val['match']" is the matching item.
|
|
-- # "val['tagline']" is the tagline in which the last part was found.
|
|
local line = vim9.index(val, 'tagline')
|
|
local add = GetAddition(line, vim9.index(val, 'match'), { val }, brackets == '')
|
|
local res = vim9.convert.decl_dict({ ['word'] = vim9.index(val, 'match') .. brackets .. add })
|
|
|
|
if vim9.bool(vim9.fn.has_key(val, 'info')) then
|
|
-- # Use info from Tag2item().
|
|
res[vim9.index_expr('info')] = vim9.index(val, 'info')
|
|
else
|
|
-- # Parse the tag line and add each part to the "info" entry.
|
|
local s = Dict2info(ParseTagline(line))
|
|
if s ~= '' then
|
|
res[vim9.index_expr('info')] = s
|
|
end
|
|
end
|
|
|
|
if vim9.bool(vim9.fn.has_key(val, 'kind')) then
|
|
res[vim9.index_expr('kind')] = vim9.index(val, 'kind')
|
|
elseif vim9.bool(add == '(') then
|
|
res[vim9.index_expr('kind')] = 'f'
|
|
else
|
|
local s = vim9.fn.matchstr(line, '\\t\\(kind:\\)\\=\\zs\\S\\ze\\(\\t\\|$\\)')
|
|
if s ~= '' then
|
|
res[vim9.index_expr('kind')] = s
|
|
end
|
|
end
|
|
|
|
if vim9.bool(vim9.fn.has_key(val, 'extra')) then
|
|
res[vim9.index_expr('menu')] = vim9.index(val, 'extra')
|
|
return res
|
|
end
|
|
|
|
-- # Isolate the command after the tag and filename.
|
|
local s = vim9.fn.matchstr(
|
|
line,
|
|
'[^\\t]*\\t[^\\t]*\\t\\zs\\(/^.*$/\\|[^\\t]*\\)\\ze\\(;"\\t\\|\\t\\|$\\)'
|
|
)
|
|
if s ~= '' then
|
|
res[vim9.index_expr('menu')] = Tagcmd2extra(
|
|
s,
|
|
vim9.index(val, 'match'),
|
|
vim9.fn.matchstr(line, '[^\\t]*\\t\\zs[^\\t]*\\ze\\t')
|
|
)
|
|
end
|
|
return res
|
|
end
|
|
|
|
Tagcmd2extra = function(cmd, name, fname)
|
|
-- # Turn a command from a tag line to something that is useful in the menu
|
|
local x = ''
|
|
if vim9.ops.RegexpMatches(cmd, '^/^') then
|
|
-- # The command is a search command, useful to see what it is.
|
|
x = vim9.fn.substitute(
|
|
vim9.fn.substitute(
|
|
vim9.fn.matchstr(cmd, '^/^\\s*\\zs.*\\ze$/'),
|
|
'\\<' .. name .. '\\>',
|
|
'@@',
|
|
''
|
|
),
|
|
'\\\\\\(.\\)',
|
|
'\\1',
|
|
'g'
|
|
) .. ' - ' .. fname
|
|
elseif vim9.bool(vim9.ops.RegexpMatches(cmd, '^\\d*$')) then
|
|
-- # The command is a line number, the file name is more useful.
|
|
x = fname .. ' - ' .. cmd
|
|
else
|
|
-- # Not recognized, use command and file name.
|
|
x = cmd .. ' - ' .. fname
|
|
end
|
|
return x
|
|
end
|
|
|
|
Nextitem = function(lead, items, depth, all)
|
|
all = vim9.bool(all)
|
|
-- # Find composing type in "lead" and match items[0] with it.
|
|
-- # Repeat this recursively for items[1], if it's there.
|
|
-- # When resolving typedefs "depth" is used to avoid infinite recursion.
|
|
-- # Return the list of matches.
|
|
|
|
-- # Use the text up to the variable name and split it in tokens.
|
|
local tokens = vim9.fn.split(lead, '\\s\\+\\|\\<')
|
|
|
|
-- # Try to recognize the type of the variable. This is rough guessing...
|
|
local res = {}
|
|
|
|
local body = function(_, tidx)
|
|
-- # Skip tokens starting with a non-ID character.
|
|
if vim9.ops.NotRegexpMatches(vim9.index(tokens, tidx), '^\\h') then
|
|
return vim9.ITER_CONTINUE
|
|
end
|
|
|
|
-- # Recognize "struct foobar" and "union foobar".
|
|
-- # Also do "class foobar" when it's C++ after all (doesn't work very well
|
|
-- # though).
|
|
if
|
|
(
|
|
vim9.index(tokens, tidx) == 'struct'
|
|
or vim9.index(tokens, tidx) == 'union'
|
|
or vim9.index(tokens, tidx) == 'class'
|
|
) and vim9.ops.Plus(tidx, 1) < vim9.fn.len(tokens)
|
|
then
|
|
res = StructMembers(
|
|
vim9.index(tokens, tidx) .. ':' .. vim9.index(tokens, vim9.ops.Plus(tidx, 1)),
|
|
items,
|
|
all
|
|
)
|
|
return vim9.ITER_BREAK
|
|
end
|
|
|
|
-- # TODO: add more reserved words
|
|
if
|
|
vim9.fn.index(
|
|
{ 'int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern' },
|
|
vim9.index(tokens, tidx)
|
|
) >= 0
|
|
then
|
|
return vim9.ITER_CONTINUE
|
|
end
|
|
|
|
-- # Use the tags file to find out if this is a typedef.
|
|
local diclist = vim9.fn.taglist('^' .. vim9.index(tokens, tidx) .. '$')
|
|
|
|
local body = function(_, tagidx)
|
|
local item = vim9.convert.decl_dict(vim9.index(diclist, tagidx))
|
|
|
|
-- # New ctags has the "typeref" field. Patched version has "typename".
|
|
if vim9.bool(vim9.fn.has_key(item, 'typeref')) then
|
|
res = vim9.fn.extend(res, StructMembers(vim9.index(item, 'typeref'), items, all))
|
|
return vim9.ITER_CONTINUE
|
|
end
|
|
if vim9.bool(vim9.fn.has_key(item, 'typename')) then
|
|
res = vim9.fn.extend(res, StructMembers(vim9.index(item, 'typename'), items, all))
|
|
return vim9.ITER_CONTINUE
|
|
end
|
|
|
|
-- # Only handle typedefs here.
|
|
if vim9.index(item, 'kind') ~= 't' then
|
|
return vim9.ITER_CONTINUE
|
|
end
|
|
|
|
-- # Skip matches local to another file.
|
|
if
|
|
vim9.bool(
|
|
vim9.ops.And(
|
|
vim9.ops.And(vim9.fn.has_key(item, 'static'), vim9.index(item, 'static')),
|
|
vim9.fn.bufnr('%') ~= vim9.fn.bufnr(vim9.index(item, 'filename'))
|
|
)
|
|
)
|
|
then
|
|
return vim9.ITER_CONTINUE
|
|
end
|
|
|
|
-- # For old ctags we recognize "typedef struct aaa" and
|
|
-- # "typedef union bbb" in the tags file command.
|
|
local cmd = vim9.index(item, 'cmd')
|
|
local ei = vim9.fn.charidx(cmd, vim9.fn.matchend(cmd, 'typedef\\s\\+'))
|
|
if ei > 1 then
|
|
local cmdtokens = vim9.fn.split(vim9.slice(cmd, ei, nil), '\\s\\+\\|\\<')
|
|
if vim9.fn.len(cmdtokens) > 1 then
|
|
if
|
|
vim9.index(cmdtokens, 0) == 'struct'
|
|
or vim9.index(cmdtokens, 0) == 'union'
|
|
or vim9.index(cmdtokens, 0) == 'class'
|
|
then
|
|
local name = ''
|
|
-- # Use the first identifier after the "struct" or "union"
|
|
|
|
for _, ti in vim9.iter(vim9.fn.range((vim9.fn.len(cmdtokens) - 1))) do
|
|
if vim9.ops.RegexpMatches(vim9.index(cmdtokens, ti), '^\\w') then
|
|
name = vim9.index(cmdtokens, ti)
|
|
break
|
|
end
|
|
end
|
|
|
|
if name ~= '' then
|
|
res = vim9.fn.extend(
|
|
res,
|
|
StructMembers(vim9.index(cmdtokens, 0) .. ':' .. name, items, all)
|
|
)
|
|
end
|
|
elseif vim9.bool(depth < 10) then
|
|
-- # Could be "typedef other_T some_T".
|
|
res = vim9.fn.extend(
|
|
res,
|
|
Nextitem(vim9.index(cmdtokens, 0), items, vim9.ops.Plus(depth, 1), all)
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
return vim9.ITER_DEFAULT
|
|
end
|
|
|
|
for _, tagidx in vim9.iter(vim9.fn.range(vim9.fn.len(diclist))) do
|
|
local nvim9_status, nvim9_ret = body(_, tagidx)
|
|
if nvim9_status == vim9.ITER_BREAK then
|
|
break
|
|
elseif nvim9_status == vim9.ITER_RETURN then
|
|
return nvim9_ret
|
|
end
|
|
end
|
|
|
|
if vim9.fn.len(res) > 0 then
|
|
return vim9.ITER_BREAK
|
|
end
|
|
|
|
return vim9.ITER_DEFAULT
|
|
end
|
|
|
|
for _, tidx in vim9.iter(vim9.fn.range(vim9.fn.len(tokens))) do
|
|
local nvim9_status, nvim9_ret = body(_, tidx)
|
|
if nvim9_status == vim9.ITER_BREAK then
|
|
break
|
|
elseif nvim9_status == vim9.ITER_RETURN then
|
|
return nvim9_ret
|
|
end
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
StructMembers = function(atypename, items, all)
|
|
all = vim9.bool(all)
|
|
|
|
-- # Search for members of structure "typename" in tags files.
|
|
-- # Return a list with resulting matches.
|
|
-- # Each match is a dictionary with "match" and "tagline" entries.
|
|
-- # When "all" is true find all, otherwise just return 1 if there is any member.
|
|
|
|
-- # Todo: What about local structures?
|
|
local fnames = vim9.fn.join(vim9.fn.map(vim9.fn.tagfiles(), function(_, v)
|
|
return vim9.fn.escape(v, ' \\#%')
|
|
end))
|
|
if fnames == '' then
|
|
return {}
|
|
end
|
|
|
|
local typename = atypename
|
|
local qflist = {}
|
|
local cached = 0
|
|
local n = ''
|
|
if vim9.bool(vim9.prefix['Bang'](all)) then
|
|
n = '1'
|
|
if vim9.bool(vim9.fn.has_key(grepCache, typename)) then
|
|
qflist = vim9.index(grepCache, typename)
|
|
cached = 1
|
|
end
|
|
else
|
|
n = ''
|
|
end
|
|
if vim9.bool(vim9.prefix['Bang'](cached)) then
|
|
while 1 do
|
|
vim.api.nvim_command(
|
|
'silent! keepjumps noautocmd '
|
|
.. n
|
|
.. 'vimgrep '
|
|
.. '/\\t'
|
|
.. typename
|
|
.. '\\(\\t\\|$\\)/j '
|
|
.. fnames
|
|
)
|
|
|
|
qflist = vim9.fn.getqflist()
|
|
if vim9.fn.len(qflist) > 0 or vim9.fn.match(typename, '::') < 0 then
|
|
break
|
|
end
|
|
-- # No match for "struct:context::name", remove "context::" and try again.
|
|
typename = vim9.fn.substitute(typename, ':[^:]*::', ':', '')
|
|
end
|
|
|
|
if vim9.bool(vim9.prefix['Bang'](all)) then
|
|
-- # Store the result to be able to use it again later.
|
|
grepCache[vim9.index_expr(typename)] = qflist
|
|
end
|
|
end
|
|
|
|
-- # Skip over [...] items
|
|
local idx = 0
|
|
local target = ''
|
|
while 1 do
|
|
if idx >= vim9.fn.len(items) then
|
|
target = ''
|
|
break
|
|
end
|
|
if vim9.index(vim9.index(items, idx), 0) ~= '[' then
|
|
target = vim9.index(items, idx)
|
|
break
|
|
end
|
|
idx = idx + 1
|
|
end
|
|
-- # Put matching members in matches[].
|
|
local matches = {}
|
|
|
|
for _, l in vim9.iter(qflist) do
|
|
local memb = vim9.fn.matchstr(vim9.index(l, 'text'), '[^\\t]*')
|
|
if vim9.ops.RegexpMatches(memb, '^' .. target) then
|
|
-- # Skip matches local to another file.
|
|
if
|
|
vim9.fn.match(vim9.index(l, 'text'), '\tfile:') < 0
|
|
or vim9.fn.bufnr('%')
|
|
== vim9.fn.bufnr(vim9.fn.matchstr(vim9.index(l, 'text'), '\\t\\zs[^\\t]*'))
|
|
then
|
|
local item =
|
|
vim9.convert.decl_dict({ ['match'] = memb, ['tagline'] = vim9.index(l, 'text') })
|
|
|
|
-- # Add the kind of item.
|
|
local s =
|
|
vim9.fn.matchstr(vim9.index(l, 'text'), '\\t\\(kind:\\)\\=\\zs\\S\\ze\\(\\t\\|$\\)')
|
|
if s ~= '' then
|
|
item[vim9.index_expr('kind')] = s
|
|
if s == 'f' then
|
|
item[vim9.index_expr('match')] = memb .. '('
|
|
end
|
|
end
|
|
|
|
vim9.fn.add(matches, item)
|
|
end
|
|
end
|
|
end
|
|
|
|
if vim9.fn.len(matches) > 0 then
|
|
-- # Skip over next [...] items
|
|
idx = idx + 1
|
|
while 1 do
|
|
if idx >= vim9.fn.len(items) then
|
|
return matches
|
|
end
|
|
if vim9.index(vim9.index(items, idx), 0) ~= '[' then
|
|
break
|
|
end
|
|
idx = idx + 1
|
|
end
|
|
|
|
-- # More items following. For each of the possible members find the
|
|
-- # matching following members.
|
|
return SearchMembers(matches, vim9.slice(items, idx, nil), all)
|
|
end
|
|
|
|
-- # Failed to find anything.
|
|
return {}
|
|
end
|
|
|
|
SearchMembers = function(matches, items, all)
|
|
all = vim9.bool(all)
|
|
|
|
-- # For matching members, find matches for following items.
|
|
-- # When "all" is true find all, otherwise just return 1 if there is any member.
|
|
local res = {}
|
|
|
|
for _, i in vim9.iter(vim9.fn.range(vim9.fn.len(matches))) do
|
|
local typename = ''
|
|
local line = ''
|
|
if vim9.bool(vim9.fn.has_key(vim9.index(matches, i), 'dict')) then
|
|
if vim9.bool(vim9.fn.has_key(vim9.index(vim9.index(matches, i), 'dict'), 'typename')) then
|
|
typename = vim9.index(vim9.index(vim9.index(matches, i), 'dict'), 'typename')
|
|
elseif vim9.bool(vim9.fn.has_key(vim9.index(vim9.index(matches, i), 'dict'), 'typeref')) then
|
|
typename = vim9.index(vim9.index(vim9.index(matches, i), 'dict'), 'typeref')
|
|
end
|
|
line = '\t' .. vim9.index(vim9.index(vim9.index(matches, i), 'dict'), 'cmd')
|
|
else
|
|
line = vim9.index(vim9.index(matches, i), 'tagline')
|
|
local eb = vim9.fn.matchend(line, '\\ttypename:')
|
|
local e = vim9.fn.charidx(line, eb)
|
|
if e < 0 then
|
|
eb = vim9.fn.matchend(line, '\\ttyperef:')
|
|
e = vim9.fn.charidx(line, eb)
|
|
end
|
|
if e > 0 then
|
|
-- # Use typename field
|
|
typename = vim9.fn.matchstr(line, '[^\\t]*', eb)
|
|
end
|
|
end
|
|
|
|
if typename ~= '' then
|
|
res = vim9.fn.extend(res, StructMembers(typename, items, all))
|
|
else
|
|
-- # Use the search command (the declaration itself).
|
|
local sb = vim9.fn.match(line, '\\t\\zs/^')
|
|
local s = vim9.fn.charidx(line, sb)
|
|
if s > 0 then
|
|
local e = vim9.fn.charidx(
|
|
line,
|
|
vim9.fn.match(line, '\\<' .. vim9.index(vim9.index(matches, i), 'match') .. '\\>', sb)
|
|
)
|
|
if e > 0 then
|
|
res =
|
|
vim9.fn.extend(res, Nextitem(vim9.slice(line, s, vim9.ops.Minus(e, 1)), items, 0, all))
|
|
end
|
|
end
|
|
end
|
|
if vim9.bool(vim9.ops.And(vim9.prefix['Bang'](all), vim9.fn.len(res) > 0)) then
|
|
break
|
|
end
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- #}}}1
|
|
|
|
-- # vim: noet sw=2 sts=2
|
|
return M
|