blob: d5bfa076a951564b59f76f28145d72db29304852 [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 Moolenaar30b65812012-07-12 22:01:11 +02004" Last Change: 2012 Jun 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 [...].
75 let items = []
76 let s = 0
77 while 1
78 let e = match(base, '\.\|->\|\[', s)
79 if e < 0
80 if s == 0 || base[s - 1] != ']'
81 call add(items, strpart(base, s))
82 endif
83 break
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000084 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000085 if s == 0 || base[s - 1] != ']'
86 call add(items, strpart(base, s, e - s))
87 endif
88 if base[e] == '.'
89 let s = e + 1 " skip over '.'
90 elseif base[e] == '-'
91 let s = e + 2 " skip over '->'
92 else
93 " Skip over [...].
94 let n = 0
95 let s = e
96 let e += 1
97 while e < len(base)
98 if base[e] == ']'
99 if n == 0
100 break
101 endif
102 let n -= 1
103 elseif base[e] == '[' " nested [...]
104 let n += 1
105 endif
106 let e += 1
107 endwhile
108 let e += 1
109 call add(items, strpart(base, s, e - s))
110 let s = e
111 endif
112 endwhile
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000113
Bram Moolenaara4a08382005-09-09 19:52:02 +0000114 " Find the variable items[0].
115 " 1. in current function (like with "gd")
116 " 2. in tags file(s) (like with ":tag")
117 " 3. in current file (like with "gD")
118 let res = []
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000119 if searchdecl(items[0], 0, 1) == 0
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000120 " Found, now figure out the type.
121 " TODO: join previous line if it makes sense
122 let line = getline('.')
123 let col = col('.')
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000124 if stridx(strpart(line, 0, col), ';') != -1
125 " Handle multiple declarations on the same line.
126 let col2 = col - 1
127 while line[col2] != ';'
128 let col2 -= 1
129 endwhile
130 let line = strpart(line, col2 + 1)
131 let col -= col2
132 endif
133 if stridx(strpart(line, 0, col), ',') != -1
134 " Handle multiple declarations on the same line in a function
135 " declaration.
136 let col2 = col - 1
137 while line[col2] != ','
138 let col2 -= 1
139 endwhile
140 if strpart(line, col2 + 1, col - col2 - 1) =~ ' *[^ ][^ ]* *[^ ]'
141 let line = strpart(line, col2 + 1)
142 let col -= col2
143 endif
144 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000145 if len(items) == 1
146 " Completing one word and it's a local variable: May add '[', '.' or
147 " '->'.
148 let match = items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000149 let kind = 'v'
150 if match(line, '\<' . match . '\s*\[') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000151 let match .= '['
152 else
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000153 let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000154 if len(res) > 0
155 " There are members, thus add "." or "->".
156 if match(line, '\*[ \t(]*' . match . '\>') > 0
157 let match .= '->'
158 else
159 let match .= '.'
160 endif
161 endif
162 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000163 let res = [{'match': match, 'tagline' : '', 'kind' : kind, 'info' : line}]
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000164 else
165 " Completing "var.", "var.something", etc.
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200166 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000167 endif
168 endif
169
170 if len(items) == 1
171 " Only one part, no "." or "->": complete from tags file.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000172 let tags = taglist('^' . base)
173
174 " Remove members, these can't appear without something in front.
175 call filter(tags, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
176
177 " Remove static matches in other files.
178 call filter(tags, '!has_key(v:val, "static") || !v:val["static"] || bufnr("%") == bufnr(v:val["filename"])')
179
180 call extend(res, map(tags, 's:Tag2item(v:val)'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000181 endif
182
183 if len(res) == 0
184 " Find the variable in the tags file(s)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000185 let diclist = taglist('^' . items[0] . '$')
186
Bram Moolenaareb94e552006-03-11 21:35:11 +0000187 " Remove members, these can't appear without something in front.
188 call filter(diclist, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
189
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000190 let res = []
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000191 for i in range(len(diclist))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000192 " New ctags has the "typeref" field. Patched version has "typename".
Bram Moolenaara4a08382005-09-09 19:52:02 +0000193 if has_key(diclist[i], 'typename')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000194 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 1))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000195 elseif has_key(diclist[i], 'typeref')
196 call extend(res, s:StructMembers(diclist[i]['typeref'], items[1:], 1))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000197 endif
198
199 " For a variable use the command, which must be a search pattern that
200 " shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000201 if diclist[i]['kind'] == 'v'
202 let line = diclist[i]['cmd']
203 if line[0] == '/' && line[1] == '^'
Bram Moolenaare3226be2005-12-18 22:10:00 +0000204 let col = match(line, '\<' . items[0] . '\>')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000205 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1))
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000206 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000207 endif
208 endfor
209 endif
210
Bram Moolenaara4a08382005-09-09 19:52:02 +0000211 if len(res) == 0 && searchdecl(items[0], 1) == 0
212 " Found, now figure out the type.
213 " TODO: join previous line if it makes sense
214 let line = getline('.')
215 let col = col('.')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000216 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000217 endif
218
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000219 " If the last item(s) are [...] they need to be added to the matches.
220 let last = len(items) - 1
221 let brackets = ''
222 while last >= 0
223 if items[last][0] != '['
224 break
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000225 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000226 let brackets = items[last] . brackets
227 let last -= 1
228 endwhile
Bram Moolenaara4a08382005-09-09 19:52:02 +0000229
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000230 return map(res, 's:Tagline2item(v:val, brackets)')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000231endfunc
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000232
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000233function! s:GetAddition(line, match, memarg, bracket)
234 " Guess if the item is an array.
235 if a:bracket && match(a:line, a:match . '\s*\[') > 0
236 return '['
237 endif
238
239 " Check if the item has members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000240 if len(s:SearchMembers(a:memarg, [''], 0)) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000241 " If there is a '*' before the name use "->".
242 if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0
243 return '->'
244 else
245 return '.'
Bram Moolenaar280f1262006-01-30 00:14:18 +0000246 endif
247 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000248 return ''
249endfunction
250
251" Turn the tag info "val" into an item for completion.
252" "val" is is an item in the list returned by taglist().
253" If it is a variable we may add "." or "->". Don't do it for other types,
254" such as a typedef, by not including the info that s:GetAddition() uses.
255function! s:Tag2item(val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000256 let res = {'match': a:val['name']}
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000257
Bram Moolenaareb94e552006-03-11 21:35:11 +0000258 let res['extra'] = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename'])
259
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000260 let s = s:Dict2info(a:val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000261 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000262 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000263 endif
264
265 let res['tagline'] = ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000266 if has_key(a:val, "kind")
Bram Moolenaareb94e552006-03-11 21:35:11 +0000267 let kind = a:val['kind']
268 let res['kind'] = kind
269 if kind == 'v'
270 let res['tagline'] = "\t" . a:val['cmd']
271 let res['dict'] = a:val
272 elseif kind == 'f'
273 let res['match'] = a:val['name'] . '('
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000274 endif
275 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000276
277 return res
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000278endfunction
279
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000280" Use all the items in dictionary for the "info" entry.
281function! s:Dict2info(dict)
282 let info = ''
283 for k in sort(keys(a:dict))
284 let info .= k . repeat(' ', 10 - len(k))
285 if k == 'cmd'
286 let info .= substitute(matchstr(a:dict['cmd'], '/^\s*\zs.*\ze$/'), '\\\(.\)', '\1', 'g')
287 else
288 let info .= a:dict[k]
289 endif
290 let info .= "\n"
291 endfor
292 return info
293endfunc
294
295" Parse a tag line and return a dictionary with items like taglist()
296function! s:ParseTagline(line)
297 let l = split(a:line, "\t")
298 let d = {}
299 if len(l) >= 3
300 let d['name'] = l[0]
301 let d['filename'] = l[1]
302 let d['cmd'] = l[2]
303 let n = 2
304 if l[2] =~ '^/'
305 " Find end of cmd, it may contain Tabs.
306 while n < len(l) && l[n] !~ '/;"$'
307 let n += 1
308 let d['cmd'] .= " " . l[n]
309 endwhile
310 endif
311 for i in range(n + 1, len(l) - 1)
312 if l[i] == 'file:'
313 let d['static'] = 1
314 elseif l[i] !~ ':'
315 let d['kind'] = l[i]
316 else
317 let d[matchstr(l[i], '[^:]*')] = matchstr(l[i], ':\zs.*')
318 endif
319 endfor
320 endif
321
322 return d
323endfunction
324
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000325" Turn a match item "val" into an item for completion.
326" "val['match']" is the matching item.
327" "val['tagline']" is the tagline in which the last part was found.
328function! s:Tagline2item(val, brackets)
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000329 let line = a:val['tagline']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000330 let add = s:GetAddition(line, a:val['match'], [a:val], a:brackets == '')
331 let res = {'word': a:val['match'] . a:brackets . add }
332
333 if has_key(a:val, 'info')
334 " Use info from Tag2item().
335 let res['info'] = a:val['info']
336 else
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000337 " Parse the tag line and add each part to the "info" entry.
338 let s = s:Dict2info(s:ParseTagline(line))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000339 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000340 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000341 endif
342 endif
343
344 if has_key(a:val, 'kind')
345 let res['kind'] = a:val['kind']
346 elseif add == '('
347 let res['kind'] = 'f'
348 else
349 let s = matchstr(line, '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
350 if s != ''
351 let res['kind'] = s
352 endif
353 endif
354
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000355 if has_key(a:val, 'extra')
Bram Moolenaareb94e552006-03-11 21:35:11 +0000356 let res['menu'] = a:val['extra']
357 return res
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000358 endif
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000359
360 " Isolate the command after the tag and filename.
361 let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
362 if s != ''
Bram Moolenaareb94e552006-03-11 21:35:11 +0000363 let res['menu'] = s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t'))
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000364 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000365 return res
Bram Moolenaar280f1262006-01-30 00:14:18 +0000366endfunction
367
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000368" Turn a command from a tag line to something that is useful in the menu
369function! s:Tagcmd2extra(cmd, name, fname)
370 if a:cmd =~ '^/^'
371 " The command is a search command, useful to see what it is.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000372 let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/')
373 let x = substitute(x, '\<' . a:name . '\>', '@@', '')
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000374 let x = substitute(x, '\\\(.\)', '\1', 'g')
375 let x = x . ' - ' . a:fname
376 elseif a:cmd =~ '^\d*$'
377 " The command is a line number, the file name is more useful.
378 let x = a:fname . ' - ' . a:cmd
379 else
380 " Not recognized, use command and file name.
381 let x = a:cmd . ' - ' . a:fname
382 endif
383 return x
384endfunction
Bram Moolenaar280f1262006-01-30 00:14:18 +0000385
Bram Moolenaara4a08382005-09-09 19:52:02 +0000386" Find composing type in "lead" and match items[0] with it.
387" Repeat this recursively for items[1], if it's there.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000388" When resolving typedefs "depth" is used to avoid infinite recursion.
Bram Moolenaara4a08382005-09-09 19:52:02 +0000389" Return the list of matches.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000390function! s:Nextitem(lead, items, depth, all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000391
392 " Use the text up to the variable name and split it in tokens.
393 let tokens = split(a:lead, '\s\+\|\<')
394
395 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000396 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000397 for tidx in range(len(tokens))
398
Bram Moolenaar1056d982006-03-09 22:37:52 +0000399 " Skip tokens starting with a non-ID character.
400 if tokens[tidx] !~ '^\h'
401 continue
402 endif
403
Bram Moolenaara4a08382005-09-09 19:52:02 +0000404 " Recognize "struct foobar" and "union foobar".
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000405 " Also do "class foobar" when it's C++ after all (doesn't work very well
406 " though).
407 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union' || tokens[tidx] == 'class') && tidx + 1 < len(tokens)
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000408 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000409 break
410 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000411
Bram Moolenaara4a08382005-09-09 19:52:02 +0000412 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000413 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000414 continue
415 endif
416
417 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000418 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000419 for tagidx in range(len(diclist))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000420 let item = diclist[tagidx]
421
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000422 " New ctags has the "typeref" field. Patched version has "typename".
423 if has_key(item, 'typeref')
424 call extend(res, s:StructMembers(item['typeref'], a:items, a:all))
425 continue
426 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000427 if has_key(item, 'typename')
428 call extend(res, s:StructMembers(item['typename'], a:items, a:all))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000429 continue
430 endif
431
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000432 " Only handle typedefs here.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000433 if item['kind'] != 't'
434 continue
435 endif
436
437 " Skip matches local to another file.
438 if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename'])
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000439 continue
440 endif
441
442 " For old ctags we recognize "typedef struct aaa" and
443 " "typedef union bbb" in the tags file command.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000444 let cmd = item['cmd']
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000445 let ei = matchend(cmd, 'typedef\s\+')
446 if ei > 1
447 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
448 if len(cmdtokens) > 1
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000449 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union' || cmdtokens[0] == 'class'
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000450 let name = ''
451 " Use the first identifier after the "struct" or "union"
452 for ti in range(len(cmdtokens) - 1)
453 if cmdtokens[ti] =~ '^\w'
454 let name = cmdtokens[ti]
455 break
456 endif
457 endfor
458 if name != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000459 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000460 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000461 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000462 " Could be "typedef other_T some_T".
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000463 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000464 endif
465 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000466 endif
467 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000468 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000469 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000470 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000471 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000472
Bram Moolenaara4a08382005-09-09 19:52:02 +0000473 return res
474endfunction
475
476
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000477" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000478" Return a list with resulting matches.
479" Each match is a dictionary with "match" and "tagline" entries.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000480" When "all" is non-zero find all, otherwise just return 1 if there is any
481" member.
482function! s:StructMembers(typename, items, all)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000483 " Todo: What about local structures?
Bram Moolenaar862c27a2006-05-13 09:09:15 +0000484 let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000485 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000486 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000487 endif
488
489 let typename = a:typename
490 let qflist = []
Bram Moolenaar1056d982006-03-09 22:37:52 +0000491 let cached = 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000492 if a:all == 0
493 let n = '1' " stop at first found match
Bram Moolenaar1056d982006-03-09 22:37:52 +0000494 if has_key(s:grepCache, a:typename)
495 let qflist = s:grepCache[a:typename]
496 let cached = 1
497 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000498 else
499 let n = ''
500 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000501 if !cached
502 while 1
Bram Moolenaar30b65812012-07-12 22:01:11 +0200503 exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaar1056d982006-03-09 22:37:52 +0000504
505 let qflist = getqflist()
506 if len(qflist) > 0 || match(typename, "::") < 0
507 break
508 endif
509 " No match for "struct:context::name", remove "context::" and try again.
510 let typename = substitute(typename, ':[^:]*::', ':', '')
511 endwhile
512
513 if a:all == 0
514 " Store the result to be able to use it again later.
515 let s:grepCache[a:typename] = qflist
Bram Moolenaara4a08382005-09-09 19:52:02 +0000516 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000517 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000518
Bram Moolenaareb94e552006-03-11 21:35:11 +0000519 " Put matching members in matches[].
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000520 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000521 for l in qflist
522 let memb = matchstr(l['text'], '[^\t]*')
523 if memb =~ '^' . a:items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000524 " Skip matches local to another file.
525 if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*'))
526 let item = {'match': memb, 'tagline': l['text']}
527
528 " Add the kind of item.
529 let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
530 if s != ''
531 let item['kind'] = s
532 if s == 'f'
533 let item['match'] = memb . '('
534 endif
535 endif
536
537 call add(matches, item)
538 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000539 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000540 endfor
541
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000542 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000543 " Skip over [...] items
544 let idx = 1
545 while 1
546 if idx >= len(a:items)
547 return matches " No further items, return the result.
548 endif
549 if a:items[idx][0] != '['
550 break
551 endif
552 let idx += 1
553 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000554
555 " More items following. For each of the possible members find the
556 " matching following members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000557 return s:SearchMembers(matches, a:items[idx :], a:all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000558 endif
559
560 " Failed to find anything.
561 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000562endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000563
564" For matching members, find matches for following items.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000565" When "all" is non-zero find all, otherwise just return 1 if there is any
566" member.
567function! s:SearchMembers(matches, items, all)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000568 let res = []
569 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000570 let typename = ''
571 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000572 if has_key(a:matches[i].dict, 'typename')
573 let typename = a:matches[i].dict['typename']
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000574 elseif has_key(a:matches[i].dict, 'typeref')
575 let typename = a:matches[i].dict['typeref']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000576 endif
577 let line = "\t" . a:matches[i].dict['cmd']
578 else
579 let line = a:matches[i]['tagline']
580 let e = matchend(line, '\ttypename:')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000581 if e < 0
582 let e = matchend(line, '\ttyperef:')
583 endif
Bram Moolenaar280f1262006-01-30 00:14:18 +0000584 if e > 0
585 " Use typename field
586 let typename = matchstr(line, '[^\t]*', e)
587 endif
588 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000589
Bram Moolenaar280f1262006-01-30 00:14:18 +0000590 if typename != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000591 call extend(res, s:StructMembers(typename, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000592 else
593 " Use the search command (the declaration itself).
594 let s = match(line, '\t\zs/^')
595 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000596 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000597 if e > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000598 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000599 endif
600 endif
601 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000602 if a:all == 0 && len(res) > 0
603 break
604 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000605 endfor
606 return res
607endfunc
Bram Moolenaarb6b046b2011-12-30 13:11:27 +0100608
609let &cpo = s:cpo_save
610unlet s:cpo_save