blob: 12fc92df2ca7767bdb93159b83c8eeb37fd2699c [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 Moolenaar00a927d2010-05-14 23:24:24 +02004" Last Change: 2010 Mar 23
Bram Moolenaare344bea2005-09-01 20:46:49 +00005
Bram Moolenaara4a08382005-09-09 19:52:02 +00006
Bram Moolenaarf75a9632005-09-13 21:20:47 +00007" This function is used for the 'omnifunc' option.
Bram Moolenaare344bea2005-09-01 20:46:49 +00008function! ccomplete#Complete(findstart, base)
9 if a:findstart
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000010 " Locate the start of the item, including ".", "->" and "[...]".
Bram Moolenaare344bea2005-09-01 20:46:49 +000011 let line = getline('.')
12 let start = col('.') - 1
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000013 let lastword = -1
Bram Moolenaare344bea2005-09-01 20:46:49 +000014 while start > 0
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000015 if line[start - 1] =~ '\w'
16 let start -= 1
17 elseif line[start - 1] =~ '\.'
18 if lastword == -1
19 let lastword = start
20 endif
Bram Moolenaare344bea2005-09-01 20:46:49 +000021 let start -= 1
22 elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000023 if lastword == -1
24 let lastword = start
25 endif
Bram Moolenaare344bea2005-09-01 20:46:49 +000026 let start -= 2
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000027 elseif line[start - 1] == ']'
28 " Skip over [...].
29 let n = 0
30 let start -= 1
31 while start > 0
32 let start -= 1
33 if line[start] == '['
34 if n == 0
35 break
36 endif
37 let n -= 1
38 elseif line[start] == ']' " nested []
39 let n += 1
40 endif
41 endwhile
Bram Moolenaare344bea2005-09-01 20:46:49 +000042 else
43 break
44 endif
45 endwhile
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000046
47 " Return the column of the last word, which is going to be changed.
48 " Remember the text that comes before it in s:prepended.
49 if lastword == -1
50 let s:prepended = ''
51 return start
52 endif
53 let s:prepended = strpart(line, start, lastword - start)
54 return lastword
Bram Moolenaare344bea2005-09-01 20:46:49 +000055 endif
56
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000057 " Return list of matches.
58
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000059 let base = s:prepended . a:base
60
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000061 " Don't do anything for an empty base, would result in all the tags in the
62 " tags file.
63 if base == ''
64 return []
65 endif
66
Bram Moolenaar1056d982006-03-09 22:37:52 +000067 " init cache for vimgrep to empty
68 let s:grepCache = {}
69
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000070 " Split item in words, keep empty word after "." or "->".
71 " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000072 " We can't use split, because we need to skip nested [...].
73 let items = []
74 let s = 0
75 while 1
76 let e = match(base, '\.\|->\|\[', s)
77 if e < 0
78 if s == 0 || base[s - 1] != ']'
79 call add(items, strpart(base, s))
80 endif
81 break
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000082 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000083 if s == 0 || base[s - 1] != ']'
84 call add(items, strpart(base, s, e - s))
85 endif
86 if base[e] == '.'
87 let s = e + 1 " skip over '.'
88 elseif base[e] == '-'
89 let s = e + 2 " skip over '->'
90 else
91 " Skip over [...].
92 let n = 0
93 let s = e
94 let e += 1
95 while e < len(base)
96 if base[e] == ']'
97 if n == 0
98 break
99 endif
100 let n -= 1
101 elseif base[e] == '[' " nested [...]
102 let n += 1
103 endif
104 let e += 1
105 endwhile
106 let e += 1
107 call add(items, strpart(base, s, e - s))
108 let s = e
109 endif
110 endwhile
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000111
Bram Moolenaara4a08382005-09-09 19:52:02 +0000112 " Find the variable items[0].
113 " 1. in current function (like with "gd")
114 " 2. in tags file(s) (like with ":tag")
115 " 3. in current file (like with "gD")
116 let res = []
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000117 if searchdecl(items[0], 0, 1) == 0
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000118 " Found, now figure out the type.
119 " TODO: join previous line if it makes sense
120 let line = getline('.')
121 let col = col('.')
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000122 if stridx(strpart(line, 0, col), ';') != -1
123 " Handle multiple declarations on the same line.
124 let col2 = col - 1
125 while line[col2] != ';'
126 let col2 -= 1
127 endwhile
128 let line = strpart(line, col2 + 1)
129 let col -= col2
130 endif
131 if stridx(strpart(line, 0, col), ',') != -1
132 " Handle multiple declarations on the same line in a function
133 " declaration.
134 let col2 = col - 1
135 while line[col2] != ','
136 let col2 -= 1
137 endwhile
138 if strpart(line, col2 + 1, col - col2 - 1) =~ ' *[^ ][^ ]* *[^ ]'
139 let line = strpart(line, col2 + 1)
140 let col -= col2
141 endif
142 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000143 if len(items) == 1
144 " Completing one word and it's a local variable: May add '[', '.' or
145 " '->'.
146 let match = items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000147 let kind = 'v'
148 if match(line, '\<' . match . '\s*\[') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000149 let match .= '['
150 else
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000151 let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000152 if len(res) > 0
153 " There are members, thus add "." or "->".
154 if match(line, '\*[ \t(]*' . match . '\>') > 0
155 let match .= '->'
156 else
157 let match .= '.'
158 endif
159 endif
160 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000161 let res = [{'match': match, 'tagline' : '', 'kind' : kind, 'info' : line}]
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000162 else
163 " Completing "var.", "var.something", etc.
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200164 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000165 endif
166 endif
167
168 if len(items) == 1
169 " Only one part, no "." or "->": complete from tags file.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000170 let tags = taglist('^' . base)
171
172 " Remove members, these can't appear without something in front.
173 call filter(tags, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
174
175 " Remove static matches in other files.
176 call filter(tags, '!has_key(v:val, "static") || !v:val["static"] || bufnr("%") == bufnr(v:val["filename"])')
177
178 call extend(res, map(tags, 's:Tag2item(v:val)'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000179 endif
180
181 if len(res) == 0
182 " Find the variable in the tags file(s)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000183 let diclist = taglist('^' . items[0] . '$')
184
Bram Moolenaareb94e552006-03-11 21:35:11 +0000185 " Remove members, these can't appear without something in front.
186 call filter(diclist, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
187
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000188 let res = []
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000189 for i in range(len(diclist))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000190 " New ctags has the "typeref" field. Patched version has "typename".
Bram Moolenaara4a08382005-09-09 19:52:02 +0000191 if has_key(diclist[i], 'typename')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000192 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 1))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000193 elseif has_key(diclist[i], 'typeref')
194 call extend(res, s:StructMembers(diclist[i]['typeref'], items[1:], 1))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000195 endif
196
197 " For a variable use the command, which must be a search pattern that
198 " shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000199 if diclist[i]['kind'] == 'v'
200 let line = diclist[i]['cmd']
201 if line[0] == '/' && line[1] == '^'
Bram Moolenaare3226be2005-12-18 22:10:00 +0000202 let col = match(line, '\<' . items[0] . '\>')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000203 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1))
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000204 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000205 endif
206 endfor
207 endif
208
Bram Moolenaara4a08382005-09-09 19:52:02 +0000209 if len(res) == 0 && searchdecl(items[0], 1) == 0
210 " Found, now figure out the type.
211 " TODO: join previous line if it makes sense
212 let line = getline('.')
213 let col = col('.')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000214 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000215 endif
216
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000217 " If the last item(s) are [...] they need to be added to the matches.
218 let last = len(items) - 1
219 let brackets = ''
220 while last >= 0
221 if items[last][0] != '['
222 break
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000223 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000224 let brackets = items[last] . brackets
225 let last -= 1
226 endwhile
Bram Moolenaara4a08382005-09-09 19:52:02 +0000227
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000228 return map(res, 's:Tagline2item(v:val, brackets)')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000229endfunc
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000230
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000231function! s:GetAddition(line, match, memarg, bracket)
232 " Guess if the item is an array.
233 if a:bracket && match(a:line, a:match . '\s*\[') > 0
234 return '['
235 endif
236
237 " Check if the item has members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000238 if len(s:SearchMembers(a:memarg, [''], 0)) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000239 " If there is a '*' before the name use "->".
240 if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0
241 return '->'
242 else
243 return '.'
Bram Moolenaar280f1262006-01-30 00:14:18 +0000244 endif
245 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000246 return ''
247endfunction
248
249" Turn the tag info "val" into an item for completion.
250" "val" is is an item in the list returned by taglist().
251" If it is a variable we may add "." or "->". Don't do it for other types,
252" such as a typedef, by not including the info that s:GetAddition() uses.
253function! s:Tag2item(val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000254 let res = {'match': a:val['name']}
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000255
Bram Moolenaareb94e552006-03-11 21:35:11 +0000256 let res['extra'] = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename'])
257
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000258 let s = s:Dict2info(a:val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000259 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000260 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000261 endif
262
263 let res['tagline'] = ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000264 if has_key(a:val, "kind")
Bram Moolenaareb94e552006-03-11 21:35:11 +0000265 let kind = a:val['kind']
266 let res['kind'] = kind
267 if kind == 'v'
268 let res['tagline'] = "\t" . a:val['cmd']
269 let res['dict'] = a:val
270 elseif kind == 'f'
271 let res['match'] = a:val['name'] . '('
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000272 endif
273 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000274
275 return res
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000276endfunction
277
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000278" Use all the items in dictionary for the "info" entry.
279function! s:Dict2info(dict)
280 let info = ''
281 for k in sort(keys(a:dict))
282 let info .= k . repeat(' ', 10 - len(k))
283 if k == 'cmd'
284 let info .= substitute(matchstr(a:dict['cmd'], '/^\s*\zs.*\ze$/'), '\\\(.\)', '\1', 'g')
285 else
286 let info .= a:dict[k]
287 endif
288 let info .= "\n"
289 endfor
290 return info
291endfunc
292
293" Parse a tag line and return a dictionary with items like taglist()
294function! s:ParseTagline(line)
295 let l = split(a:line, "\t")
296 let d = {}
297 if len(l) >= 3
298 let d['name'] = l[0]
299 let d['filename'] = l[1]
300 let d['cmd'] = l[2]
301 let n = 2
302 if l[2] =~ '^/'
303 " Find end of cmd, it may contain Tabs.
304 while n < len(l) && l[n] !~ '/;"$'
305 let n += 1
306 let d['cmd'] .= " " . l[n]
307 endwhile
308 endif
309 for i in range(n + 1, len(l) - 1)
310 if l[i] == 'file:'
311 let d['static'] = 1
312 elseif l[i] !~ ':'
313 let d['kind'] = l[i]
314 else
315 let d[matchstr(l[i], '[^:]*')] = matchstr(l[i], ':\zs.*')
316 endif
317 endfor
318 endif
319
320 return d
321endfunction
322
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000323" Turn a match item "val" into an item for completion.
324" "val['match']" is the matching item.
325" "val['tagline']" is the tagline in which the last part was found.
326function! s:Tagline2item(val, brackets)
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000327 let line = a:val['tagline']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000328 let add = s:GetAddition(line, a:val['match'], [a:val], a:brackets == '')
329 let res = {'word': a:val['match'] . a:brackets . add }
330
331 if has_key(a:val, 'info')
332 " Use info from Tag2item().
333 let res['info'] = a:val['info']
334 else
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000335 " Parse the tag line and add each part to the "info" entry.
336 let s = s:Dict2info(s:ParseTagline(line))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000337 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000338 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000339 endif
340 endif
341
342 if has_key(a:val, 'kind')
343 let res['kind'] = a:val['kind']
344 elseif add == '('
345 let res['kind'] = 'f'
346 else
347 let s = matchstr(line, '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
348 if s != ''
349 let res['kind'] = s
350 endif
351 endif
352
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000353 if has_key(a:val, 'extra')
Bram Moolenaareb94e552006-03-11 21:35:11 +0000354 let res['menu'] = a:val['extra']
355 return res
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000356 endif
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000357
358 " Isolate the command after the tag and filename.
359 let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
360 if s != ''
Bram Moolenaareb94e552006-03-11 21:35:11 +0000361 let res['menu'] = s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t'))
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000362 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000363 return res
Bram Moolenaar280f1262006-01-30 00:14:18 +0000364endfunction
365
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000366" Turn a command from a tag line to something that is useful in the menu
367function! s:Tagcmd2extra(cmd, name, fname)
368 if a:cmd =~ '^/^'
369 " The command is a search command, useful to see what it is.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000370 let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/')
371 let x = substitute(x, '\<' . a:name . '\>', '@@', '')
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000372 let x = substitute(x, '\\\(.\)', '\1', 'g')
373 let x = x . ' - ' . a:fname
374 elseif a:cmd =~ '^\d*$'
375 " The command is a line number, the file name is more useful.
376 let x = a:fname . ' - ' . a:cmd
377 else
378 " Not recognized, use command and file name.
379 let x = a:cmd . ' - ' . a:fname
380 endif
381 return x
382endfunction
Bram Moolenaar280f1262006-01-30 00:14:18 +0000383
Bram Moolenaara4a08382005-09-09 19:52:02 +0000384" Find composing type in "lead" and match items[0] with it.
385" Repeat this recursively for items[1], if it's there.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000386" When resolving typedefs "depth" is used to avoid infinite recursion.
Bram Moolenaara4a08382005-09-09 19:52:02 +0000387" Return the list of matches.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000388function! s:Nextitem(lead, items, depth, all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000389
390 " Use the text up to the variable name and split it in tokens.
391 let tokens = split(a:lead, '\s\+\|\<')
392
393 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000394 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000395 for tidx in range(len(tokens))
396
Bram Moolenaar1056d982006-03-09 22:37:52 +0000397 " Skip tokens starting with a non-ID character.
398 if tokens[tidx] !~ '^\h'
399 continue
400 endif
401
Bram Moolenaara4a08382005-09-09 19:52:02 +0000402 " Recognize "struct foobar" and "union foobar".
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000403 " Also do "class foobar" when it's C++ after all (doesn't work very well
404 " though).
405 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union' || tokens[tidx] == 'class') && tidx + 1 < len(tokens)
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000406 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000407 break
408 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000409
Bram Moolenaara4a08382005-09-09 19:52:02 +0000410 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000411 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000412 continue
413 endif
414
415 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000416 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000417 for tagidx in range(len(diclist))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000418 let item = diclist[tagidx]
419
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000420 " New ctags has the "typeref" field. Patched version has "typename".
421 if has_key(item, 'typeref')
422 call extend(res, s:StructMembers(item['typeref'], a:items, a:all))
423 continue
424 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000425 if has_key(item, 'typename')
426 call extend(res, s:StructMembers(item['typename'], a:items, a:all))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000427 continue
428 endif
429
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000430 " Only handle typedefs here.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000431 if item['kind'] != 't'
432 continue
433 endif
434
435 " Skip matches local to another file.
436 if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename'])
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000437 continue
438 endif
439
440 " For old ctags we recognize "typedef struct aaa" and
441 " "typedef union bbb" in the tags file command.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000442 let cmd = item['cmd']
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000443 let ei = matchend(cmd, 'typedef\s\+')
444 if ei > 1
445 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
446 if len(cmdtokens) > 1
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000447 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union' || cmdtokens[0] == 'class'
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000448 let name = ''
449 " Use the first identifier after the "struct" or "union"
450 for ti in range(len(cmdtokens) - 1)
451 if cmdtokens[ti] =~ '^\w'
452 let name = cmdtokens[ti]
453 break
454 endif
455 endfor
456 if name != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000457 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000458 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000459 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000460 " Could be "typedef other_T some_T".
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000461 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000462 endif
463 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000464 endif
465 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000466 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000467 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000468 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000469 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000470
Bram Moolenaara4a08382005-09-09 19:52:02 +0000471 return res
472endfunction
473
474
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000475" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000476" Return a list with resulting matches.
477" Each match is a dictionary with "match" and "tagline" entries.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000478" When "all" is non-zero find all, otherwise just return 1 if there is any
479" member.
480function! s:StructMembers(typename, items, all)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000481 " Todo: What about local structures?
Bram Moolenaar862c27a2006-05-13 09:09:15 +0000482 let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000483 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000484 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000485 endif
486
487 let typename = a:typename
488 let qflist = []
Bram Moolenaar1056d982006-03-09 22:37:52 +0000489 let cached = 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000490 if a:all == 0
491 let n = '1' " stop at first found match
Bram Moolenaar1056d982006-03-09 22:37:52 +0000492 if has_key(s:grepCache, a:typename)
493 let qflist = s:grepCache[a:typename]
494 let cached = 1
495 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000496 else
497 let n = ''
498 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000499 if !cached
500 while 1
501 exe 'silent! ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
502
503 let qflist = getqflist()
504 if len(qflist) > 0 || match(typename, "::") < 0
505 break
506 endif
507 " No match for "struct:context::name", remove "context::" and try again.
508 let typename = substitute(typename, ':[^:]*::', ':', '')
509 endwhile
510
511 if a:all == 0
512 " Store the result to be able to use it again later.
513 let s:grepCache[a:typename] = qflist
Bram Moolenaara4a08382005-09-09 19:52:02 +0000514 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000515 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000516
Bram Moolenaareb94e552006-03-11 21:35:11 +0000517 " Put matching members in matches[].
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000518 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000519 for l in qflist
520 let memb = matchstr(l['text'], '[^\t]*')
521 if memb =~ '^' . a:items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000522 " Skip matches local to another file.
523 if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*'))
524 let item = {'match': memb, 'tagline': l['text']}
525
526 " Add the kind of item.
527 let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
528 if s != ''
529 let item['kind'] = s
530 if s == 'f'
531 let item['match'] = memb . '('
532 endif
533 endif
534
535 call add(matches, item)
536 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000537 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000538 endfor
539
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000540 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000541 " Skip over [...] items
542 let idx = 1
543 while 1
544 if idx >= len(a:items)
545 return matches " No further items, return the result.
546 endif
547 if a:items[idx][0] != '['
548 break
549 endif
550 let idx += 1
551 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000552
553 " More items following. For each of the possible members find the
554 " matching following members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000555 return s:SearchMembers(matches, a:items[idx :], a:all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000556 endif
557
558 " Failed to find anything.
559 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000560endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000561
562" For matching members, find matches for following items.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000563" When "all" is non-zero find all, otherwise just return 1 if there is any
564" member.
565function! s:SearchMembers(matches, items, all)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000566 let res = []
567 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000568 let typename = ''
569 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000570 if has_key(a:matches[i].dict, 'typename')
571 let typename = a:matches[i].dict['typename']
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000572 elseif has_key(a:matches[i].dict, 'typeref')
573 let typename = a:matches[i].dict['typeref']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000574 endif
575 let line = "\t" . a:matches[i].dict['cmd']
576 else
577 let line = a:matches[i]['tagline']
578 let e = matchend(line, '\ttypename:')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000579 if e < 0
580 let e = matchend(line, '\ttyperef:')
581 endif
Bram Moolenaar280f1262006-01-30 00:14:18 +0000582 if e > 0
583 " Use typename field
584 let typename = matchstr(line, '[^\t]*', e)
585 endif
586 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000587
Bram Moolenaar280f1262006-01-30 00:14:18 +0000588 if typename != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000589 call extend(res, s:StructMembers(typename, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000590 else
591 " Use the search command (the declaration itself).
592 let s = match(line, '\t\zs/^')
593 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000594 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000595 if e > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000596 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000597 endif
598 endif
599 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000600 if a:all == 0 && len(res) > 0
601 break
602 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000603 endfor
604 return res
605endfunc