blob: 2d5e0ee1e74a0cbf0edcb762967f5ee9c5977dac [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 Moolenaar76b92b22006-03-24 22:46:53 +00004" Last Change: 2006 Mar 24
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".
382 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000383 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000384 break
385 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000386
Bram Moolenaara4a08382005-09-09 19:52:02 +0000387 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000388 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000389 continue
390 endif
391
392 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000393 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000394 for tagidx in range(len(diclist))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000395 let item = diclist[tagidx]
396
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000397 " New ctags has the "typeref" field. Patched version has "typename".
398 if has_key(item, 'typeref')
399 call extend(res, s:StructMembers(item['typeref'], a:items, a:all))
400 continue
401 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000402 if has_key(item, 'typename')
403 call extend(res, s:StructMembers(item['typename'], a:items, a:all))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000404 continue
405 endif
406
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000407 " Only handle typedefs here.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000408 if item['kind'] != 't'
409 continue
410 endif
411
412 " Skip matches local to another file.
413 if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename'])
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000414 continue
415 endif
416
417 " For old ctags we recognize "typedef struct aaa" and
418 " "typedef union bbb" in the tags file command.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000419 let cmd = item['cmd']
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000420 let ei = matchend(cmd, 'typedef\s\+')
421 if ei > 1
422 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
423 if len(cmdtokens) > 1
424 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
425 let name = ''
426 " Use the first identifier after the "struct" or "union"
427 for ti in range(len(cmdtokens) - 1)
428 if cmdtokens[ti] =~ '^\w'
429 let name = cmdtokens[ti]
430 break
431 endif
432 endfor
433 if name != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000434 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000435 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000436 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000437 " Could be "typedef other_T some_T".
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000438 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000439 endif
440 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000441 endif
442 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000443 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000444 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000445 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000446 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000447
Bram Moolenaara4a08382005-09-09 19:52:02 +0000448 return res
449endfunction
450
451
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000452" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000453" Return a list with resulting matches.
454" Each match is a dictionary with "match" and "tagline" entries.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000455" When "all" is non-zero find all, otherwise just return 1 if there is any
456" member.
457function! s:StructMembers(typename, items, all)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000458 " Todo: What about local structures?
459 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
460 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000461 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000462 endif
463
464 let typename = a:typename
465 let qflist = []
Bram Moolenaar1056d982006-03-09 22:37:52 +0000466 let cached = 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000467 if a:all == 0
468 let n = '1' " stop at first found match
Bram Moolenaar1056d982006-03-09 22:37:52 +0000469 if has_key(s:grepCache, a:typename)
470 let qflist = s:grepCache[a:typename]
471 let cached = 1
472 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000473 else
474 let n = ''
475 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000476 if !cached
477 while 1
478 exe 'silent! ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
479
480 let qflist = getqflist()
481 if len(qflist) > 0 || match(typename, "::") < 0
482 break
483 endif
484 " No match for "struct:context::name", remove "context::" and try again.
485 let typename = substitute(typename, ':[^:]*::', ':', '')
486 endwhile
487
488 if a:all == 0
489 " Store the result to be able to use it again later.
490 let s:grepCache[a:typename] = qflist
Bram Moolenaara4a08382005-09-09 19:52:02 +0000491 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000492 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000493
Bram Moolenaareb94e552006-03-11 21:35:11 +0000494 " Put matching members in matches[].
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000495 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000496 for l in qflist
497 let memb = matchstr(l['text'], '[^\t]*')
498 if memb =~ '^' . a:items[0]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000499 " Skip matches local to another file.
500 if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*'))
501 let item = {'match': memb, 'tagline': l['text']}
502
503 " Add the kind of item.
504 let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
505 if s != ''
506 let item['kind'] = s
507 if s == 'f'
508 let item['match'] = memb . '('
509 endif
510 endif
511
512 call add(matches, item)
513 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000514 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000515 endfor
516
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000517 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000518 " Skip over [...] items
519 let idx = 1
520 while 1
521 if idx >= len(a:items)
522 return matches " No further items, return the result.
523 endif
524 if a:items[idx][0] != '['
525 break
526 endif
527 let idx += 1
528 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000529
530 " More items following. For each of the possible members find the
531 " matching following members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000532 return s:SearchMembers(matches, a:items[idx :], a:all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000533 endif
534
535 " Failed to find anything.
536 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000537endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000538
539" For matching members, find matches for following items.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000540" When "all" is non-zero find all, otherwise just return 1 if there is any
541" member.
542function! s:SearchMembers(matches, items, all)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000543 let res = []
544 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000545 let typename = ''
546 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000547 if has_key(a:matches[i].dict, 'typename')
548 let typename = a:matches[i].dict['typename']
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000549 elseif has_key(a:matches[i].dict, 'typeref')
550 let typename = a:matches[i].dict['typeref']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000551 endif
552 let line = "\t" . a:matches[i].dict['cmd']
553 else
554 let line = a:matches[i]['tagline']
555 let e = matchend(line, '\ttypename:')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000556 if e < 0
557 let e = matchend(line, '\ttyperef:')
558 endif
Bram Moolenaar280f1262006-01-30 00:14:18 +0000559 if e > 0
560 " Use typename field
561 let typename = matchstr(line, '[^\t]*', e)
562 endif
563 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000564
Bram Moolenaar280f1262006-01-30 00:14:18 +0000565 if typename != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000566 call extend(res, s:StructMembers(typename, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000567 else
568 " Use the search command (the declaration itself).
569 let s = match(line, '\t\zs/^')
570 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000571 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000572 if e > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000573 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000574 endif
575 endif
576 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000577 if a:all == 0 && len(res) > 0
578 break
579 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000580 endfor
581 return res
582endfunc