blob: 95a20e16b0507e5d203ddc8e15195b7e049826c8 [file] [log] [blame]
Bram Moolenaare344bea2005-09-01 20:46:49 +00001" Vim completion script
2" Language: C
3" Maintainer: Bram Moolenaar <Bram@vim.org>
Bram Moolenaar4466ad62020-11-21 13:16:30 +01004" Last Change: 2020 Nov 14
Bram Moolenaare344bea2005-09-01 20:46:49 +00005
Bram Moolenaarb6b046b2011-12-30 13:11:27 +01006let s:cpo_save = &cpo
7set cpo&vim
Bram Moolenaara4a08382005-09-09 19:52:02 +00008
Bram Moolenaarf75a9632005-09-13 21:20:47 +00009" This function is used for the 'omnifunc' option.
Bram Moolenaar4466ad62020-11-21 13:16:30 +010010func ccomplete#Complete(findstart, base)
Bram Moolenaare344bea2005-09-01 20:46:49 +000011 if a:findstart
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000012 " Locate the start of the item, including ".", "->" and "[...]".
Bram Moolenaare344bea2005-09-01 20:46:49 +000013 let line = getline('.')
14 let start = col('.') - 1
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000015 let lastword = -1
Bram Moolenaare344bea2005-09-01 20:46:49 +000016 while start > 0
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000017 if line[start - 1] =~ '\w'
18 let start -= 1
19 elseif line[start - 1] =~ '\.'
20 if lastword == -1
21 let lastword = start
22 endif
Bram Moolenaare344bea2005-09-01 20:46:49 +000023 let start -= 1
24 elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000025 if lastword == -1
26 let lastword = start
27 endif
Bram Moolenaare344bea2005-09-01 20:46:49 +000028 let start -= 2
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000029 elseif line[start - 1] == ']'
30 " Skip over [...].
31 let n = 0
32 let start -= 1
33 while start > 0
34 let start -= 1
35 if line[start] == '['
36 if n == 0
37 break
38 endif
39 let n -= 1
40 elseif line[start] == ']' " nested []
41 let n += 1
42 endif
43 endwhile
Bram Moolenaare344bea2005-09-01 20:46:49 +000044 else
45 break
46 endif
47 endwhile
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000048
49 " Return the column of the last word, which is going to be changed.
50 " Remember the text that comes before it in s:prepended.
51 if lastword == -1
52 let s:prepended = ''
53 return start
54 endif
55 let s:prepended = strpart(line, start, lastword - start)
56 return lastword
Bram Moolenaare344bea2005-09-01 20:46:49 +000057 endif
58
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000059 " Return list of matches.
60
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000061 let base = s:prepended . a:base
62
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000063 " Don't do anything for an empty base, would result in all the tags in the
64 " tags file.
65 if base == ''
66 return []
67 endif
68
Bram Moolenaar1056d982006-03-09 22:37:52 +000069 " init cache for vimgrep to empty
70 let s:grepCache = {}
71
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000072 " Split item in words, keep empty word after "." or "->".
73 " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000074 " We can't use split, because we need to skip nested [...].
Bram Moolenaar20aac6c2018-09-02 21:07:30 +020075 " "aa[...]" -> ['aa', '[...]'], "aa.bb[...]" -> ['aa', 'bb', '[...]'], etc.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000076 let items = []
77 let s = 0
Bram Moolenaar20aac6c2018-09-02 21:07:30 +020078 let arrays = 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000079 while 1
80 let e = match(base, '\.\|->\|\[', s)
81 if e < 0
82 if s == 0 || base[s - 1] != ']'
83 call add(items, strpart(base, s))
84 endif
85 break
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000086 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000087 if s == 0 || base[s - 1] != ']'
88 call add(items, strpart(base, s, e - s))
89 endif
90 if base[e] == '.'
91 let s = e + 1 " skip over '.'
92 elseif base[e] == '-'
93 let s = e + 2 " skip over '->'
94 else
95 " Skip over [...].
96 let n = 0
97 let s = e
98 let e += 1
99 while e < len(base)
100 if base[e] == ']'
101 if n == 0
102 break
103 endif
104 let n -= 1
105 elseif base[e] == '[' " nested [...]
106 let n += 1
107 endif
108 let e += 1
109 endwhile
110 let e += 1
111 call add(items, strpart(base, s, e - s))
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200112 let arrays += 1
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000113 let s = e
114 endif
115 endwhile
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000116
Bram Moolenaara4a08382005-09-09 19:52:02 +0000117 " Find the variable items[0].
118 " 1. in current function (like with "gd")
119 " 2. in tags file(s) (like with ":tag")
120 " 3. in current file (like with "gD")
121 let res = []
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000122 if searchdecl(items[0], 0, 1) == 0
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000123 " Found, now figure out the type.
124 " TODO: join previous line if it makes sense
125 let line = getline('.')
126 let col = col('.')
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000127 if stridx(strpart(line, 0, col), ';') != -1
128 " Handle multiple declarations on the same line.
129 let col2 = col - 1
130 while line[col2] != ';'
131 let col2 -= 1
132 endwhile
133 let line = strpart(line, col2 + 1)
134 let col -= col2
135 endif
136 if stridx(strpart(line, 0, col), ',') != -1
137 " Handle multiple declarations on the same line in a function
138 " declaration.
139 let col2 = col - 1
140 while line[col2] != ','
141 let col2 -= 1
142 endwhile
143 if strpart(line, col2 + 1, col - col2 - 1) =~ ' *[^ ][^ ]* *[^ ]'
144 let line = strpart(line, col2 + 1)
145 let col -= col2
146 endif
147 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000148 if len(items) == 1
149 " Completing one word and it's a local variable: May add '[', '.' or
150 " '->'.
151 let match = items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000152 let kind = 'v'
153 if match(line, '\<' . match . '\s*\[') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000154 let match .= '['
155 else
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000156 let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000157 if len(res) > 0
158 " There are members, thus add "." or "->".
159 if match(line, '\*[ \t(]*' . match . '\>') > 0
160 let match .= '->'
161 else
162 let match .= '.'
163 endif
164 endif
165 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000166 let res = [{'match': match, 'tagline' : '', 'kind' : kind, 'info' : line}]
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200167 elseif len(items) == arrays + 1
168 " Completing one word and it's a local array variable: build tagline
169 " from declaration line
170 let match = items[0]
171 let kind = 'v'
172 let tagline = "\t/^" . line . '$/'
173 let res = [{'match': match, 'tagline' : tagline, 'kind' : kind, 'info' : line}]
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000174 else
175 " Completing "var.", "var.something", etc.
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200176 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000177 endif
178 endif
179
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200180 if len(items) == 1 || len(items) == arrays + 1
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000181 " Only one part, no "." or "->": complete from tags file.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200182 if len(items) == 1
183 let tags = taglist('^' . base)
184 else
185 let tags = taglist('^' . items[0] . '$')
186 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000187
188 " Remove members, these can't appear without something in front.
189 call filter(tags, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
190
191 " Remove static matches in other files.
192 call filter(tags, '!has_key(v:val, "static") || !v:val["static"] || bufnr("%") == bufnr(v:val["filename"])')
193
194 call extend(res, map(tags, 's:Tag2item(v:val)'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000195 endif
196
197 if len(res) == 0
198 " Find the variable in the tags file(s)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000199 let diclist = taglist('^' . items[0] . '$')
200
Bram Moolenaareb94e552006-03-11 21:35:11 +0000201 " Remove members, these can't appear without something in front.
202 call filter(diclist, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
203
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000204 let res = []
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000205 for i in range(len(diclist))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000206 " New ctags has the "typeref" field. Patched version has "typename".
Bram Moolenaara4a08382005-09-09 19:52:02 +0000207 if has_key(diclist[i], 'typename')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000208 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 1))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000209 elseif has_key(diclist[i], 'typeref')
210 call extend(res, s:StructMembers(diclist[i]['typeref'], items[1:], 1))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000211 endif
212
213 " For a variable use the command, which must be a search pattern that
214 " shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000215 if diclist[i]['kind'] == 'v'
216 let line = diclist[i]['cmd']
217 if line[0] == '/' && line[1] == '^'
Bram Moolenaare3226be2005-12-18 22:10:00 +0000218 let col = match(line, '\<' . items[0] . '\>')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000219 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1))
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000220 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000221 endif
222 endfor
223 endif
224
Bram Moolenaara4a08382005-09-09 19:52:02 +0000225 if len(res) == 0 && searchdecl(items[0], 1) == 0
226 " Found, now figure out the type.
227 " TODO: join previous line if it makes sense
228 let line = getline('.')
229 let col = col('.')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000230 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000231 endif
232
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000233 " If the last item(s) are [...] they need to be added to the matches.
234 let last = len(items) - 1
235 let brackets = ''
236 while last >= 0
237 if items[last][0] != '['
238 break
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000239 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000240 let brackets = items[last] . brackets
241 let last -= 1
242 endwhile
Bram Moolenaara4a08382005-09-09 19:52:02 +0000243
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000244 return map(res, 's:Tagline2item(v:val, brackets)')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000245endfunc
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000246
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100247func s:GetAddition(line, match, memarg, bracket)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000248 " Guess if the item is an array.
249 if a:bracket && match(a:line, a:match . '\s*\[') > 0
250 return '['
251 endif
252
253 " Check if the item has members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000254 if len(s:SearchMembers(a:memarg, [''], 0)) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000255 " If there is a '*' before the name use "->".
256 if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0
257 return '->'
258 else
259 return '.'
Bram Moolenaar280f1262006-01-30 00:14:18 +0000260 endif
261 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000262 return ''
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100263endfunc
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000264
265" Turn the tag info "val" into an item for completion.
266" "val" is is an item in the list returned by taglist().
267" If it is a variable we may add "." or "->". Don't do it for other types,
268" such as a typedef, by not including the info that s:GetAddition() uses.
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100269func s:Tag2item(val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000270 let res = {'match': a:val['name']}
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000271
Bram Moolenaareb94e552006-03-11 21:35:11 +0000272 let res['extra'] = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename'])
273
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000274 let s = s:Dict2info(a:val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000275 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000276 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000277 endif
278
279 let res['tagline'] = ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000280 if has_key(a:val, "kind")
Bram Moolenaareb94e552006-03-11 21:35:11 +0000281 let kind = a:val['kind']
282 let res['kind'] = kind
283 if kind == 'v'
284 let res['tagline'] = "\t" . a:val['cmd']
285 let res['dict'] = a:val
286 elseif kind == 'f'
287 let res['match'] = a:val['name'] . '('
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000288 endif
289 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000290
291 return res
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100292endfunc
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000293
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000294" Use all the items in dictionary for the "info" entry.
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100295func s:Dict2info(dict)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000296 let info = ''
297 for k in sort(keys(a:dict))
298 let info .= k . repeat(' ', 10 - len(k))
299 if k == 'cmd'
300 let info .= substitute(matchstr(a:dict['cmd'], '/^\s*\zs.*\ze$/'), '\\\(.\)', '\1', 'g')
301 else
302 let info .= a:dict[k]
303 endif
304 let info .= "\n"
305 endfor
306 return info
307endfunc
308
309" Parse a tag line and return a dictionary with items like taglist()
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100310func s:ParseTagline(line)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000311 let l = split(a:line, "\t")
312 let d = {}
313 if len(l) >= 3
314 let d['name'] = l[0]
315 let d['filename'] = l[1]
316 let d['cmd'] = l[2]
317 let n = 2
318 if l[2] =~ '^/'
319 " Find end of cmd, it may contain Tabs.
320 while n < len(l) && l[n] !~ '/;"$'
321 let n += 1
322 let d['cmd'] .= " " . l[n]
323 endwhile
324 endif
325 for i in range(n + 1, len(l) - 1)
326 if l[i] == 'file:'
327 let d['static'] = 1
328 elseif l[i] !~ ':'
329 let d['kind'] = l[i]
330 else
331 let d[matchstr(l[i], '[^:]*')] = matchstr(l[i], ':\zs.*')
332 endif
333 endfor
334 endif
335
336 return d
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100337endfunc
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000338
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000339" Turn a match item "val" into an item for completion.
340" "val['match']" is the matching item.
341" "val['tagline']" is the tagline in which the last part was found.
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100342func s:Tagline2item(val, brackets)
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000343 let line = a:val['tagline']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000344 let add = s:GetAddition(line, a:val['match'], [a:val], a:brackets == '')
345 let res = {'word': a:val['match'] . a:brackets . add }
346
347 if has_key(a:val, 'info')
348 " Use info from Tag2item().
349 let res['info'] = a:val['info']
350 else
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000351 " Parse the tag line and add each part to the "info" entry.
352 let s = s:Dict2info(s:ParseTagline(line))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000353 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000354 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000355 endif
356 endif
357
358 if has_key(a:val, 'kind')
359 let res['kind'] = a:val['kind']
360 elseif add == '('
361 let res['kind'] = 'f'
362 else
363 let s = matchstr(line, '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
364 if s != ''
365 let res['kind'] = s
366 endif
367 endif
368
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000369 if has_key(a:val, 'extra')
Bram Moolenaareb94e552006-03-11 21:35:11 +0000370 let res['menu'] = a:val['extra']
371 return res
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000372 endif
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000373
374 " Isolate the command after the tag and filename.
375 let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
376 if s != ''
Bram Moolenaareb94e552006-03-11 21:35:11 +0000377 let res['menu'] = s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t'))
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000378 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000379 return res
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100380endfunc
Bram Moolenaar280f1262006-01-30 00:14:18 +0000381
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000382" Turn a command from a tag line to something that is useful in the menu
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100383func s:Tagcmd2extra(cmd, name, fname)
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000384 if a:cmd =~ '^/^'
385 " The command is a search command, useful to see what it is.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000386 let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/')
387 let x = substitute(x, '\<' . a:name . '\>', '@@', '')
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000388 let x = substitute(x, '\\\(.\)', '\1', 'g')
389 let x = x . ' - ' . a:fname
390 elseif a:cmd =~ '^\d*$'
391 " The command is a line number, the file name is more useful.
392 let x = a:fname . ' - ' . a:cmd
393 else
394 " Not recognized, use command and file name.
395 let x = a:cmd . ' - ' . a:fname
396 endif
397 return x
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100398endfunc
Bram Moolenaar280f1262006-01-30 00:14:18 +0000399
Bram Moolenaara4a08382005-09-09 19:52:02 +0000400" Find composing type in "lead" and match items[0] with it.
401" Repeat this recursively for items[1], if it's there.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000402" When resolving typedefs "depth" is used to avoid infinite recursion.
Bram Moolenaara4a08382005-09-09 19:52:02 +0000403" Return the list of matches.
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100404func s:Nextitem(lead, items, depth, all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000405
406 " Use the text up to the variable name and split it in tokens.
407 let tokens = split(a:lead, '\s\+\|\<')
408
409 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000410 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000411 for tidx in range(len(tokens))
412
Bram Moolenaar1056d982006-03-09 22:37:52 +0000413 " Skip tokens starting with a non-ID character.
414 if tokens[tidx] !~ '^\h'
415 continue
416 endif
417
Bram Moolenaara4a08382005-09-09 19:52:02 +0000418 " Recognize "struct foobar" and "union foobar".
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000419 " Also do "class foobar" when it's C++ after all (doesn't work very well
420 " though).
421 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union' || tokens[tidx] == 'class') && tidx + 1 < len(tokens)
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000422 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000423 break
424 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000425
Bram Moolenaara4a08382005-09-09 19:52:02 +0000426 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000427 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000428 continue
429 endif
430
431 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000432 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000433 for tagidx in range(len(diclist))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000434 let item = diclist[tagidx]
435
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000436 " New ctags has the "typeref" field. Patched version has "typename".
437 if has_key(item, 'typeref')
438 call extend(res, s:StructMembers(item['typeref'], a:items, a:all))
439 continue
440 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000441 if has_key(item, 'typename')
442 call extend(res, s:StructMembers(item['typename'], a:items, a:all))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000443 continue
444 endif
445
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000446 " Only handle typedefs here.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000447 if item['kind'] != 't'
448 continue
449 endif
450
451 " Skip matches local to another file.
452 if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename'])
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000453 continue
454 endif
455
456 " For old ctags we recognize "typedef struct aaa" and
457 " "typedef union bbb" in the tags file command.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000458 let cmd = item['cmd']
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000459 let ei = matchend(cmd, 'typedef\s\+')
460 if ei > 1
461 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
462 if len(cmdtokens) > 1
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000463 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union' || cmdtokens[0] == 'class'
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000464 let name = ''
465 " Use the first identifier after the "struct" or "union"
466 for ti in range(len(cmdtokens) - 1)
467 if cmdtokens[ti] =~ '^\w'
468 let name = cmdtokens[ti]
469 break
470 endif
471 endfor
472 if name != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000473 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000474 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000475 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000476 " Could be "typedef other_T some_T".
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000477 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000478 endif
479 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000480 endif
481 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000482 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000483 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000484 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000485 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000486
Bram Moolenaara4a08382005-09-09 19:52:02 +0000487 return res
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100488endfunc
Bram Moolenaara4a08382005-09-09 19:52:02 +0000489
490
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000491" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000492" Return a list with resulting matches.
493" Each match is a dictionary with "match" and "tagline" entries.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000494" When "all" is non-zero find all, otherwise just return 1 if there is any
495" member.
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100496func s:StructMembers(typename, items, all)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000497 " Todo: What about local structures?
Bram Moolenaar862c27a2006-05-13 09:09:15 +0000498 let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000499 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000500 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000501 endif
502
503 let typename = a:typename
504 let qflist = []
Bram Moolenaar1056d982006-03-09 22:37:52 +0000505 let cached = 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000506 if a:all == 0
507 let n = '1' " stop at first found match
Bram Moolenaar1056d982006-03-09 22:37:52 +0000508 if has_key(s:grepCache, a:typename)
509 let qflist = s:grepCache[a:typename]
510 let cached = 1
511 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000512 else
513 let n = ''
514 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000515 if !cached
516 while 1
Bram Moolenaar30b65812012-07-12 22:01:11 +0200517 exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaar1056d982006-03-09 22:37:52 +0000518
519 let qflist = getqflist()
520 if len(qflist) > 0 || match(typename, "::") < 0
521 break
522 endif
523 " No match for "struct:context::name", remove "context::" and try again.
524 let typename = substitute(typename, ':[^:]*::', ':', '')
525 endwhile
526
527 if a:all == 0
528 " Store the result to be able to use it again later.
529 let s:grepCache[a:typename] = qflist
Bram Moolenaara4a08382005-09-09 19:52:02 +0000530 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000531 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000532
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200533 " Skip over [...] items
534 let idx = 0
535 while 1
536 if idx >= len(a:items)
537 let target = '' " No further items, matching all members
538 break
539 endif
540 if a:items[idx][0] != '['
541 let target = a:items[idx]
542 break
543 endif
544 let idx += 1
545 endwhile
Bram Moolenaareb94e552006-03-11 21:35:11 +0000546 " Put matching members in matches[].
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000547 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000548 for l in qflist
549 let memb = matchstr(l['text'], '[^\t]*')
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200550 if memb =~ '^' . target
Bram Moolenaareb94e552006-03-11 21:35:11 +0000551 " Skip matches local to another file.
552 if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*'))
553 let item = {'match': memb, 'tagline': l['text']}
554
555 " Add the kind of item.
556 let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
557 if s != ''
558 let item['kind'] = s
559 if s == 'f'
560 let item['match'] = memb . '('
561 endif
562 endif
563
564 call add(matches, item)
565 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000566 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000567 endfor
568
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000569 if len(matches) > 0
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200570 " Skip over next [...] items
571 let idx += 1
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000572 while 1
573 if idx >= len(a:items)
574 return matches " No further items, return the result.
575 endif
576 if a:items[idx][0] != '['
577 break
578 endif
579 let idx += 1
580 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000581
582 " More items following. For each of the possible members find the
583 " matching following members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000584 return s:SearchMembers(matches, a:items[idx :], a:all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000585 endif
586
587 " Failed to find anything.
588 return []
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100589endfunc
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000590
591" For matching members, find matches for following items.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000592" When "all" is non-zero find all, otherwise just return 1 if there is any
593" member.
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100594func s:SearchMembers(matches, items, all)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000595 let res = []
596 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000597 let typename = ''
598 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000599 if has_key(a:matches[i].dict, 'typename')
600 let typename = a:matches[i].dict['typename']
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000601 elseif has_key(a:matches[i].dict, 'typeref')
602 let typename = a:matches[i].dict['typeref']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000603 endif
604 let line = "\t" . a:matches[i].dict['cmd']
605 else
606 let line = a:matches[i]['tagline']
607 let e = matchend(line, '\ttypename:')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000608 if e < 0
609 let e = matchend(line, '\ttyperef:')
610 endif
Bram Moolenaar280f1262006-01-30 00:14:18 +0000611 if e > 0
612 " Use typename field
613 let typename = matchstr(line, '[^\t]*', e)
614 endif
615 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000616
Bram Moolenaar280f1262006-01-30 00:14:18 +0000617 if typename != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000618 call extend(res, s:StructMembers(typename, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000619 else
620 " Use the search command (the declaration itself).
621 let s = match(line, '\t\zs/^')
622 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000623 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000624 if e > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000625 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000626 endif
627 endif
628 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000629 if a:all == 0 && len(res) > 0
630 break
631 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000632 endfor
633 return res
634endfunc
Bram Moolenaarb6b046b2011-12-30 13:11:27 +0100635
636let &cpo = s:cpo_save
637unlet s:cpo_save
Bram Moolenaard1caa942020-04-10 22:10:56 +0200638
639" vim: noet sw=2 sts=2