blob: 156b3af0252d0c9ec5d6d7e93e8e865e7b0862b9 [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 Moolenaar20aac6c2018-09-02 21:07:30 +02004" Last Change: 2018 Aug 20
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 Moolenaare344bea2005-09-01 20:46:49 +000010function! ccomplete#Complete(findstart, base)
11 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 Moolenaar0e5bd962006-02-04 00:59:56 +0000247function! s:GetAddition(line, match, memarg, bracket)
248 " 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 ''
263endfunction
264
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.
269function! 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 Moolenaar0e5bd962006-02-04 00:59:56 +0000292endfunction
293
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000294" Use all the items in dictionary for the "info" entry.
295function! s:Dict2info(dict)
296 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()
310function! s:ParseTagline(line)
311 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
337endfunction
338
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.
342function! 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 Moolenaar280f1262006-01-30 00:14:18 +0000380endfunction
381
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000382" Turn a command from a tag line to something that is useful in the menu
383function! s:Tagcmd2extra(cmd, name, fname)
384 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
398endfunction
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 Moolenaar1f35bf92006-03-07 22:38:47 +0000404function! 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
488endfunction
489
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.
496function! 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 Moolenaare344bea2005-09-01 20:46:49 +0000589endfunction
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.
594function! 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