Mercurial > vim
view runtime/autoload/ccomplete.vim @ 34359:0447bf3a88a5 v9.1.0110
patch 9.1.0110: filetype: add 'Config.in' filetype detection
Commit: https://github.com/vim/vim/commit/5f20f050efed3431beaf85739f0113e9ef0abd8e
Author: Brandon Maier <brandon.maier@collins.com>
Date: Wed Feb 14 22:30:06 2024 +0100
patch 9.1.0110: filetype: add 'Config.in' filetype detection
The 'Config.in' file type is for Buildroot configuration files.
Buildroot Config.in files use the same Kconfig backend as the Linux
kernel's Kconfig files.
Buildroot also has other filename variants that follow "Config.in.*",
they are used to distinguish multiple Config.in files in the same
directory.
See https://buildroot.org/downloads/manual/manual.html#_literal_config_in_literal_file
closes: #14038
Signed-off-by: Brandon Maier <brandon.maier@collins.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Wed, 14 Feb 2024 22:45:02 +0100 |
parents | 4027cefc2aab |
children | 9dfa17769328 |
line wrap: on
line source
vim9script noclear # Vim completion script # Language: C # Maintainer: The Vim Project <https://github.com/vim/vim> # Last Change: 2023 Aug 10 # 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 # 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] != ';' --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] != ',' --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<string>): bool => v->has_key('kind') ? v.kind != 'm' : true) res = [] for i: number in len(diclist)->range() # 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 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() 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 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() # 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. var diclist: list<dict<any>> = taglist('^' .. tokens[tidx] .. '$') for tagidx: number in len(diclist)->range() 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 # 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 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 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 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() 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: noet sw=2 sts=2