summaryrefslogtreecommitdiff
path: root/uvim/runtime/autoload/ccomplete.vim
diff options
context:
space:
mode:
Diffstat (limited to 'uvim/runtime/autoload/ccomplete.vim')
-rw-r--r--uvim/runtime/autoload/ccomplete.vim743
1 files changed, 0 insertions, 743 deletions
diff --git a/uvim/runtime/autoload/ccomplete.vim b/uvim/runtime/autoload/ccomplete.vim
deleted file mode 100644
index 51237be98b..0000000000
--- a/uvim/runtime/autoload/ccomplete.vim
+++ /dev/null
@@ -1,743 +0,0 @@
-vim9script noclear
-
-# Vim completion script
-# Language: C
-# Maintainer: The Vim Project <https://github.com/vim/vim>
-# Last Change: 2026 Feb 18
-# Rewritten in Vim9 script by github user lacygoill
-# Former Maintainer: Bram Moolenaar <Bram@vim.org>
-
-var prepended: string
-var grepCache: dict<list<dict<any>>>
-
-# This function is used for the 'omnifunc' option.
-export def Complete(findstart: bool, abase: string): any # {{{1
- if findstart
- # Locate the start of the item, including ".", "->" and "[...]".
- var line: string = getline('.')
- var start: number = charcol('.') - 1
- var lastword: number = -1
- while start > 0
- if line[start - 1] =~ '\w'
- --start
- elseif line[start - 1] =~ '\.'
- if lastword == -1
- lastword = start
- endif
- --start
- elseif start > 1 && line[start - 2] == '-'
- && line[start - 1] == '>'
- if lastword == -1
- lastword = start
- endif
- start -= 2
- elseif line[start - 1] == ']'
- # Skip over [...].
- var n: number = 0
- --start
- while start > 0
- --start
- if line[start] == '['
- if n == 0
- break
- endif
- --n
- elseif line[start] == ']' # nested []
- ++n
- 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 prepended.
- if lastword == -1
- prepended = ''
- return byteidx(line, start)
- endif
- prepended = line[start : lastword - 1]
- return byteidx(line, lastword)
- endif
-
- # Return list of matches.
-
- var base: string = prepended .. abase
-
- # 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
- 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.
- var items: list<string>
- var s: number = 0
- var arrays: number = 0
- while 1
- var e: number = base->charidx(match(base, '\.\|->\|\[', s))
- if e < 0
- if s == 0 || base[s - 1] != ']'
- items->add(base[s :])
- endif
- break
- endif
- if s == 0 || base[s - 1] != ']'
- items->add(base[s : e - 1])
- endif
- if base[e] == '.'
- # skip over '.'
- s = e + 1
- elseif base[e] == '-'
- # skip over '->'
- s = e + 2
- else
- # Skip over [...].
- var n: number = 0
- s = e
- ++e
- while e < strcharlen(base)
- if base[e] == ']'
- if n == 0
- break
- endif
- --n
- elseif base[e] == '[' # nested [...]
- ++n
- endif
- ++e
- endwhile
- ++e
- items->add(base[s : e - 1])
- ++arrays
- s = e
- endif
- endwhile
-
- if complete_check()
- return v:none
- endif
-
- # 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")
- var res: list<dict<any>>
- if items[0]->searchdecl(false, true) == 0
- # Found, now figure out the type.
- # TODO: join previous line if it makes sense
- var line: string = getline('.')
- var col: number = charcol('.')
- if line[: col - 1]->stridx(';') >= 0
- # Handle multiple declarations on the same line.
- var col2: number = col - 1
- while line[col2] != ';'
- if complete_check()
- return res
- endif
- --col2
- endwhile
- line = line[col2 + 1 :]
- col -= col2
- endif
- if line[: col - 1]->stridx(',') >= 0
- # Handle multiple declarations on the same line in a function
- # declaration.
- var col2: number = col - 1
- while line[col2] != ','
- if complete_check()
- return res
- endif
- --col2
- endwhile
- if line[col2 + 1 : col - 1] =~ ' *[^ ][^ ]* *[^ ]'
- line = line[col2 + 1 :]
- col -= col2
- endif
- endif
- if len(items) == 1
- # Completing one word and it's a local variable: May add '[', '.' or
- # '->'.
- var match: string = items[0]
- var kind: string = 'v'
- if match(line, '\<' .. match .. '\s*\[') > 0
- match ..= '['
- else
- res = line[: col - 1]->Nextitem([''], 0, true)
- if len(res) > 0
- # There are members, thus add "." or "->".
- if match(line, '\*[ \t(]*' .. match .. '\>') > 0
- match ..= '->'
- else
- match ..= '.'
- endif
- endif
- endif
- 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
- var match: string = items[0]
- var kind: string = 'v'
- var tagline: string = "\t/^" .. line .. '$/'
- res = [{match: match, tagline: tagline, kind: kind, info: line}]
- else
- # Completing "var.", "var.something", etc.
- res = line[: col - 1]->Nextitem(items[1 :], 0, true)
- endif
- endif
-
- if len(items) == 1 || len(items) == arrays + 1
- # Only one part, no "." or "->": complete from tags file.
- var tags: list<dict<any>>
- if len(items) == 1
- tags = taglist('^' .. base)
- else
- tags = taglist('^' .. items[0] .. '$')
- endif
-
- tags
- # Remove members, these can't appear without something in front.
- ->filter((_, v: dict<any>): bool =>
- v->has_key('kind') ? v.kind != 'm' : true)
- # Remove static matches in other files.
- ->filter((_, v: dict<any>): bool =>
- !v->has_key('static')
- || !v['static']
- || bufnr('%') == bufnr(v['filename']))
-
- res = res->extend(tags->map((_, v: dict<any>) => Tag2item(v)))
- endif
-
- if len(res) == 0
- # Find the variable in the tags file(s)
- var diclist: list<dict<any>> = taglist('^' .. items[0] .. '$')
- # Remove members, these can't appear without something in front.
- ->filter((_, v: dict<any>): bool =>
- v->has_key('kind') ? v.kind != 'm' : true)
-
- res = []
- for i: number in len(diclist)->range()
- if complete_check()
- return res
- endif
- # New ctags has the "typeref" field. Patched version has "typename".
- if diclist[i]->has_key('typename')
- res = res->extend(diclist[i]['typename']->StructMembers(items[1 :], true))
- elseif diclist[i]->has_key('typeref')
- res = res->extend(diclist[i]['typeref']->StructMembers(items[1 :], true))
- 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'
- var line: string = diclist[i]['cmd']
- if line[: 1] == '/^'
- var col: number = line->charidx(match(line, '\<' .. items[0] .. '\>'))
- res = res->extend(line[2 : col - 1]->Nextitem(items[1 :], 0, true))
- endif
- endif
- endfor
- endif
-
- if len(res) == 0 && items[0]->searchdecl(true) == 0
- # Found, now figure out the type.
- # TODO: join previous line if it makes sense
- var line: string = getline('.')
- var col: number = charcol('.')
- res = line[: col - 1]->Nextitem(items[1 :], 0, true)
- endif
-
- # If the last item(s) are [...] they need to be added to the matches.
- var last: number = len(items) - 1
- var brackets: string = ''
- while last >= 0
- if complete_check()
- return res
- endif
- if items[last][0] != '['
- break
- endif
- brackets = items[last] .. brackets
- --last
- endwhile
-
- return res->map((_, v: dict<any>): dict<string> => Tagline2item(v, brackets))
-enddef
-
-def GetAddition( # {{{1
- line: string,
- match: string,
- memarg: list<dict<any>>,
- bracket: bool): string
- # Guess if the item is an array.
- if bracket && match(line, match .. '\s*\[') > 0
- return '['
- endif
-
- # Check if the item has members.
- if SearchMembers(memarg, [''], false)->len() > 0
- # If there is a '*' before the name use "->".
- if match(line, '\*[ \t(]*' .. match .. '\>') > 0
- return '->'
- else
- return '.'
- endif
- endif
- return ''
-enddef
-
-def Tag2item(val: dict<any>): dict<any> # {{{1
-# 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.
- var res: dict<any> = {match: val['name']}
-
- res['extra'] = Tagcmd2extra(val['cmd'], val['name'], val['filename'])
-
- var s: string = Dict2info(val)
- if s != ''
- res['info'] = s
- endif
-
- res['tagline'] = ''
- if val->has_key('kind')
- var kind: string = val['kind']
- res['kind'] = kind
- if kind == 'v'
- res['tagline'] = "\t" .. val['cmd']
- res['dict'] = val
- elseif kind == 'f'
- res['match'] = val['name'] .. '('
- endif
- endif
-
- return res
-enddef
-
-def Dict2info(dict: dict<any>): string # {{{1
-# Use all the items in dictionary for the "info" entry.
- var info: string = ''
- for k: string in dict->keys()->sort()
- if complete_check()
- return info
- endif
- info ..= k .. repeat(' ', 10 - strlen(k))
- if k == 'cmd'
- info ..= dict['cmd']
- ->matchstr('/^\s*\zs.*\ze$/')
- ->substitute('\\\(.\)', '\1', 'g')
- else
- var dictk: any = dict[k]
- if typename(dictk) != 'string'
- info ..= dictk->string()
- else
- info ..= dictk
- endif
- endif
- info ..= "\n"
- endfor
- return info
-enddef
-
-def ParseTagline(line: string): dict<any> # {{{1
-# Parse a tag line and return a dictionary with items like taglist()
- var l: list<string> = split(line, "\t")
- var d: dict<any>
- if len(l) >= 3
- d['name'] = l[0]
- d['filename'] = l[1]
- d['cmd'] = l[2]
- var n: number = 2
- if l[2] =~ '^/'
- # Find end of cmd, it may contain Tabs.
- while n < len(l) && l[n] !~ '/;"$'
- ++n
- d['cmd'] ..= ' ' .. l[n]
- endwhile
- endif
- for i: number in range(n + 1, len(l) - 1)
- if complete_check()
- return d
- endif
- if l[i] == 'file:'
- d['static'] = 1
- elseif l[i] !~ ':'
- d['kind'] = l[i]
- else
- d[l[i]->matchstr('[^:]*')] = l[i]->matchstr(':\zs.*')
- endif
- endfor
- endif
-
- return d
-enddef
-
-def Tagline2item(val: dict<any>, brackets: string): dict<string> # {{{1
-# 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.
- var line: string = val['tagline']
- var add: string = GetAddition(line, val['match'], [val], brackets == '')
- var res: dict<string> = {word: val['match'] .. brackets .. add}
-
- if val->has_key('info')
- # Use info from Tag2item().
- res['info'] = val['info']
- else
- # Parse the tag line and add each part to the "info" entry.
- var s: string = ParseTagline(line)->Dict2info()
- if s != ''
- res['info'] = s
- endif
- endif
-
- if val->has_key('kind')
- res['kind'] = val['kind']
- elseif add == '('
- res['kind'] = 'f'
- else
- var s: string = line->matchstr('\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
- if s != ''
- res['kind'] = s
- endif
- endif
-
- if val->has_key('extra')
- res['menu'] = val['extra']
- return res
- endif
-
- # Isolate the command after the tag and filename.
- var s: string = line->matchstr('[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
- if s != ''
- res['menu'] = s->Tagcmd2extra(val['match'], line->matchstr('[^\t]*\t\zs[^\t]*\ze\t'))
- endif
- return res
-enddef
-
-def Tagcmd2extra( # {{{1
- cmd: string,
- name: string,
- fname: string): string
-# Turn a command from a tag line to something that is useful in the menu
- var x: string
- if cmd =~ '^/^'
- # The command is a search command, useful to see what it is.
- x = cmd
- ->matchstr('^/^\s*\zs.*\ze$/')
- ->substitute('\<' .. name .. '\>', '@@', '')
- ->substitute('\\\(.\)', '\1', 'g')
- .. ' - ' .. fname
- elseif cmd =~ '^\d*$'
- # 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
- endif
- return x
-enddef
-
-def Nextitem( # {{{1
- lead: string,
- items: list<string>,
- depth: number,
- all: bool): list<dict<string>>
-# 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.
- var tokens: list<string> = split(lead, '\s\+\|\<')
-
- # Try to recognize the type of the variable. This is rough guessing...
- var res: list<dict<string>>
- for tidx: number in len(tokens)->range()
- if complete_check()
- return res
- endif
-
- # 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)
- res = StructMembers(tokens[tidx] .. ':' .. tokens[tidx + 1], items, all)
- break
- endif
-
- # TODO: add more reserved words
- if ['int', 'short', 'char', 'float',
- 'double', 'static', 'unsigned', 'extern']->index(tokens[tidx]) >= 0
- continue
- endif
-
- # Use the tags file to find out if this is a typedef or struct
- var diclist: list<dict<any>> = taglist('^' .. tokens[tidx] .. '$')
- for tagidx: number in len(diclist)->range()
-
- if complete_check()
- return res
- endif
-
- var item: dict<any> = diclist[tagidx]
-
- # New ctags has the "typeref" field. Patched version has "typename".
- if item->has_key('typeref')
- res = res->extend(item['typeref']->StructMembers(items, all))
- continue
- endif
- if item->has_key('typename')
- res = res->extend(item['typename']->StructMembers(items, all))
- continue
- endif
-
- # handle struct
- if item['kind'] == 's'
- res = StructMembers('struct:' .. tokens[tidx], items, all)
- break
- endif
-
-
- # Only handle typedefs here.
- if item['kind'] != 't'
- continue
- endif
-
- # Skip matches local to another file.
- if item->has_key('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.
- var cmd: string = item['cmd']
- var ei: number = cmd->charidx(matchend(cmd, 'typedef\s\+'))
- if ei > 1
- var cmdtokens: list<string> = cmd[ei :]->split('\s\+\|\<')
- if len(cmdtokens) > 1
- if cmdtokens[0] == 'struct'
- || cmdtokens[0] == 'union'
- || cmdtokens[0] == 'class'
- var name: string = ''
- # Use the first identifier after the "struct" or "union"
- for ti: number in (len(cmdtokens) - 1)->range()
- if cmdtokens[ti] =~ '^\w'
- name = cmdtokens[ti]
- break
- endif
- endfor
- if name != ''
- res = res->extend(StructMembers(cmdtokens[0] .. ':' .. name, items, all))
- endif
- elseif depth < 10
- # Could be "typedef other_T some_T".
- res = res->extend(cmdtokens[0]->Nextitem(items, depth + 1, all))
- endif
- endif
- endif
- endfor
- if len(res) > 0
- break
- endif
- endfor
-
- return res
-enddef
-
-def StructMembers( # {{{1
- atypename: string,
- items: list<string>,
- all: bool): list<dict<string>>
-
-# 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?
- var fnames: string = tagfiles()
- ->map((_, v: string) => escape(v, ' \#%'))
- ->join()
- if fnames == ''
- return []
- endif
-
- var typename: string = atypename
- var qflist: list<dict<any>>
- var cached: number = 0
- var n: string
- if !all
- n = '1' # stop at first found match
- if grepCache->has_key(typename)
- qflist = grepCache[typename]
- cached = 1
- endif
- else
- n = ''
- endif
- if !cached
- while 1
- if complete_check()
- return []
- endif
- execute 'silent! keepjumps noautocmd '
- .. n .. 'vimgrep ' .. '/\t' .. typename .. '\(\t\|$\)/j '
- .. fnames
-
- qflist = getqflist()
- if len(qflist) > 0 || match(typename, '::') < 0
- break
- endif
- # No match for "struct:context::name", remove "context::" and try again.
- typename = typename->substitute(':[^:]*::', ':', '')
- endwhile
-
- if !all
- # Store the result to be able to use it again later.
- grepCache[typename] = qflist
- endif
- endif
-
- # Skip over [...] items
- var idx: number = 0
- var target: string
- while 1
- if complete_check()
- return []
- endif
- if idx >= len(items)
- target = '' # No further items, matching all members
- break
- endif
- if items[idx][0] != '['
- target = items[idx]
- break
- endif
- ++idx
- endwhile
- # Put matching members in matches[].
- var matches: list<dict<string>>
- for l: dict<any> in qflist
- var memb: string = l['text']->matchstr('[^\t]*')
- if memb =~ '^' .. target
- # Skip matches local to another file.
- if match(l['text'], "\tfile:") < 0
- || bufnr('%') == l['text']->matchstr('\t\zs[^\t]*')->bufnr()
- var item: dict<string> = {match: memb, tagline: l['text']}
-
- # Add the kind of item.
- var s: string = l['text']->matchstr('\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
- if s != ''
- item['kind'] = s
- if s == 'f'
- item['match'] = memb .. '('
- endif
- endif
-
- matches->add(item)
- endif
- endif
- endfor
-
- if len(matches) > 0
- # Skip over next [...] items
- ++idx
- while 1
- if complete_check()
- return matches
- endif
- if idx >= len(items)
- return matches # No further items, return the result.
- endif
- if items[idx][0] != '['
- break
- endif
- ++idx
- endwhile
-
- # More items following. For each of the possible members find the
- # matching following members.
- return SearchMembers(matches, items[idx :], all)
- endif
-
- # Failed to find anything.
- return []
-enddef
-
-def SearchMembers( # {{{1
- matches: list<dict<any>>,
- items: list<string>,
- all: bool): list<dict<string>>
-
-# For matching members, find matches for following items.
-# When "all" is true find all, otherwise just return 1 if there is any member.
- var res: list<dict<string>>
- for i: number in len(matches)->range()
- if complete_check()
- return res
- endif
- var typename: string = ''
- var line: string
- if matches[i]->has_key('dict')
- if matches[i]['dict']->has_key('typename')
- typename = matches[i]['dict']['typename']
- elseif matches[i]['dict']->has_key('typeref')
- typename = matches[i]['dict']['typeref']
- endif
- line = "\t" .. matches[i]['dict']['cmd']
- else
- line = matches[i]['tagline']
- var eb: number = matchend(line, '\ttypename:')
- var e: number = charidx(line, eb)
- if e < 0
- eb = matchend(line, '\ttyperef:')
- e = charidx(line, eb)
- endif
- if e > 0
- # Use typename field
- typename = line->matchstr('[^\t]*', eb)
- endif
- endif
-
- if typename != ''
- res = res->extend(StructMembers(typename, items, all))
- else
- # Use the search command (the declaration itself).
- var sb: number = line->match('\t\zs/^')
- var s: number = charidx(line, sb)
- if s > 0
- var e: number = line
- ->charidx(match(line, '\<' .. matches[i]['match'] .. '\>', sb))
- if e > 0
- res = res->extend(line[s : e - 1]->Nextitem(items, 0, all))
- endif
- endif
- endif
- if !all && len(res) > 0
- break
- endif
- endfor
- return res
-enddef
-#}}}1
-
-# vim: et sw=2 sts=2