mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
dist: generated version of ccomplete.vim (#21623)
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.
This commit is contained in:
parent
19591e9918
commit
39d70fcafd
857
runtime/autoload/ccomplete.lua
Normal file
857
runtime/autoload/ccomplete.lua
Normal file
@ -0,0 +1,857 @@
|
||||
----------------------------------------
|
||||
-- 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
|
@ -1,639 +1,8 @@
|
||||
" Vim completion script
|
||||
" Language: C
|
||||
" Maintainer: Bram Moolenaar <Bram@vim.org>
|
||||
" Last Change: 2020 Nov 14
|
||||
" Generated vim file by vim9jit. Please do not edit
|
||||
let s:path = expand("<script>")
|
||||
let s:lua_path = fnamemodify(s:path, ":r") . ".lua"
|
||||
let s:nvim_module = luaeval('require("_vim9script").autoload(_A)', s:lua_path)
|
||||
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
" This function is used for the 'omnifunc' option.
|
||||
func ccomplete#Complete(findstart, base)
|
||||
if a:findstart
|
||||
" Locate the start of the item, including ".", "->" and "[...]".
|
||||
let line = getline('.')
|
||||
let start = col('.') - 1
|
||||
let lastword = -1
|
||||
while start > 0
|
||||
if line[start - 1] =~ '\w'
|
||||
let start -= 1
|
||||
elseif line[start - 1] =~ '\.'
|
||||
if lastword == -1
|
||||
let lastword = start
|
||||
endif
|
||||
let start -= 1
|
||||
elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
|
||||
if lastword == -1
|
||||
let lastword = start
|
||||
endif
|
||||
let start -= 2
|
||||
elseif line[start - 1] == ']'
|
||||
" Skip over [...].
|
||||
let n = 0
|
||||
let start -= 1
|
||||
while start > 0
|
||||
let start -= 1
|
||||
if line[start] == '['
|
||||
if n == 0
|
||||
break
|
||||
endif
|
||||
let n -= 1
|
||||
elseif line[start] == ']' " nested []
|
||||
let n += 1
|
||||
endif
|
||||
endwhile
|
||||
else
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
|
||||
" Return the column of the last word, which is going to be changed.
|
||||
" Remember the text that comes before it in s:prepended.
|
||||
if lastword == -1
|
||||
let s:prepended = ''
|
||||
return start
|
||||
endif
|
||||
let s:prepended = strpart(line, start, lastword - start)
|
||||
return lastword
|
||||
endif
|
||||
|
||||
" Return list of matches.
|
||||
|
||||
let base = s:prepended . a:base
|
||||
|
||||
" Don't do anything for an empty base, would result in all the tags in the
|
||||
" tags file.
|
||||
if base == ''
|
||||
return []
|
||||
endif
|
||||
|
||||
" init cache for vimgrep to empty
|
||||
let s: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.
|
||||
let items = []
|
||||
let s = 0
|
||||
let arrays = 0
|
||||
while 1
|
||||
let e = match(base, '\.\|->\|\[', s)
|
||||
if e < 0
|
||||
if s == 0 || base[s - 1] != ']'
|
||||
call add(items, strpart(base, s))
|
||||
endif
|
||||
break
|
||||
endif
|
||||
if s == 0 || base[s - 1] != ']'
|
||||
call add(items, strpart(base, s, e - s))
|
||||
endif
|
||||
if base[e] == '.'
|
||||
let s = e + 1 " skip over '.'
|
||||
elseif base[e] == '-'
|
||||
let s = e + 2 " skip over '->'
|
||||
else
|
||||
" Skip over [...].
|
||||
let n = 0
|
||||
let s = e
|
||||
let e += 1
|
||||
while e < len(base)
|
||||
if base[e] == ']'
|
||||
if n == 0
|
||||
break
|
||||
endif
|
||||
let n -= 1
|
||||
elseif base[e] == '[' " nested [...]
|
||||
let n += 1
|
||||
endif
|
||||
let e += 1
|
||||
endwhile
|
||||
let e += 1
|
||||
call add(items, strpart(base, s, e - s))
|
||||
let arrays += 1
|
||||
let s = e
|
||||
endif
|
||||
endwhile
|
||||
|
||||
" 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")
|
||||
let res = []
|
||||
if searchdecl(items[0], 0, 1) == 0
|
||||
" Found, now figure out the type.
|
||||
" TODO: join previous line if it makes sense
|
||||
let line = getline('.')
|
||||
let col = col('.')
|
||||
if stridx(strpart(line, 0, col), ';') != -1
|
||||
" Handle multiple declarations on the same line.
|
||||
let col2 = col - 1
|
||||
while line[col2] != ';'
|
||||
let col2 -= 1
|
||||
endwhile
|
||||
let line = strpart(line, col2 + 1)
|
||||
let col -= col2
|
||||
endif
|
||||
if stridx(strpart(line, 0, col), ',') != -1
|
||||
" Handle multiple declarations on the same line in a function
|
||||
" declaration.
|
||||
let col2 = col - 1
|
||||
while line[col2] != ','
|
||||
let col2 -= 1
|
||||
endwhile
|
||||
if strpart(line, col2 + 1, col - col2 - 1) =~ ' *[^ ][^ ]* *[^ ]'
|
||||
let line = strpart(line, col2 + 1)
|
||||
let col -= col2
|
||||
endif
|
||||
endif
|
||||
if len(items) == 1
|
||||
" Completing one word and it's a local variable: May add '[', '.' or
|
||||
" '->'.
|
||||
let match = items[0]
|
||||
let kind = 'v'
|
||||
if match(line, '\<' . match . '\s*\[') > 0
|
||||
let match .= '['
|
||||
else
|
||||
let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1)
|
||||
if len(res) > 0
|
||||
" There are members, thus add "." or "->".
|
||||
if match(line, '\*[ \t(]*' . match . '\>') > 0
|
||||
let match .= '->'
|
||||
else
|
||||
let match .= '.'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
let res = [{'match': match, 'tagline' : '', 'kind' : kind, 'info' : line}]
|
||||
elseif len(items) == arrays + 1
|
||||
" Completing one word and it's a local array variable: build tagline
|
||||
" from declaration line
|
||||
let match = items[0]
|
||||
let kind = 'v'
|
||||
let tagline = "\t/^" . line . '$/'
|
||||
let res = [{'match': match, 'tagline' : tagline, 'kind' : kind, 'info' : line}]
|
||||
else
|
||||
" Completing "var.", "var.something", etc.
|
||||
let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
|
||||
endif
|
||||
endif
|
||||
|
||||
if len(items) == 1 || len(items) == arrays + 1
|
||||
" Only one part, no "." or "->": complete from tags file.
|
||||
if len(items) == 1
|
||||
let tags = taglist('^' . base)
|
||||
else
|
||||
let tags = taglist('^' . items[0] . '$')
|
||||
endif
|
||||
|
||||
" Remove members, these can't appear without something in front.
|
||||
call filter(tags, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
|
||||
|
||||
" Remove static matches in other files.
|
||||
call filter(tags, '!has_key(v:val, "static") || !v:val["static"] || bufnr("%") == bufnr(v:val["filename"])')
|
||||
|
||||
call extend(res, map(tags, 's:Tag2item(v:val)'))
|
||||
endif
|
||||
|
||||
if len(res) == 0
|
||||
" Find the variable in the tags file(s)
|
||||
let diclist = taglist('^' . items[0] . '$')
|
||||
|
||||
" Remove members, these can't appear without something in front.
|
||||
call filter(diclist, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
|
||||
|
||||
let res = []
|
||||
for i in range(len(diclist))
|
||||
" New ctags has the "typeref" field. Patched version has "typename".
|
||||
if has_key(diclist[i], 'typename')
|
||||
call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 1))
|
||||
elseif has_key(diclist[i], 'typeref')
|
||||
call extend(res, s:StructMembers(diclist[i]['typeref'], items[1:], 1))
|
||||
endif
|
||||
|
||||
" For a variable use the command, which must be a search pattern that
|
||||
" shows the declaration of the variable.
|
||||
if diclist[i]['kind'] == 'v'
|
||||
let line = diclist[i]['cmd']
|
||||
if line[0] == '/' && line[1] == '^'
|
||||
let col = match(line, '\<' . items[0] . '\>')
|
||||
call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1))
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
if len(res) == 0 && searchdecl(items[0], 1) == 0
|
||||
" Found, now figure out the type.
|
||||
" TODO: join previous line if it makes sense
|
||||
let line = getline('.')
|
||||
let col = col('.')
|
||||
let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
|
||||
endif
|
||||
|
||||
" If the last item(s) are [...] they need to be added to the matches.
|
||||
let last = len(items) - 1
|
||||
let brackets = ''
|
||||
while last >= 0
|
||||
if items[last][0] != '['
|
||||
break
|
||||
endif
|
||||
let brackets = items[last] . brackets
|
||||
let last -= 1
|
||||
endwhile
|
||||
|
||||
return map(res, 's:Tagline2item(v:val, brackets)')
|
||||
endfunc
|
||||
|
||||
func s:GetAddition(line, match, memarg, bracket)
|
||||
" Guess if the item is an array.
|
||||
if a:bracket && match(a:line, a:match . '\s*\[') > 0
|
||||
return '['
|
||||
endif
|
||||
|
||||
" Check if the item has members.
|
||||
if len(s:SearchMembers(a:memarg, [''], 0)) > 0
|
||||
" If there is a '*' before the name use "->".
|
||||
if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0
|
||||
return '->'
|
||||
else
|
||||
return '.'
|
||||
endif
|
||||
endif
|
||||
return ''
|
||||
endfunc
|
||||
|
||||
" 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 s:GetAddition() uses.
|
||||
func s:Tag2item(val)
|
||||
let res = {'match': a:val['name']}
|
||||
|
||||
let res['extra'] = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename'])
|
||||
|
||||
let s = s:Dict2info(a:val)
|
||||
if s != ''
|
||||
let res['info'] = s
|
||||
endif
|
||||
|
||||
let res['tagline'] = ''
|
||||
if has_key(a:val, "kind")
|
||||
let kind = a:val['kind']
|
||||
let res['kind'] = kind
|
||||
if kind == 'v'
|
||||
let res['tagline'] = "\t" . a:val['cmd']
|
||||
let res['dict'] = a:val
|
||||
elseif kind == 'f'
|
||||
let res['match'] = a:val['name'] . '('
|
||||
endif
|
||||
endif
|
||||
|
||||
return res
|
||||
endfunc
|
||||
|
||||
" Use all the items in dictionary for the "info" entry.
|
||||
func s:Dict2info(dict)
|
||||
let info = ''
|
||||
for k in sort(keys(a:dict))
|
||||
let info .= k . repeat(' ', 10 - len(k))
|
||||
if k == 'cmd'
|
||||
let info .= substitute(matchstr(a:dict['cmd'], '/^\s*\zs.*\ze$/'), '\\\(.\)', '\1', 'g')
|
||||
else
|
||||
let info .= a:dict[k]
|
||||
endif
|
||||
let info .= "\n"
|
||||
endfor
|
||||
return info
|
||||
endfunc
|
||||
|
||||
" Parse a tag line and return a dictionary with items like taglist()
|
||||
func s:ParseTagline(line)
|
||||
let l = split(a:line, "\t")
|
||||
let d = {}
|
||||
if len(l) >= 3
|
||||
let d['name'] = l[0]
|
||||
let d['filename'] = l[1]
|
||||
let d['cmd'] = l[2]
|
||||
let n = 2
|
||||
if l[2] =~ '^/'
|
||||
" Find end of cmd, it may contain Tabs.
|
||||
while n < len(l) && l[n] !~ '/;"$'
|
||||
let n += 1
|
||||
let d['cmd'] .= " " . l[n]
|
||||
endwhile
|
||||
endif
|
||||
for i in range(n + 1, len(l) - 1)
|
||||
if l[i] == 'file:'
|
||||
let d['static'] = 1
|
||||
elseif l[i] !~ ':'
|
||||
let d['kind'] = l[i]
|
||||
else
|
||||
let d[matchstr(l[i], '[^:]*')] = matchstr(l[i], ':\zs.*')
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
return d
|
||||
endfunc
|
||||
|
||||
" 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.
|
||||
func s:Tagline2item(val, brackets)
|
||||
let line = a:val['tagline']
|
||||
let add = s:GetAddition(line, a:val['match'], [a:val], a:brackets == '')
|
||||
let res = {'word': a:val['match'] . a:brackets . add }
|
||||
|
||||
if has_key(a:val, 'info')
|
||||
" Use info from Tag2item().
|
||||
let res['info'] = a:val['info']
|
||||
else
|
||||
" Parse the tag line and add each part to the "info" entry.
|
||||
let s = s:Dict2info(s:ParseTagline(line))
|
||||
if s != ''
|
||||
let res['info'] = s
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(a:val, 'kind')
|
||||
let res['kind'] = a:val['kind']
|
||||
elseif add == '('
|
||||
let res['kind'] = 'f'
|
||||
else
|
||||
let s = matchstr(line, '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
|
||||
if s != ''
|
||||
let res['kind'] = s
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(a:val, 'extra')
|
||||
let res['menu'] = a:val['extra']
|
||||
return res
|
||||
endif
|
||||
|
||||
" Isolate the command after the tag and filename.
|
||||
let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
|
||||
if s != ''
|
||||
let res['menu'] = s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t'))
|
||||
endif
|
||||
return res
|
||||
endfunc
|
||||
|
||||
" Turn a command from a tag line to something that is useful in the menu
|
||||
func s:Tagcmd2extra(cmd, name, fname)
|
||||
if a:cmd =~ '^/^'
|
||||
" The command is a search command, useful to see what it is.
|
||||
let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/')
|
||||
let x = substitute(x, '\<' . a:name . '\>', '@@', '')
|
||||
let x = substitute(x, '\\\(.\)', '\1', 'g')
|
||||
let x = x . ' - ' . a:fname
|
||||
elseif a:cmd =~ '^\d*$'
|
||||
" The command is a line number, the file name is more useful.
|
||||
let x = a:fname . ' - ' . a:cmd
|
||||
else
|
||||
" Not recognized, use command and file name.
|
||||
let x = a:cmd . ' - ' . a:fname
|
||||
endif
|
||||
return x
|
||||
endfunc
|
||||
|
||||
" 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.
|
||||
func s:Nextitem(lead, items, depth, all)
|
||||
|
||||
" Use the text up to the variable name and split it in tokens.
|
||||
let tokens = split(a:lead, '\s\+\|\<')
|
||||
|
||||
" Try to recognize the type of the variable. This is rough guessing...
|
||||
let res = []
|
||||
for tidx in range(len(tokens))
|
||||
|
||||
" Skip tokens starting with a non-ID character.
|
||||
if tokens[tidx] !~ '^\h'
|
||||
continue
|
||||
endif
|
||||
|
||||
" Recognize "struct foobar" and "union foobar".
|
||||
" Also do "class foobar" when it's C++ after all (doesn't work very well
|
||||
" though).
|
||||
if (tokens[tidx] == 'struct' || tokens[tidx] == 'union' || tokens[tidx] == 'class') && tidx + 1 < len(tokens)
|
||||
let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
|
||||
break
|
||||
endif
|
||||
|
||||
" TODO: add more reserved words
|
||||
if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
|
||||
continue
|
||||
endif
|
||||
|
||||
" Use the tags file to find out if this is a typedef.
|
||||
let diclist = taglist('^' . tokens[tidx] . '$')
|
||||
for tagidx in range(len(diclist))
|
||||
let item = diclist[tagidx]
|
||||
|
||||
" New ctags has the "typeref" field. Patched version has "typename".
|
||||
if has_key(item, 'typeref')
|
||||
call extend(res, s:StructMembers(item['typeref'], a:items, a:all))
|
||||
continue
|
||||
endif
|
||||
if has_key(item, 'typename')
|
||||
call extend(res, s:StructMembers(item['typename'], a:items, a:all))
|
||||
continue
|
||||
endif
|
||||
|
||||
" Only handle typedefs here.
|
||||
if item['kind'] != 't'
|
||||
continue
|
||||
endif
|
||||
|
||||
" Skip matches local to another file.
|
||||
if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename'])
|
||||
continue
|
||||
endif
|
||||
|
||||
" For old ctags we recognize "typedef struct aaa" and
|
||||
" "typedef union bbb" in the tags file command.
|
||||
let cmd = item['cmd']
|
||||
let ei = matchend(cmd, 'typedef\s\+')
|
||||
if ei > 1
|
||||
let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
|
||||
if len(cmdtokens) > 1
|
||||
if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union' || cmdtokens[0] == 'class'
|
||||
let name = ''
|
||||
" Use the first identifier after the "struct" or "union"
|
||||
for ti in range(len(cmdtokens) - 1)
|
||||
if cmdtokens[ti] =~ '^\w'
|
||||
let name = cmdtokens[ti]
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
if name != ''
|
||||
call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
|
||||
endif
|
||||
elseif a:depth < 10
|
||||
" Could be "typedef other_T some_T".
|
||||
call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
if len(res) > 0
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
return res
|
||||
endfunc
|
||||
|
||||
|
||||
" 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 non-zero find all, otherwise just return 1 if there is any
|
||||
" member.
|
||||
func s:StructMembers(typename, items, all)
|
||||
" Todo: What about local structures?
|
||||
let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
|
||||
if fnames == ''
|
||||
return []
|
||||
endif
|
||||
|
||||
let typename = a:typename
|
||||
let qflist = []
|
||||
let cached = 0
|
||||
if a:all == 0
|
||||
let n = '1' " stop at first found match
|
||||
if has_key(s:grepCache, a:typename)
|
||||
let qflist = s:grepCache[a:typename]
|
||||
let cached = 1
|
||||
endif
|
||||
else
|
||||
let n = ''
|
||||
endif
|
||||
if !cached
|
||||
while 1
|
||||
exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
|
||||
|
||||
let qflist = getqflist()
|
||||
if len(qflist) > 0 || match(typename, "::") < 0
|
||||
break
|
||||
endif
|
||||
" No match for "struct:context::name", remove "context::" and try again.
|
||||
let typename = substitute(typename, ':[^:]*::', ':', '')
|
||||
endwhile
|
||||
|
||||
if a:all == 0
|
||||
" Store the result to be able to use it again later.
|
||||
let s:grepCache[a:typename] = qflist
|
||||
endif
|
||||
endif
|
||||
|
||||
" Skip over [...] items
|
||||
let idx = 0
|
||||
while 1
|
||||
if idx >= len(a:items)
|
||||
let target = '' " No further items, matching all members
|
||||
break
|
||||
endif
|
||||
if a:items[idx][0] != '['
|
||||
let target = a:items[idx]
|
||||
break
|
||||
endif
|
||||
let idx += 1
|
||||
endwhile
|
||||
" Put matching members in matches[].
|
||||
let matches = []
|
||||
for l in qflist
|
||||
let memb = matchstr(l['text'], '[^\t]*')
|
||||
if memb =~ '^' . target
|
||||
" Skip matches local to another file.
|
||||
if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*'))
|
||||
let item = {'match': memb, 'tagline': l['text']}
|
||||
|
||||
" Add the kind of item.
|
||||
let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
|
||||
if s != ''
|
||||
let item['kind'] = s
|
||||
if s == 'f'
|
||||
let item['match'] = memb . '('
|
||||
endif
|
||||
endif
|
||||
|
||||
call add(matches, item)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
if len(matches) > 0
|
||||
" Skip over next [...] items
|
||||
let idx += 1
|
||||
while 1
|
||||
if idx >= len(a:items)
|
||||
return matches " No further items, return the result.
|
||||
endif
|
||||
if a:items[idx][0] != '['
|
||||
break
|
||||
endif
|
||||
let idx += 1
|
||||
endwhile
|
||||
|
||||
" More items following. For each of the possible members find the
|
||||
" matching following members.
|
||||
return s:SearchMembers(matches, a:items[idx :], a:all)
|
||||
endif
|
||||
|
||||
" Failed to find anything.
|
||||
return []
|
||||
endfunc
|
||||
|
||||
" For matching members, find matches for following items.
|
||||
" When "all" is non-zero find all, otherwise just return 1 if there is any
|
||||
" member.
|
||||
func s:SearchMembers(matches, items, all)
|
||||
let res = []
|
||||
for i in range(len(a:matches))
|
||||
let typename = ''
|
||||
if has_key(a:matches[i], 'dict')
|
||||
if has_key(a:matches[i].dict, 'typename')
|
||||
let typename = a:matches[i].dict['typename']
|
||||
elseif has_key(a:matches[i].dict, 'typeref')
|
||||
let typename = a:matches[i].dict['typeref']
|
||||
endif
|
||||
let line = "\t" . a:matches[i].dict['cmd']
|
||||
else
|
||||
let line = a:matches[i]['tagline']
|
||||
let e = matchend(line, '\ttypename:')
|
||||
if e < 0
|
||||
let e = matchend(line, '\ttyperef:')
|
||||
endif
|
||||
if e > 0
|
||||
" Use typename field
|
||||
let typename = matchstr(line, '[^\t]*', e)
|
||||
endif
|
||||
endif
|
||||
|
||||
if typename != ''
|
||||
call extend(res, s:StructMembers(typename, a:items, a:all))
|
||||
else
|
||||
" Use the search command (the declaration itself).
|
||||
let s = match(line, '\t\zs/^')
|
||||
if s > 0
|
||||
let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
|
||||
if e > 0
|
||||
call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
if a:all == 0 && len(res) > 0
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
return res
|
||||
endfunc
|
||||
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: noet sw=2 sts=2
|
||||
function! ccomplete#Complete(findstart, abase) abort
|
||||
return s:nvim_module.Complete(a:findstart, a:abase)
|
||||
endfunction
|
||||
|
627
runtime/lua/_vim9script.lua
Normal file
627
runtime/lua/_vim9script.lua
Normal file
@ -0,0 +1,627 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- This file is auto generated by vim9jit. Do not edit by hand.
|
||||
-- All content is in the source repository.
|
||||
-- Bugs should be reported to: github.com/tjdevries/vim9jit
|
||||
--
|
||||
-- In addition, this file is considered "private" by neovim. You should
|
||||
-- not expect any of the APIs, functions, etc to be stable. They are subject
|
||||
-- to change at any time.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local vim9 = (function()
|
||||
local M = {}
|
||||
|
||||
M.ternary = function(cond, if_true, if_false)
|
||||
if cond then
|
||||
if type(if_true) == 'function' then
|
||||
return if_true()
|
||||
else
|
||||
return if_true
|
||||
end
|
||||
else
|
||||
if type(if_false) == 'function' then
|
||||
return if_false()
|
||||
else
|
||||
return if_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
M.fn_mut = function(name, args, info)
|
||||
local result = vim.fn._Vim9ScriptFn(name, args)
|
||||
for idx, val in pairs(result[2]) do
|
||||
M.replace(args[idx], val)
|
||||
end
|
||||
|
||||
-- Substitute returning the reference to the
|
||||
-- returned value
|
||||
if info.replace then
|
||||
return args[info.replace + 1]
|
||||
end
|
||||
|
||||
return result[1]
|
||||
end
|
||||
|
||||
M.replace = function(orig, new)
|
||||
if type(orig) == 'table' and type(new) == 'table' then
|
||||
for k in pairs(orig) do
|
||||
orig[k] = nil
|
||||
end
|
||||
|
||||
for k, v in pairs(new) do
|
||||
orig[k] = v
|
||||
end
|
||||
|
||||
return orig
|
||||
end
|
||||
|
||||
return new
|
||||
end
|
||||
|
||||
M.index = function(obj, idx)
|
||||
if vim.tbl_islist(obj) then
|
||||
if idx < 0 then
|
||||
return obj[#obj + idx + 1]
|
||||
else
|
||||
return obj[idx + 1]
|
||||
end
|
||||
elseif type(obj) == 'table' then
|
||||
return obj[idx]
|
||||
elseif type(obj) == 'string' then
|
||||
return string.sub(obj, idx + 1, idx + 1)
|
||||
end
|
||||
|
||||
error('invalid type for indexing: ' .. vim.inspect(obj))
|
||||
end
|
||||
|
||||
M.index_expr = function(idx)
|
||||
if type(idx) == 'string' then
|
||||
return idx
|
||||
elseif type(idx) == 'number' then
|
||||
return idx + 1
|
||||
else
|
||||
error(string.format('not yet handled: %s', vim.inspect(idx)))
|
||||
end
|
||||
end
|
||||
|
||||
M.slice = function(obj, start, finish)
|
||||
if start == nil then
|
||||
start = 0
|
||||
end
|
||||
|
||||
if start < 0 then
|
||||
start = #obj + start
|
||||
end
|
||||
assert(type(start) == 'number')
|
||||
|
||||
if finish == nil then
|
||||
finish = #obj
|
||||
end
|
||||
|
||||
if finish < 0 then
|
||||
finish = #obj + finish
|
||||
end
|
||||
assert(type(finish) == 'number')
|
||||
|
||||
local slicer
|
||||
if vim.tbl_islist(obj) then
|
||||
slicer = vim.list_slice
|
||||
elseif type(obj) == 'string' then
|
||||
slicer = string.sub
|
||||
else
|
||||
error('invalid type for slicing: ' .. vim.inspect(obj))
|
||||
end
|
||||
|
||||
return slicer(obj, start + 1, finish + 1)
|
||||
end
|
||||
|
||||
-- Currently unused, but this could be used to embed vim9jit within a
|
||||
-- running nvim application and transpile "on the fly" as files are
|
||||
-- sourced. There would still need to be some work done to make that
|
||||
-- work correctly with imports and what not, but overall it could
|
||||
-- work well for calling ":source X" from within a vimscript/vim9script
|
||||
-- function
|
||||
M.make_source_cmd = function()
|
||||
local group = vim.api.nvim_create_augroup('vim9script-source', {})
|
||||
vim.api.nvim_create_autocmd('SourceCmd', {
|
||||
pattern = '*.vim',
|
||||
group = group,
|
||||
callback = function(a)
|
||||
local file = vim.fn.readfile(a.file)
|
||||
for _, line in ipairs(file) do
|
||||
-- TODO: Or starts with def <something>
|
||||
-- You can use def in legacy vim files
|
||||
if vim.startswith(line, 'vim9script') then
|
||||
-- TODO: Use the rust lib to actually
|
||||
-- generate the corresponding lua code and then
|
||||
-- execute that (instead of sourcing it directly)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_exec(table.concat(file, '\n'), false)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
M.iter = function(expr)
|
||||
if vim.tbl_islist(expr) then
|
||||
return ipairs(expr)
|
||||
else
|
||||
return pairs(expr)
|
||||
end
|
||||
end
|
||||
|
||||
M.ITER_DEFAULT = 0
|
||||
M.ITER_CONTINUE = 1
|
||||
M.ITER_BREAK = 2
|
||||
M.ITER_RETURN = 3
|
||||
|
||||
return M
|
||||
end)()
|
||||
|
||||
vim.cmd([[
|
||||
function! _Vim9ScriptFn(name, args) abort
|
||||
try
|
||||
let ret = function(a:name, a:args)()
|
||||
catch
|
||||
echo "Failed..."
|
||||
echo a:name
|
||||
echo a:args
|
||||
|
||||
throw v:errmsg
|
||||
endtry
|
||||
|
||||
return [ret, a:args]
|
||||
endfunction
|
||||
]])
|
||||
|
||||
vim9['autoload'] = (function()
|
||||
return function(path)
|
||||
return loadfile(path)()
|
||||
end
|
||||
end)()
|
||||
vim9['bool'] = (function()
|
||||
return function(...)
|
||||
return vim9.convert.to_vim_bool(...)
|
||||
end
|
||||
end)()
|
||||
vim9['convert'] = (function()
|
||||
local M = {}
|
||||
|
||||
M.decl_bool = function(val)
|
||||
if type(val) == 'boolean' then
|
||||
return val
|
||||
elseif type(val) == 'number' then
|
||||
if val == 0 then
|
||||
return false
|
||||
elseif val == 1 then
|
||||
return true
|
||||
else
|
||||
error(string.format('bad number passed to bool declaration: %s', val))
|
||||
end
|
||||
end
|
||||
|
||||
error(string.format('invalid bool declaration: %s', vim.inspect(val)))
|
||||
end
|
||||
|
||||
M.decl_dict = function(val)
|
||||
if type(val) == 'nil' then
|
||||
return vim.empty_dict()
|
||||
elseif type(val) == 'table' then
|
||||
if vim.tbl_isempty(val) then
|
||||
return vim.empty_dict()
|
||||
elseif vim.tbl_islist(val) then
|
||||
error(string.format('Cannot pass list to dictionary? %s', vim.inspect(val)))
|
||||
else
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
error(string.format('invalid dict declaration: %s', vim.inspect(val)))
|
||||
end
|
||||
|
||||
M.to_vim_bool = function(val)
|
||||
if type(val) == 'boolean' then
|
||||
return val
|
||||
elseif type(val) == 'number' then
|
||||
return val ~= 0
|
||||
elseif type(val) == 'string' then
|
||||
return string.len(val) ~= 0
|
||||
elseif type(val) == 'table' then
|
||||
return not vim.tbl_isempty(val)
|
||||
elseif val == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
error('unhandled type: ' .. vim.inspect(val))
|
||||
end
|
||||
|
||||
return M
|
||||
end)()
|
||||
vim9['fn'] = (function()
|
||||
local M = {}
|
||||
|
||||
M.insert = function(list, item, idx)
|
||||
if idx == nil then
|
||||
idx = 1
|
||||
end
|
||||
|
||||
table.insert(list, idx + 1, item)
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
M.extend = function(left, right, expr3)
|
||||
if expr3 ~= nil then
|
||||
error("haven't written this code yet")
|
||||
end
|
||||
|
||||
if vim.tbl_islist(right) then
|
||||
vim.list_extend(left, right)
|
||||
return left
|
||||
else
|
||||
-- local result = vim.tbl_extend(left, right)
|
||||
for k, v in pairs(right) do
|
||||
left[k] = v
|
||||
end
|
||||
|
||||
return left
|
||||
end
|
||||
end
|
||||
|
||||
M.add = function(list, item)
|
||||
table.insert(list, item)
|
||||
return list
|
||||
end
|
||||
|
||||
M.has_key = function(obj, key)
|
||||
return not not obj[key]
|
||||
end
|
||||
|
||||
M.prop_type_add = function(...)
|
||||
local args = { ... }
|
||||
print('[prop_type_add]', vim.inspect(args))
|
||||
end
|
||||
|
||||
do
|
||||
local has_overrides = {
|
||||
-- We do have vim9script ;) that's this plugin
|
||||
['vim9script'] = true,
|
||||
|
||||
-- Include some vim patches that are sometimes required by variuos vim9script plugins
|
||||
-- that we implement via vim9jit
|
||||
[ [[patch-8.2.2261]] ] = true,
|
||||
[ [[patch-8.2.4257]] ] = true,
|
||||
}
|
||||
|
||||
M.has = function(patch)
|
||||
if has_overrides[patch] then
|
||||
return true
|
||||
end
|
||||
|
||||
return vim.fn.has(patch)
|
||||
end
|
||||
end
|
||||
|
||||
--[=[
|
||||
Currently missing patch, can be removed in the future.
|
||||
|
||||
readdirex({directory} [, {expr} [, {dict}]]) *readdirex()*
|
||||
Extended version of |readdir()|.
|
||||
Return a list of Dictionaries with file and directory
|
||||
information in {directory}.
|
||||
This is useful if you want to get the attributes of file and
|
||||
directory at the same time as getting a list of a directory.
|
||||
This is much faster than calling |readdir()| then calling
|
||||
|getfperm()|, |getfsize()|, |getftime()| and |getftype()| for
|
||||
each file and directory especially on MS-Windows.
|
||||
The list will by default be sorted by name (case sensitive),
|
||||
the sorting can be changed by using the optional {dict}
|
||||
argument, see |readdir()|.
|
||||
|
||||
The Dictionary for file and directory information has the
|
||||
following items:
|
||||
group Group name of the entry. (Only on Unix)
|
||||
name Name of the entry.
|
||||
perm Permissions of the entry. See |getfperm()|.
|
||||
size Size of the entry. See |getfsize()|.
|
||||
time Timestamp of the entry. See |getftime()|.
|
||||
type Type of the entry.
|
||||
On Unix, almost same as |getftype()| except:
|
||||
Symlink to a dir "linkd"
|
||||
Other symlink "link"
|
||||
On MS-Windows:
|
||||
Normal file "file"
|
||||
Directory "dir"
|
||||
Junction "junction"
|
||||
Symlink to a dir "linkd"
|
||||
Other symlink "link"
|
||||
Other reparse point "reparse"
|
||||
user User name of the entry's owner. (Only on Unix)
|
||||
On Unix, if the entry is a symlink, the Dictionary includes
|
||||
the information of the target (except the "type" item).
|
||||
On MS-Windows, it includes the information of the symlink
|
||||
itself because of performance reasons.
|
||||
--]=]
|
||||
M.readdirex = function(dir)
|
||||
local files = vim.fn.readdir(dir)
|
||||
local direx = {}
|
||||
for _, f in ipairs(files) do
|
||||
table.insert(direx, {
|
||||
name = f,
|
||||
type = vim.fn.getftype(f),
|
||||
})
|
||||
end
|
||||
|
||||
return direx
|
||||
end
|
||||
|
||||
M.mapnew = function(tbl, expr)
|
||||
return vim.fn.map(tbl, expr)
|
||||
end
|
||||
|
||||
M.typename = function(val)
|
||||
local ty = type(val)
|
||||
if ty == 'string' then
|
||||
return 'string'
|
||||
elseif ty == 'boolean' then
|
||||
return 'bool'
|
||||
elseif ty == 'number' then
|
||||
return 'number'
|
||||
else
|
||||
error(string.format('typename: %s', val))
|
||||
end
|
||||
end
|
||||
|
||||
-- Popup menu stuff: Could be rolled into other plugin later
|
||||
-- but currently is here for testing purposes (and implements
|
||||
-- some very simple compat layers at the moment)
|
||||
do
|
||||
local pos_map = {
|
||||
topleft = 'NW',
|
||||
topright = 'NE',
|
||||
botleft = 'SW',
|
||||
botright = 'SE',
|
||||
}
|
||||
|
||||
M.popup_menu = function(_, options)
|
||||
-- print "OPTIONS:"
|
||||
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
local win = vim.api.nvim_open_win(buf, true, {
|
||||
relative = 'editor',
|
||||
style = 'minimal',
|
||||
anchor = pos_map[options.pos],
|
||||
height = options.maxheight or options.minheight,
|
||||
width = options.maxwidth or options.minwidth,
|
||||
row = options.line,
|
||||
col = options.col,
|
||||
})
|
||||
|
||||
if options.filter then
|
||||
local loop
|
||||
loop = function()
|
||||
vim.cmd([[redraw!]])
|
||||
local ok, ch = pcall(vim.fn.getcharstr)
|
||||
if not ok then
|
||||
return
|
||||
end -- interrupted
|
||||
|
||||
if ch == '<C-C>' then
|
||||
return
|
||||
end
|
||||
|
||||
if not require('vim9script').bool(options.filter(nil, ch)) then
|
||||
vim.cmd.normal(ch)
|
||||
end
|
||||
|
||||
vim.schedule(loop)
|
||||
end
|
||||
|
||||
vim.schedule(loop)
|
||||
end
|
||||
|
||||
return win
|
||||
end
|
||||
|
||||
M.popup_settext = function(id, text)
|
||||
if type(text) == 'string' then
|
||||
-- text = vim.split(text, "\n")
|
||||
error("Haven't handled string yet")
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
for _, obj in ipairs(text) do
|
||||
table.insert(lines, obj.text)
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_lines(vim.api.nvim_win_get_buf(id), 0, -1, false, lines)
|
||||
end
|
||||
|
||||
M.popup_filter_menu = function()
|
||||
print('ok, just pretend we filtered the menu')
|
||||
end
|
||||
|
||||
M.popup_setoptions = function(id, _)
|
||||
print('setting options...', id)
|
||||
end
|
||||
end
|
||||
|
||||
M = setmetatable(M, {
|
||||
__index = vim.fn,
|
||||
})
|
||||
|
||||
return M
|
||||
end)()
|
||||
vim9['heredoc'] = (function()
|
||||
local M = {}
|
||||
|
||||
M.trim = function(lines)
|
||||
local min_whitespace = 9999
|
||||
for _, line in ipairs(lines) do
|
||||
local _, finish = string.find(line, '^%s*')
|
||||
min_whitespace = math.min(min_whitespace, finish)
|
||||
end
|
||||
|
||||
local trimmed_lines = {}
|
||||
for _, line in ipairs(lines) do
|
||||
table.insert(trimmed_lines, string.sub(line, min_whitespace + 1))
|
||||
end
|
||||
|
||||
return trimmed_lines
|
||||
end
|
||||
|
||||
return M
|
||||
end)()
|
||||
vim9['import'] = (function()
|
||||
local imported = {}
|
||||
imported.autoload = setmetatable({}, {
|
||||
__index = function(_, name)
|
||||
local luaname = 'autoload/' .. string.gsub(name, '%.vim$', '.lua')
|
||||
local runtime_file = vim.api.nvim_get_runtime_file(luaname, false)[1]
|
||||
if not runtime_file then
|
||||
error('unable to find autoload file:' .. name)
|
||||
end
|
||||
|
||||
return imported.absolute[vim.fn.fnamemodify(runtime_file, ':p')]
|
||||
end,
|
||||
})
|
||||
|
||||
imported.absolute = setmetatable({}, {
|
||||
__index = function(self, name)
|
||||
if vim.loop.fs_stat(name) then
|
||||
local result = loadfile(name)()
|
||||
rawset(self, name, result)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
error(string.format('unabled to find absolute file: %s', name))
|
||||
end,
|
||||
})
|
||||
|
||||
return function(info)
|
||||
local name = info.name
|
||||
|
||||
if info.autoload then
|
||||
return imported.autoload[info.name]
|
||||
end
|
||||
|
||||
local debug_info = debug.getinfo(2, 'S')
|
||||
local sourcing_path = vim.fn.fnamemodify(string.sub(debug_info.source, 2), ':p')
|
||||
|
||||
-- Relative paths
|
||||
if vim.startswith(name, '../') or vim.startswith(name, './') then
|
||||
local luaname = string.gsub(name, '%.vim$', '.lua')
|
||||
local directory = vim.fn.fnamemodify(sourcing_path, ':h')
|
||||
local search = directory .. '/' .. luaname
|
||||
return imported.absolute[search]
|
||||
end
|
||||
|
||||
if vim.startswith(name, '/') then
|
||||
error('absolute path')
|
||||
-- local luaname = string.gsub(name, "%.vim", ".lua")
|
||||
-- local runtime_file = vim.api.nvim_get_runtime_file(luaname, false)[1]
|
||||
-- if runtime_file then
|
||||
-- runtime_file = vim.fn.fnamemodify(runtime_file, ":p")
|
||||
-- return loadfile(runtime_file)()
|
||||
-- end
|
||||
end
|
||||
|
||||
error('Unhandled case' .. vim.inspect(info) .. vim.inspect(debug_info))
|
||||
end
|
||||
end)()
|
||||
vim9['ops'] = (function()
|
||||
local lib = vim9
|
||||
|
||||
local M = {}
|
||||
|
||||
M['And'] = function(left, right)
|
||||
return lib.bool(left) and lib.bool(right)
|
||||
end
|
||||
|
||||
M['Or'] = function(left, right)
|
||||
return lib.bool(left) or lib.bool(right)
|
||||
end
|
||||
|
||||
M['Plus'] = function(left, right)
|
||||
return left + right
|
||||
end
|
||||
|
||||
M['Multiply'] = function(left, right)
|
||||
return left * right
|
||||
end
|
||||
|
||||
M['Divide'] = function(left, right)
|
||||
return left / right
|
||||
end
|
||||
|
||||
M['StringConcat'] = function(left, right)
|
||||
return left .. right
|
||||
end
|
||||
|
||||
M['EqualTo'] = function(left, right)
|
||||
return left == right
|
||||
end
|
||||
|
||||
M['NotEqualTo'] = function(left, right)
|
||||
return not M['EqualTo'](left, right)
|
||||
end
|
||||
|
||||
M['LessThan'] = function(left, right)
|
||||
return left < right
|
||||
end
|
||||
|
||||
M['LessThanOrEqual'] = function(left, right)
|
||||
return left <= right
|
||||
end
|
||||
|
||||
M['GreaterThan'] = function(left, right)
|
||||
return left > right
|
||||
end
|
||||
|
||||
M['GreaterThanOrEqual'] = function(left, right)
|
||||
return left >= right
|
||||
end
|
||||
|
||||
M['RegexpMatches'] = function(left, right)
|
||||
return not not vim.regex(right):match_str(left)
|
||||
end
|
||||
|
||||
M['RegexpMatchesIns'] = function(left, right)
|
||||
return not not vim.regex('\\c' .. right):match_str(left)
|
||||
end
|
||||
|
||||
M['NotRegexpMatches'] = function(left, right)
|
||||
return not M['RegexpMatches'](left, right)
|
||||
end
|
||||
|
||||
M['Modulo'] = function(left, right)
|
||||
return left % right
|
||||
end
|
||||
|
||||
M['Minus'] = function(left, right)
|
||||
-- TODO: This is not right :)
|
||||
return left - right
|
||||
end
|
||||
|
||||
return M
|
||||
end)()
|
||||
vim9['prefix'] = (function()
|
||||
local lib = vim9
|
||||
|
||||
local M = {}
|
||||
|
||||
M['Minus'] = function(right)
|
||||
return -right
|
||||
end
|
||||
|
||||
M['Bang'] = function(right)
|
||||
return not lib.bool(right)
|
||||
end
|
||||
|
||||
return M
|
||||
end)()
|
||||
|
||||
return vim9
|
61
test/functional/plugin/ccomplete_spec.lua
Normal file
61
test/functional/plugin/ccomplete_spec.lua
Normal file
@ -0,0 +1,61 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local clear = helpers.clear
|
||||
local command = helpers.command
|
||||
local eq = helpers.eq
|
||||
local eval = helpers.eval
|
||||
local feed = helpers.feed
|
||||
local write_file = helpers.write_file
|
||||
|
||||
describe('ccomplete#Complete', function()
|
||||
setup(function()
|
||||
-- Realistic tags generated from neovim source tree using `ctags -R *`
|
||||
write_file(
|
||||
'Xtags',
|
||||
[[
|
||||
augroup_del src/nvim/autocmd.c /^void augroup_del(char *name, bool stupid_legacy_mode)$/;" f typeref:typename:void
|
||||
augroup_exists src/nvim/autocmd.c /^bool augroup_exists(const char *name)$/;" f typeref:typename:bool
|
||||
augroup_find src/nvim/autocmd.c /^int augroup_find(const char *name)$/;" f typeref:typename:int
|
||||
aupat_get_buflocal_nr src/nvim/autocmd.c /^int aupat_get_buflocal_nr(char *pat, int patlen)$/;" f typeref:typename:int
|
||||
aupat_is_buflocal src/nvim/autocmd.c /^bool aupat_is_buflocal(char *pat, int patlen)$/;" f typeref:typename:bool
|
||||
expand_get_augroup_name src/nvim/autocmd.c /^char *expand_get_augroup_name(expand_T *xp, int idx)$/;" f typeref:typename:char *
|
||||
expand_get_event_name src/nvim/autocmd.c /^char *expand_get_event_name(expand_T *xp, int idx)$/;" f typeref:typename:char *
|
||||
]]
|
||||
)
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
command('set tags=Xtags')
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
os.remove('Xtags')
|
||||
end)
|
||||
|
||||
it('can complete from Xtags', function()
|
||||
local completed = eval('ccomplete#Complete(0, "a")')
|
||||
eq(5, #completed)
|
||||
eq('augroup_del(', completed[1].word)
|
||||
eq('f', completed[1].kind)
|
||||
|
||||
local aupat = eval('ccomplete#Complete(0, "aupat")')
|
||||
eq(2, #aupat)
|
||||
eq('aupat_get_buflocal_nr(', aupat[1].word)
|
||||
eq('f', aupat[1].kind)
|
||||
end)
|
||||
|
||||
it('does not error when returning no matches', function()
|
||||
local completed = eval('ccomplete#Complete(0, "doesnotmatch")')
|
||||
eq({}, completed)
|
||||
end)
|
||||
|
||||
it('can find the beginning of a word for C', function()
|
||||
command('set filetype=c')
|
||||
feed('i int something = augroup')
|
||||
local result = eval('ccomplete#Complete(1, "")')
|
||||
eq(#' int something = ', result)
|
||||
|
||||
local completed = eval('ccomplete#Complete(0, "augroup")')
|
||||
eq(3, #completed)
|
||||
end)
|
||||
end)
|
Loading…
Reference in New Issue
Block a user