blob: 0f013d79b76d95943deca2b3c11dd5467f3c4abd [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 Moolenaar862c27a2006-05-13 09:09:15 +00004" Last Change: 2006 May 08
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 Moolenaar0e5bd962006-02-04 00:59:56 +0000122 if len(items) == 1
123 " Completing one word and it's a local variable: May add '[', '.' or
124 " '->'.
125 let match = items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000126 let kind = 'v'
127 if match(line, '\<' . match . '\s*\[') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000128 let match .= '['
129 else
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000130 let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000131 if len(res) > 0
132 " There are members, thus add "." or "->".
133 if match(line, '\*[ \t(]*' . match . '\>') > 0
134 let match .= '->'
135 else
136 let match .= '.'
137 endif
138 endif
139 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000140 let res = [{'match': match, 'tagline' : '', 'kind' : kind, 'info' : line}]
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000141 else
142 " Completing "var.", "var.something", etc.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000143 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000144 endif
145 endif
146
147 if len(items) == 1
148 " Only one part, no "." or "->": complete from tags file.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000149 let tags = taglist('^' . base)
150
151 " Remove members, these can't appear without something in front.
152 call filter(tags, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
153
154 " Remove static matches in other files.
155 call filter(tags, '!has_key(v:val, "static") || !v:val["static"] || bufnr("%") == bufnr(v:val["filename"])')
156
157 call extend(res, map(tags, 's:Tag2item(v:val)'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000158 endif
159
160 if len(res) == 0
161 " Find the variable in the tags file(s)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000162 let diclist = taglist('^' . items[0] . '$')
163
Bram Moolenaareb94e552006-03-11 21:35:11 +0000164 " Remove members, these can't appear without something in front.
165 call filter(diclist, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1')
166
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000167 let res = []
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000168 for i in range(len(diclist))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000169 " New ctags has the "typeref" field. Patched version has "typename".
Bram Moolenaara4a08382005-09-09 19:52:02 +0000170 if has_key(diclist[i], 'typename')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000171 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 1))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000172 elseif has_key(diclist[i], 'typeref')
173 call extend(res, s:StructMembers(diclist[i]['typeref'], items[1:], 1))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000174 endif
175
176 " For a variable use the command, which must be a search pattern that
177 " shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000178 if diclist[i]['kind'] == 'v'
179 let line = diclist[i]['cmd']
180 if line[0] == '/' && line[1] == '^'
Bram Moolenaare3226be2005-12-18 22:10:00 +0000181 let col = match(line, '\<' . items[0] . '\>')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000182 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1))
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000183 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000184 endif
185 endfor
186 endif
187
Bram Moolenaara4a08382005-09-09 19:52:02 +0000188 if len(res) == 0 && searchdecl(items[0], 1) == 0
189 " Found, now figure out the type.
190 " TODO: join previous line if it makes sense
191 let line = getline('.')
192 let col = col('.')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000193 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000194 endif
195
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000196 " If the last item(s) are [...] they need to be added to the matches.
197 let last = len(items) - 1
198 let brackets = ''
199 while last >= 0
200 if items[last][0] != '['
201 break
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000202 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000203 let brackets = items[last] . brackets
204 let last -= 1
205 endwhile
Bram Moolenaara4a08382005-09-09 19:52:02 +0000206
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000207 return map(res, 's:Tagline2item(v:val, brackets)')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000208endfunc
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000209
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000210function! s:GetAddition(line, match, memarg, bracket)
211 " Guess if the item is an array.
212 if a:bracket && match(a:line, a:match . '\s*\[') > 0
213 return '['
214 endif
215
216 " Check if the item has members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000217 if len(s:SearchMembers(a:memarg, [''], 0)) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000218 " If there is a '*' before the name use "->".
219 if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0
220 return '->'
221 else
222 return '.'
Bram Moolenaar280f1262006-01-30 00:14:18 +0000223 endif
224 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000225 return ''
226endfunction
227
228" Turn the tag info "val" into an item for completion.
229" "val" is is an item in the list returned by taglist().
230" If it is a variable we may add "." or "->". Don't do it for other types,
231" such as a typedef, by not including the info that s:GetAddition() uses.
232function! s:Tag2item(val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000233 let res = {'match': a:val['name']}
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000234
Bram Moolenaareb94e552006-03-11 21:35:11 +0000235 let res['extra'] = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename'])
236
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000237 let s = s:Dict2info(a:val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000238 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000239 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000240 endif
241
242 let res['tagline'] = ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000243 if has_key(a:val, "kind")
Bram Moolenaareb94e552006-03-11 21:35:11 +0000244 let kind = a:val['kind']
245 let res['kind'] = kind
246 if kind == 'v'
247 let res['tagline'] = "\t" . a:val['cmd']
248 let res['dict'] = a:val
249 elseif kind == 'f'
250 let res['match'] = a:val['name'] . '('
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000251 endif
252 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000253
254 return res
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000255endfunction
256
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000257" Use all the items in dictionary for the "info" entry.
258function! s:Dict2info(dict)
259 let info = ''
260 for k in sort(keys(a:dict))
261 let info .= k . repeat(' ', 10 - len(k))
262 if k == 'cmd'
263 let info .= substitute(matchstr(a:dict['cmd'], '/^\s*\zs.*\ze$/'), '\\\(.\)', '\1', 'g')
264 else
265 let info .= a:dict[k]
266 endif
267 let info .= "\n"
268 endfor
269 return info
270endfunc
271
272" Parse a tag line and return a dictionary with items like taglist()
273function! s:ParseTagline(line)
274 let l = split(a:line, "\t")
275 let d = {}
276 if len(l) >= 3
277 let d['name'] = l[0]
278 let d['filename'] = l[1]
279 let d['cmd'] = l[2]
280 let n = 2
281 if l[2] =~ '^/'
282 " Find end of cmd, it may contain Tabs.
283 while n < len(l) && l[n] !~ '/;"$'
284 let n += 1
285 let d['cmd'] .= " " . l[n]
286 endwhile
287 endif
288 for i in range(n + 1, len(l) - 1)
289 if l[i] == 'file:'
290 let d['static'] = 1
291 elseif l[i] !~ ':'
292 let d['kind'] = l[i]
293 else
294 let d[matchstr(l[i], '[^:]*')] = matchstr(l[i], ':\zs.*')
295 endif
296 endfor
297 endif
298
299 return d
300endfunction
301
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000302" Turn a match item "val" into an item for completion.
303" "val['match']" is the matching item.
304" "val['tagline']" is the tagline in which the last part was found.
305function! s:Tagline2item(val, brackets)
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000306 let line = a:val['tagline']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000307 let add = s:GetAddition(line, a:val['match'], [a:val], a:brackets == '')
308 let res = {'word': a:val['match'] . a:brackets . add }
309
310 if has_key(a:val, 'info')
311 " Use info from Tag2item().
312 let res['info'] = a:val['info']
313 else
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000314 " Parse the tag line and add each part to the "info" entry.
315 let s = s:Dict2info(s:ParseTagline(line))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000316 if s != ''
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000317 let res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000318 endif
319 endif
320
321 if has_key(a:val, 'kind')
322 let res['kind'] = a:val['kind']
323 elseif add == '('
324 let res['kind'] = 'f'
325 else
326 let s = matchstr(line, '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
327 if s != ''
328 let res['kind'] = s
329 endif
330 endif
331
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000332 if has_key(a:val, 'extra')
Bram Moolenaareb94e552006-03-11 21:35:11 +0000333 let res['menu'] = a:val['extra']
334 return res
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000335 endif
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000336
337 " Isolate the command after the tag and filename.
338 let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
339 if s != ''
Bram Moolenaareb94e552006-03-11 21:35:11 +0000340 let res['menu'] = s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t'))
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000341 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000342 return res
Bram Moolenaar280f1262006-01-30 00:14:18 +0000343endfunction
344
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000345" Turn a command from a tag line to something that is useful in the menu
346function! s:Tagcmd2extra(cmd, name, fname)
347 if a:cmd =~ '^/^'
348 " The command is a search command, useful to see what it is.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000349 let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/')
350 let x = substitute(x, '\<' . a:name . '\>', '@@', '')
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000351 let x = substitute(x, '\\\(.\)', '\1', 'g')
352 let x = x . ' - ' . a:fname
353 elseif a:cmd =~ '^\d*$'
354 " The command is a line number, the file name is more useful.
355 let x = a:fname . ' - ' . a:cmd
356 else
357 " Not recognized, use command and file name.
358 let x = a:cmd . ' - ' . a:fname
359 endif
360 return x
361endfunction
Bram Moolenaar280f1262006-01-30 00:14:18 +0000362
Bram Moolenaara4a08382005-09-09 19:52:02 +0000363" Find composing type in "lead" and match items[0] with it.
364" Repeat this recursively for items[1], if it's there.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000365" When resolving typedefs "depth" is used to avoid infinite recursion.
Bram Moolenaara4a08382005-09-09 19:52:02 +0000366" Return the list of matches.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000367function! s:Nextitem(lead, items, depth, all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000368
369 " Use the text up to the variable name and split it in tokens.
370 let tokens = split(a:lead, '\s\+\|\<')
371
372 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000373 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000374 for tidx in range(len(tokens))
375
Bram Moolenaar1056d982006-03-09 22:37:52 +0000376 " Skip tokens starting with a non-ID character.
377 if tokens[tidx] !~ '^\h'
378 continue
379 endif
380
Bram Moolenaara4a08382005-09-09 19:52:02 +0000381 " Recognize "struct foobar" and "union foobar".
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000382 " Also do "class foobar" when it's C++ after all (doesn't work very well
383 " though).
384 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union' || tokens[tidx] == 'class') && tidx + 1 < len(tokens)
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000385 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000386 break
387 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000388
Bram Moolenaara4a08382005-09-09 19:52:02 +0000389 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000390 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000391 continue
392 endif
393
394 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000395 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000396 for tagidx in range(len(diclist))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000397 let item = diclist[tagidx]
398
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000399 " New ctags has the "typeref" field. Patched version has "typename".
400 if has_key(item, 'typeref')
401 call extend(res, s:StructMembers(item['typeref'], a:items, a:all))
402 continue
403 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000404 if has_key(item, 'typename')
405 call extend(res, s:StructMembers(item['typename'], a:items, a:all))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000406 continue
407 endif
408
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000409 " Only handle typedefs here.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000410 if item['kind'] != 't'
411 continue
412 endif
413
414 " Skip matches local to another file.
415 if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename'])
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000416 continue
417 endif
418
419 " For old ctags we recognize "typedef struct aaa" and
420 " "typedef union bbb" in the tags file command.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000421 let cmd = item['cmd']
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000422 let ei = matchend(cmd, 'typedef\s\+')
423 if ei > 1
424 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
425 if len(cmdtokens) > 1
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000426 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union' || cmdtokens[0] == 'class'
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000427 let name = ''
428 " Use the first identifier after the "struct" or "union"
429 for ti in range(len(cmdtokens) - 1)
430 if cmdtokens[ti] =~ '^\w'
431 let name = cmdtokens[ti]
432 break
433 endif
434 endfor
435 if name != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000436 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000437 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000438 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000439 " Could be "typedef other_T some_T".
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000440 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000441 endif
442 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000443 endif
444 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000445 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000446 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000447 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000448 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000449
Bram Moolenaara4a08382005-09-09 19:52:02 +0000450 return res
451endfunction
452
453
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000454" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000455" Return a list with resulting matches.
456" Each match is a dictionary with "match" and "tagline" entries.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000457" When "all" is non-zero find all, otherwise just return 1 if there is any
458" member.
459function! s:StructMembers(typename, items, all)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000460 " Todo: What about local structures?
Bram Moolenaar862c27a2006-05-13 09:09:15 +0000461 let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000462 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000463 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000464 endif
465
466 let typename = a:typename
467 let qflist = []
Bram Moolenaar1056d982006-03-09 22:37:52 +0000468 let cached = 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000469 if a:all == 0
470 let n = '1' " stop at first found match
Bram Moolenaar1056d982006-03-09 22:37:52 +0000471 if has_key(s:grepCache, a:typename)
472 let qflist = s:grepCache[a:typename]
473 let cached = 1
474 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000475 else
476 let n = ''
477 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000478 if !cached
479 while 1
480 exe 'silent! ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
481
482 let qflist = getqflist()
483 if len(qflist) > 0 || match(typename, "::") < 0
484 break
485 endif
486 " No match for "struct:context::name", remove "context::" and try again.
487 let typename = substitute(typename, ':[^:]*::', ':', '')
488 endwhile
489
490 if a:all == 0
491 " Store the result to be able to use it again later.
492 let s:grepCache[a:typename] = qflist
Bram Moolenaara4a08382005-09-09 19:52:02 +0000493 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000494 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000495
Bram Moolenaareb94e552006-03-11 21:35:11 +0000496 " Put matching members in matches[].
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000497 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000498 for l in qflist
499 let memb = matchstr(l['text'], '[^\t]*')
500 if memb =~ '^' . a:items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000501 " Skip matches local to another file.
502 if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*'))
503 let item = {'match': memb, 'tagline': l['text']}
504
505 " Add the kind of item.
506 let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
507 if s != ''
508 let item['kind'] = s
509 if s == 'f'
510 let item['match'] = memb . '('
511 endif
512 endif
513
514 call add(matches, item)
515 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000516 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000517 endfor
518
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000519 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000520 " Skip over [...] items
521 let idx = 1
522 while 1
523 if idx >= len(a:items)
524 return matches " No further items, return the result.
525 endif
526 if a:items[idx][0] != '['
527 break
528 endif
529 let idx += 1
530 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000531
532 " More items following. For each of the possible members find the
533 " matching following members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000534 return s:SearchMembers(matches, a:items[idx :], a:all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000535 endif
536
537 " Failed to find anything.
538 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000539endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000540
541" For matching members, find matches for following items.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000542" When "all" is non-zero find all, otherwise just return 1 if there is any
543" member.
544function! s:SearchMembers(matches, items, all)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000545 let res = []
546 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000547 let typename = ''
548 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000549 if has_key(a:matches[i].dict, 'typename')
550 let typename = a:matches[i].dict['typename']
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000551 elseif has_key(a:matches[i].dict, 'typeref')
552 let typename = a:matches[i].dict['typeref']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000553 endif
554 let line = "\t" . a:matches[i].dict['cmd']
555 else
556 let line = a:matches[i]['tagline']
557 let e = matchend(line, '\ttypename:')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000558 if e < 0
559 let e = matchend(line, '\ttyperef:')
560 endif
Bram Moolenaar280f1262006-01-30 00:14:18 +0000561 if e > 0
562 " Use typename field
563 let typename = matchstr(line, '[^\t]*', e)
564 endif
565 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000566
Bram Moolenaar280f1262006-01-30 00:14:18 +0000567 if typename != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000568 call extend(res, s:StructMembers(typename, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000569 else
570 " Use the search command (the declaration itself).
571 let s = match(line, '\t\zs/^')
572 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000573 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000574 if e > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000575 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000576 endif
577 endif
578 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000579 if a:all == 0 && len(res) > 0
580 break
581 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000582 endfor
583 return res
584endfunc