blob: d8675faf6ba22c9e561b31cd9cedb49561a1222d [file] [log] [blame]
Bram Moolenaara4d131d2021-12-27 21:33:07 +00001vim9script noclear
Bram Moolenaare344bea2005-09-01 20:46:49 +00002
Bram Moolenaara4d131d2021-12-27 21:33:07 +00003# Vim completion script
4# Language: C
5# Maintainer: Bram Moolenaar <Bram@vim.org>
6# Rewritten in Vim9 script by github user lacygoill
7# Last Change: 2021 Dec 27
Bram Moolenaara4a08382005-09-09 19:52:02 +00008
Bram Moolenaara4d131d2021-12-27 21:33:07 +00009var prepended: string
10var grepCache: dict<list<dict<any>>>
11
12# This function is used for the 'omnifunc' option.
13def ccomplete#Complete(findstart: bool, abase: string): any # {{{1
14 if findstart
15 # Locate the start of the item, including ".", "->" and "[...]".
16 var line: string = getline('.')
17 var start: number = charcol('.') - 1
18 var lastword: number = -1
Bram Moolenaare344bea2005-09-01 20:46:49 +000019 while start > 0
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000020 if line[start - 1] =~ '\w'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000021 --start
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000022 elseif line[start - 1] =~ '\.'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000023 if lastword == -1
24 lastword = start
25 endif
26 --start
27 elseif start > 1 && line[start - 2] == '-'
28 && line[start - 1] == '>'
29 if lastword == -1
30 lastword = start
31 endif
32 start -= 2
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000033 elseif line[start - 1] == ']'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000034 # Skip over [...].
35 var n: number = 0
36 --start
37 while start > 0
38 --start
39 if line[start] == '['
40 if n == 0
41 break
42 endif
43 --n
44 elseif line[start] == ']' # nested []
45 ++n
46 endif
47 endwhile
Bram Moolenaare344bea2005-09-01 20:46:49 +000048 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +000049 break
Bram Moolenaare344bea2005-09-01 20:46:49 +000050 endif
51 endwhile
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000052
Bram Moolenaara4d131d2021-12-27 21:33:07 +000053 # Return the column of the last word, which is going to be changed.
54 # Remember the text that comes before it in prepended.
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000055 if lastword == -1
Bram Moolenaara4d131d2021-12-27 21:33:07 +000056 prepended = ''
57 return byteidx(line, start)
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000058 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +000059 prepended = line[start : lastword - 1]
60 return byteidx(line, lastword)
Bram Moolenaare344bea2005-09-01 20:46:49 +000061 endif
62
Bram Moolenaara4d131d2021-12-27 21:33:07 +000063 # Return list of matches.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000064
Bram Moolenaara4d131d2021-12-27 21:33:07 +000065 var base: string = prepended .. abase
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000066
Bram Moolenaara4d131d2021-12-27 21:33:07 +000067 # Don't do anything for an empty base, would result in all the tags in the
68 # tags file.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000069 if base == ''
70 return []
71 endif
72
Bram Moolenaara4d131d2021-12-27 21:33:07 +000073 # init cache for vimgrep to empty
74 grepCache = {}
Bram Moolenaar1056d982006-03-09 22:37:52 +000075
Bram Moolenaara4d131d2021-12-27 21:33:07 +000076 # Split item in words, keep empty word after "." or "->".
77 # "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
78 # We can't use split, because we need to skip nested [...].
79 # "aa[...]" -> ['aa', '[...]'], "aa.bb[...]" -> ['aa', 'bb', '[...]'], etc.
80 var items: list<string>
81 var s: number = 0
82 var arrays: number = 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000083 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +000084 var e: number = base->charidx(match(base, '\.\|->\|\[', s))
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000085 if e < 0
86 if s == 0 || base[s - 1] != ']'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000087 items->add(base[s :])
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000088 endif
89 break
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000090 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000091 if s == 0 || base[s - 1] != ']'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000092 items->add(base[s : e - 1])
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000093 endif
94 if base[e] == '.'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000095 # skip over '.'
96 s = e + 1
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000097 elseif base[e] == '-'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000098 # skip over '->'
99 s = e + 2
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000100 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000101 # Skip over [...].
102 var n: number = 0
103 s = e
104 ++e
105 while e < strcharlen(base)
106 if base[e] == ']'
107 if n == 0
108 break
109 endif
110 --n
111 elseif base[e] == '[' # nested [...]
112 ++n
113 endif
114 ++e
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000115 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000116 ++e
117 items->add(base[s : e - 1])
118 ++arrays
119 s = e
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000120 endif
121 endwhile
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000122
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000123 # Find the variable items[0].
124 # 1. in current function (like with "gd")
125 # 2. in tags file(s) (like with ":tag")
126 # 3. in current file (like with "gD")
127 var res: list<dict<any>>
128 if items[0]->searchdecl(false, true) == 0
129 # Found, now figure out the type.
130 # TODO: join previous line if it makes sense
131 var line: string = getline('.')
132 var col: number = charcol('.')
133 if line[: col - 1]->stridx(';') >= 0
134 # Handle multiple declarations on the same line.
135 var col2: number = col - 1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000136 while line[col2] != ';'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000137 --col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000138 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000139 line = line[col2 + 1 :]
140 col -= col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000141 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000142 if line[: col - 1]->stridx(',') >= 0
143 # Handle multiple declarations on the same line in a function
144 # declaration.
145 var col2: number = col - 1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000146 while line[col2] != ','
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000147 --col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000148 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000149 if line[col2 + 1 : col - 1] =~ ' *[^ ][^ ]* *[^ ]'
150 line = line[col2 + 1 :]
151 col -= col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000152 endif
153 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000154 if len(items) == 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000155 # Completing one word and it's a local variable: May add '[', '.' or
156 # '->'.
157 var match: string = items[0]
158 var kind: string = 'v'
159 if match(line, '\<' .. match .. '\s*\[') > 0
160 match ..= '['
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000161 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000162 res = line[: col - 1]->Nextitem([''], 0, true)
163 if len(res) > 0
164 # There are members, thus add "." or "->".
165 if match(line, '\*[ \t(]*' .. match .. '\>') > 0
166 match ..= '->'
167 else
168 match ..= '.'
169 endif
170 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000171 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000172 res = [{match: match, tagline: '', kind: kind, info: line}]
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200173 elseif len(items) == arrays + 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000174 # Completing one word and it's a local array variable: build tagline
175 # from declaration line
176 var match: string = items[0]
177 var kind: string = 'v'
178 var tagline: string = "\t/^" .. line .. '$/'
179 res = [{match: match, tagline: tagline, kind: kind, info: line}]
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000180 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000181 # Completing "var.", "var.something", etc.
182 res = line[: col - 1]->Nextitem(items[1 :], 0, true)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000183 endif
184 endif
185
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200186 if len(items) == 1 || len(items) == arrays + 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000187 # Only one part, no "." or "->": complete from tags file.
188 var tags: list<dict<any>>
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200189 if len(items) == 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000190 tags = taglist('^' .. base)
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200191 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000192 tags = taglist('^' .. items[0] .. '$')
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200193 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000194
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000195 tags
196 # Remove members, these can't appear without something in front.
197 ->filter((_, v: dict<any>): bool =>
198 v->has_key('kind') ? v.kind != 'm' : true)
199 # Remove static matches in other files.
200 ->filter((_, v: dict<any>): bool =>
201 !v->has_key('static')
202 || !v['static']
203 || bufnr('%') == bufnr(v['filename']))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000204
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000205 res = extendnew(res, tags->map((_, v: dict<any>) => Tag2item(v)))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000206 endif
207
208 if len(res) == 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000209 # Find the variable in the tags file(s)
210 var diclist: list<dict<any>> = taglist('^' .. items[0] .. '$')
211 # Remove members, these can't appear without something in front.
212 ->filter((_, v: dict<string>): bool =>
213 v->has_key('kind') ? v.kind != 'm' : true)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000214
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000215 res = []
216 for i: number in len(diclist)->range()
217 # New ctags has the "typeref" field. Patched version has "typename".
218 if diclist[i]->has_key('typename')
219 res = extendnew(res, diclist[i]['typename']->StructMembers(items[1 :], true))
220 elseif diclist[i]->has_key('typeref')
221 res = extendnew(res, diclist[i]['typeref']->StructMembers(items[1 :], true))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000222 endif
223
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000224 # For a variable use the command, which must be a search pattern that
225 # shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000226 if diclist[i]['kind'] == 'v'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000227 var line: string = diclist[i]['cmd']
228 if line[: 1] == '/^'
229 var col: number = line->charidx(match(line, '\<' .. items[0] .. '\>'))
230 res = extendnew(res, line[2 : col - 1]->Nextitem(items[1 :], 0, true))
231 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000232 endif
233 endfor
234 endif
235
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000236 if len(res) == 0 && items[0]->searchdecl(true) == 0
237 # Found, now figure out the type.
238 # TODO: join previous line if it makes sense
239 var line: string = getline('.')
240 var col: number = charcol('.')
241 res = line[: col - 1]->Nextitem(items[1 :], 0, true)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000242 endif
243
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000244 # If the last item(s) are [...] they need to be added to the matches.
245 var last: number = len(items) - 1
246 var brackets: string = ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000247 while last >= 0
248 if items[last][0] != '['
249 break
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000250 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000251 brackets = items[last] .. brackets
252 --last
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000253 endwhile
Bram Moolenaara4a08382005-09-09 19:52:02 +0000254
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000255 return res->map((_, v: dict<any>): dict<string> => Tagline2item(v, brackets))
256enddef
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000257
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000258def GetAddition( # {{{1
259 line: string,
260 match: string,
261 memarg: list<dict<any>>,
262 bracket: bool
263): string
264 # Guess if the item is an array.
265 if bracket && match(line, match .. '\s*\[') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000266 return '['
267 endif
268
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000269 # Check if the item has members.
270 if SearchMembers(memarg, [''], false)->len() > 0
271 # If there is a '*' before the name use "->".
272 if match(line, '\*[ \t(]*' .. match .. '\>') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000273 return '->'
274 else
275 return '.'
Bram Moolenaar280f1262006-01-30 00:14:18 +0000276 endif
277 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000278 return ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000279enddef
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000280
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000281def Tag2item(val: dict<any>): dict<any> # {{{1
282# Turn the tag info "val" into an item for completion.
283# "val" is is an item in the list returned by taglist().
284# If it is a variable we may add "." or "->". Don't do it for other types,
285# such as a typedef, by not including the info that GetAddition() uses.
286 var res: dict<any> = {match: val['name']}
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000287
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000288 res['extra'] = Tagcmd2extra(val['cmd'], val['name'], val['filename'])
Bram Moolenaareb94e552006-03-11 21:35:11 +0000289
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000290 var s: string = Dict2info(val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000291 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000292 res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000293 endif
294
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000295 res['tagline'] = ''
296 if val->has_key('kind')
297 var kind: string = val['kind']
298 res['kind'] = kind
Bram Moolenaareb94e552006-03-11 21:35:11 +0000299 if kind == 'v'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000300 res['tagline'] = "\t" .. val['cmd']
301 res['dict'] = val
Bram Moolenaareb94e552006-03-11 21:35:11 +0000302 elseif kind == 'f'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000303 res['match'] = val['name'] .. '('
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000304 endif
305 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000306
307 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000308enddef
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000309
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000310def Dict2info(dict: dict<any>): string # {{{1
311# Use all the items in dictionary for the "info" entry.
312 var info: string = ''
313 for k: string in dict->keys()->sort()
314 info ..= k .. repeat(' ', 10 - strlen(k))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000315 if k == 'cmd'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000316 info ..= dict['cmd']
317 ->matchstr('/^\s*\zs.*\ze$/')
318 ->substitute('\\\(.\)', '\1', 'g')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000319 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000320 var dictk: any = dict[k]
321 if typename(dictk) != 'string'
322 info ..= dictk->string()
323 else
324 info ..= dictk
325 endif
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000326 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000327 info ..= "\n"
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000328 endfor
329 return info
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000330enddef
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000331
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000332def ParseTagline(line: string): dict<any> # {{{1
333# Parse a tag line and return a dictionary with items like taglist()
334 var l: list<string> = split(line, "\t")
335 var d: dict<any>
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000336 if len(l) >= 3
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000337 d['name'] = l[0]
338 d['filename'] = l[1]
339 d['cmd'] = l[2]
340 var n: number = 2
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000341 if l[2] =~ '^/'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000342 # Find end of cmd, it may contain Tabs.
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000343 while n < len(l) && l[n] !~ '/;"$'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000344 ++n
345 d['cmd'] ..= ' ' .. l[n]
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000346 endwhile
347 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000348 for i: number in range(n + 1, len(l) - 1)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000349 if l[i] == 'file:'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000350 d['static'] = 1
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000351 elseif l[i] !~ ':'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000352 d['kind'] = l[i]
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000353 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000354 d[l[i]->matchstr('[^:]*')] = l[i]->matchstr(':\zs.*')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000355 endif
356 endfor
357 endif
358
359 return d
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000360enddef
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000361
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000362def Tagline2item(val: dict<any>, brackets: string): dict<string> # {{{1
363# Turn a match item "val" into an item for completion.
364# "val['match']" is the matching item.
365# "val['tagline']" is the tagline in which the last part was found.
366 var line: string = val['tagline']
367 var add: string = GetAddition(line, val['match'], [val], brackets == '')
368 var res: dict<string> = {word: val['match'] .. brackets .. add}
Bram Moolenaareb94e552006-03-11 21:35:11 +0000369
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000370 if val->has_key('info')
371 # Use info from Tag2item().
372 res['info'] = val['info']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000373 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000374 # Parse the tag line and add each part to the "info" entry.
375 var s: string = ParseTagline(line)->Dict2info()
Bram Moolenaareb94e552006-03-11 21:35:11 +0000376 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000377 res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000378 endif
379 endif
380
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000381 if val->has_key('kind')
382 res['kind'] = val['kind']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000383 elseif add == '('
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000384 res['kind'] = 'f'
Bram Moolenaareb94e552006-03-11 21:35:11 +0000385 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000386 var s: string = line->matchstr('\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
Bram Moolenaareb94e552006-03-11 21:35:11 +0000387 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000388 res['kind'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000389 endif
390 endif
391
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000392 if val->has_key('extra')
393 res['menu'] = val['extra']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000394 return res
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000395 endif
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000396
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000397 # Isolate the command after the tag and filename.
398 var s: string = line->matchstr('[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000399 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000400 res['menu'] = s->Tagcmd2extra(val['match'], line->matchstr('[^\t]*\t\zs[^\t]*\ze\t'))
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000401 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000402 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000403enddef
Bram Moolenaar280f1262006-01-30 00:14:18 +0000404
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000405def Tagcmd2extra( # {{{1
406 cmd: string,
407 name: string,
408 fname: string
409): string
410# Turn a command from a tag line to something that is useful in the menu
411 var x: string
412 if cmd =~ '^/^'
413 # The command is a search command, useful to see what it is.
414 x = cmd
415 ->matchstr('^/^\s*\zs.*\ze$/')
416 ->substitute('\<' .. name .. '\>', '@@', '')
417 ->substitute('\\\(.\)', '\1', 'g')
418 .. ' - ' .. fname
419 elseif cmd =~ '^\d*$'
420 # The command is a line number, the file name is more useful.
421 x = fname .. ' - ' .. cmd
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000422 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000423 # Not recognized, use command and file name.
424 x = cmd .. ' - ' .. fname
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000425 endif
426 return x
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000427enddef
Bram Moolenaar280f1262006-01-30 00:14:18 +0000428
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000429def Nextitem( # {{{1
430 lead: string,
431 items: list<string>,
432 depth: number,
433 all: bool
434): list<dict<string>>
435# Find composing type in "lead" and match items[0] with it.
436# Repeat this recursively for items[1], if it's there.
437# When resolving typedefs "depth" is used to avoid infinite recursion.
438# Return the list of matches.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000439
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000440 # Use the text up to the variable name and split it in tokens.
441 var tokens: list<string> = split(lead, '\s\+\|\<')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000442
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000443 # Try to recognize the type of the variable. This is rough guessing...
444 var res: list<dict<string>>
445 for tidx: number in len(tokens)->range()
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000446
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000447 # Skip tokens starting with a non-ID character.
Bram Moolenaar1056d982006-03-09 22:37:52 +0000448 if tokens[tidx] !~ '^\h'
449 continue
450 endif
451
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000452 # Recognize "struct foobar" and "union foobar".
453 # Also do "class foobar" when it's C++ after all (doesn't work very well
454 # though).
455 if (tokens[tidx] == 'struct'
456 || tokens[tidx] == 'union'
457 || tokens[tidx] == 'class')
458 && tidx + 1 < len(tokens)
459 res = StructMembers(tokens[tidx] .. ':' .. tokens[tidx + 1], items, all)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000460 break
461 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000462
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000463 # TODO: add more reserved words
464 if ['int', 'short', 'char', 'float',
465 'double', 'static', 'unsigned', 'extern']->index(tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000466 continue
467 endif
468
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000469 # Use the tags file to find out if this is a typedef.
470 var diclist: list<dict<any>> = taglist('^' .. tokens[tidx] .. '$')
471 for tagidx: number in len(diclist)->range()
472 var item: dict<any> = diclist[tagidx]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000473
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000474 # New ctags has the "typeref" field. Patched version has "typename".
475 if item->has_key('typeref')
476 res = extendnew(res, item['typeref']->StructMembers(items, all))
477 continue
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000478 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000479 if item->has_key('typename')
480 res = extendnew(res, item['typename']->StructMembers(items, all))
481 continue
Bram Moolenaara4a08382005-09-09 19:52:02 +0000482 endif
483
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000484 # Only handle typedefs here.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000485 if item['kind'] != 't'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000486 continue
Bram Moolenaareb94e552006-03-11 21:35:11 +0000487 endif
488
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000489 # Skip matches local to another file.
490 if item->has_key('static') && item['static']
491 && bufnr('%') != bufnr(item['filename'])
492 continue
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000493 endif
494
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000495 # For old ctags we recognize "typedef struct aaa" and
496 # "typedef union bbb" in the tags file command.
497 var cmd: string = item['cmd']
498 var ei: number = cmd->charidx(matchend(cmd, 'typedef\s\+'))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000499 if ei > 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000500 var cmdtokens: list<string> = cmd[ei :]->split('\s\+\|\<')
501 if len(cmdtokens) > 1
502 if cmdtokens[0] == 'struct'
503 || cmdtokens[0] == 'union'
504 || cmdtokens[0] == 'class'
505 var name: string = ''
506 # Use the first identifier after the "struct" or "union"
507 for ti: number in (len(cmdtokens) - 1)->range()
508 if cmdtokens[ti] =~ '^\w'
509 name = cmdtokens[ti]
510 break
511 endif
512 endfor
513 if name != ''
514 res = extendnew(res, StructMembers(cmdtokens[0] .. ':' .. name, items, all))
515 endif
516 elseif depth < 10
517 # Could be "typedef other_T some_T".
518 res = extendnew(res, cmdtokens[0]->Nextitem(items, depth + 1, all))
519 endif
520 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000521 endif
522 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000523 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000524 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000525 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000526 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000527
Bram Moolenaara4a08382005-09-09 19:52:02 +0000528 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000529enddef
Bram Moolenaara4a08382005-09-09 19:52:02 +0000530
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000531def StructMembers( # {{{1
532 atypename: string,
533 items: list<string>,
534 all: bool
535): list<dict<string>>
Bram Moolenaara4a08382005-09-09 19:52:02 +0000536
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000537# Search for members of structure "typename" in tags files.
538# Return a list with resulting matches.
539# Each match is a dictionary with "match" and "tagline" entries.
540# When "all" is true find all, otherwise just return 1 if there is any member.
541
542 # Todo: What about local structures?
543 var fnames: string = tagfiles()
544 ->map((_, v: string) => escape(v, ' \#%'))
545 ->join()
Bram Moolenaara4a08382005-09-09 19:52:02 +0000546 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000547 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000548 endif
549
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000550 var typename: string = atypename
551 var qflist: list<dict<any>>
552 var cached: number = 0
553 var n: string
554 if !all
555 n = '1' # stop at first found match
556 if grepCache->has_key(typename)
557 qflist = grepCache[typename]
558 cached = 1
Bram Moolenaar1056d982006-03-09 22:37:52 +0000559 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000560 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000561 n = ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000562 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000563 if !cached
564 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000565 execute 'silent! keepjumps noautocmd '
566 .. n .. 'vimgrep ' .. '/\t' .. typename .. '\(\t\|$\)/j '
567 .. fnames
Bram Moolenaar1056d982006-03-09 22:37:52 +0000568
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000569 qflist = getqflist()
570 if len(qflist) > 0 || match(typename, '::') < 0
571 break
Bram Moolenaar1056d982006-03-09 22:37:52 +0000572 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000573 # No match for "struct:context::name", remove "context::" and try again.
574 typename = typename->substitute(':[^:]*::', ':', '')
Bram Moolenaar1056d982006-03-09 22:37:52 +0000575 endwhile
576
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000577 if !all
578 # Store the result to be able to use it again later.
579 grepCache[typename] = qflist
Bram Moolenaara4a08382005-09-09 19:52:02 +0000580 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000581 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000582
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000583 # Skip over [...] items
584 var idx: number = 0
585 var target: string
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200586 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000587 if idx >= len(items)
588 target = '' # No further items, matching all members
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200589 break
590 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000591 if items[idx][0] != '['
592 target = items[idx]
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200593 break
594 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000595 ++idx
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200596 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000597 # Put matching members in matches[].
598 var matches: list<dict<string>>
599 for l: dict<any> in qflist
600 var memb: string = l['text']->matchstr('[^\t]*')
601 if memb =~ '^' .. target
602 # Skip matches local to another file.
603 if match(l['text'], "\tfile:") < 0
604 || bufnr('%') == l['text']->matchstr('\t\zs[^\t]*')->bufnr()
605 var item: dict<string> = {match: memb, tagline: l['text']}
Bram Moolenaareb94e552006-03-11 21:35:11 +0000606
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000607 # Add the kind of item.
608 var s: string = l['text']->matchstr('\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
609 if s != ''
610 item['kind'] = s
611 if s == 'f'
612 item['match'] = memb .. '('
613 endif
614 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000615
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000616 matches->add(item)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000617 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000618 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000619 endfor
620
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000621 if len(matches) > 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000622 # Skip over next [...] items
623 ++idx
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000624 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000625 if idx >= len(items)
626 return matches # No further items, return the result.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000627 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000628 if items[idx][0] != '['
629 break
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000630 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000631 ++idx
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000632 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000633
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000634 # More items following. For each of the possible members find the
635 # matching following members.
636 return SearchMembers(matches, items[idx :], all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000637 endif
638
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000639 # Failed to find anything.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000640 return []
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000641enddef
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000642
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000643def SearchMembers( # {{{1
644 matches: list<dict<any>>,
645 items: list<string>,
646 all: bool
647): list<dict<string>>
648
649# For matching members, find matches for following items.
650# When "all" is true find all, otherwise just return 1 if there is any member.
651 var res: list<dict<string>>
652 for i: number in len(matches)->range()
653 var typename: string = ''
654 var line: string
655 if matches[i]->has_key('dict')
656 if matches[i]['dict']->has_key('typename')
657 typename = matches[i]['dict']['typename']
658 elseif matches[i]['dict']->has_key('typeref')
659 typename = matches[i]['dict']['typeref']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000660 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000661 line = "\t" .. matches[i]['dict']['cmd']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000662 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000663 line = matches[i]['tagline']
664 var eb: number = matchend(line, '\ttypename:')
665 var e: number = charidx(line, eb)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000666 if e < 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000667 eb = matchend(line, '\ttyperef:')
668 e = charidx(line, eb)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000669 endif
Bram Moolenaar280f1262006-01-30 00:14:18 +0000670 if e > 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000671 # Use typename field
672 typename = line->matchstr('[^\t]*', eb)
Bram Moolenaar280f1262006-01-30 00:14:18 +0000673 endif
674 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000675
Bram Moolenaar280f1262006-01-30 00:14:18 +0000676 if typename != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000677 res = extendnew(res, StructMembers(typename, items, all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000678 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000679 # Use the search command (the declaration itself).
680 var sb: number = line->match('\t\zs/^')
681 var s: number = charidx(line, sb)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000682 if s > 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000683 var e: number = line
684 ->charidx(match(line, '\<' .. matches[i]['match'] .. '\>', sb))
685 if e > 0
686 res = extendnew(res, line[s : e - 1]->Nextitem(items, 0, all))
687 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000688 endif
689 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000690 if !all && len(res) > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000691 break
692 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000693 endfor
694 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000695enddef
696#}}}1
Bram Moolenaarb6b046b2011-12-30 13:11:27 +0100697
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000698# vim: noet sw=2 sts=2