blob: 624fdb3a0e0b55ce3217702165a015bcd090c671 [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 Moolenaarf52c7252006-02-10 23:23:57 +00004" Last Change: 2006 Feb 10
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 Moolenaarcaa0fcf2005-09-07 21:21:14 +000067 " Split item in words, keep empty word after "." or "->".
68 " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000069 " We can't use split, because we need to skip nested [...].
70 let items = []
71 let s = 0
72 while 1
73 let e = match(base, '\.\|->\|\[', s)
74 if e < 0
75 if s == 0 || base[s - 1] != ']'
76 call add(items, strpart(base, s))
77 endif
78 break
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000079 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000080 if s == 0 || base[s - 1] != ']'
81 call add(items, strpart(base, s, e - s))
82 endif
83 if base[e] == '.'
84 let s = e + 1 " skip over '.'
85 elseif base[e] == '-'
86 let s = e + 2 " skip over '->'
87 else
88 " Skip over [...].
89 let n = 0
90 let s = e
91 let e += 1
92 while e < len(base)
93 if base[e] == ']'
94 if n == 0
95 break
96 endif
97 let n -= 1
98 elseif base[e] == '[' " nested [...]
99 let n += 1
100 endif
101 let e += 1
102 endwhile
103 let e += 1
104 call add(items, strpart(base, s, e - s))
105 let s = e
106 endif
107 endwhile
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000108
Bram Moolenaara4a08382005-09-09 19:52:02 +0000109 " Find the variable items[0].
110 " 1. in current function (like with "gd")
111 " 2. in tags file(s) (like with ":tag")
112 " 3. in current file (like with "gD")
113 let res = []
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000114 if searchdecl(items[0], 0, 1) == 0
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000115 " Found, now figure out the type.
116 " TODO: join previous line if it makes sense
117 let line = getline('.')
118 let col = col('.')
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000119 if len(items) == 1
120 " Completing one word and it's a local variable: May add '[', '.' or
121 " '->'.
122 let match = items[0]
123 if match(line, match . '\s*\[') > 0
124 let match .= '['
125 else
126 let res = s:Nextitem(strpart(line, 0, col), [''], 0)
127 if len(res) > 0
128 " There are members, thus add "." or "->".
129 if match(line, '\*[ \t(]*' . match . '\>') > 0
130 let match .= '->'
131 else
132 let match .= '.'
133 endif
134 endif
135 endif
136 let res = [{'match': match, 'tagline' : ''}]
137 else
138 " Completing "var.", "var.something", etc.
139 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0)
140 endif
141 endif
142
143 if len(items) == 1
144 " Only one part, no "." or "->": complete from tags file.
145 call extend(res, map(taglist('^' . base), 's:Tag2item(v:val)'))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000146 endif
147
148 if len(res) == 0
149 " Find the variable in the tags file(s)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000150 let diclist = taglist('^' . items[0] . '$')
151
152 let res = []
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000153 for i in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000154 " New ctags has the "typename" field.
155 if has_key(diclist[i], 'typename')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000156 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:]))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000157 endif
158
159 " For a variable use the command, which must be a search pattern that
160 " shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000161 if diclist[i]['kind'] == 'v'
162 let line = diclist[i]['cmd']
163 if line[0] == '/' && line[1] == '^'
Bram Moolenaare3226be2005-12-18 22:10:00 +0000164 let col = match(line, '\<' . items[0] . '\>')
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000165 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0))
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000166 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000167 endif
168 endfor
169 endif
170
Bram Moolenaara4a08382005-09-09 19:52:02 +0000171 if len(res) == 0 && searchdecl(items[0], 1) == 0
172 " Found, now figure out the type.
173 " TODO: join previous line if it makes sense
174 let line = getline('.')
175 let col = col('.')
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000176 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000177 endif
178
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000179 " If the last item(s) are [...] they need to be added to the matches.
180 let last = len(items) - 1
181 let brackets = ''
182 while last >= 0
183 if items[last][0] != '['
184 break
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000185 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000186 let brackets = items[last] . brackets
187 let last -= 1
188 endwhile
Bram Moolenaara4a08382005-09-09 19:52:02 +0000189
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000190 return map(res, 's:Tagline2item(v:val, brackets)')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000191endfunc
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000192
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000193function! s:GetAddition(line, match, memarg, bracket)
194 " Guess if the item is an array.
195 if a:bracket && match(a:line, a:match . '\s*\[') > 0
196 return '['
197 endif
198
199 " Check if the item has members.
200 if len(s:SearchMembers(a:memarg, [''])) > 0
201 " If there is a '*' before the name use "->".
202 if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0
203 return '->'
204 else
205 return '.'
Bram Moolenaar280f1262006-01-30 00:14:18 +0000206 endif
207 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000208 return ''
209endfunction
210
211" Turn the tag info "val" into an item for completion.
212" "val" is is an item in the list returned by taglist().
213" If it is a variable we may add "." or "->". Don't do it for other types,
214" such as a typedef, by not including the info that s:GetAddition() uses.
215function! s:Tag2item(val)
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000216 let x = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename'])
217
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000218 if has_key(a:val, "kind")
219 if a:val["kind"] == 'v'
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000220 return {'match': a:val['name'], 'tagline': "\t" . a:val['cmd'], 'dict': a:val, 'extra': x}
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000221 endif
222 if a:val["kind"] == 'f'
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000223 return {'match': a:val['name'] . '(', 'tagline': "", 'extra': x}
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000224 endif
225 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000226 return {'match': a:val['name'], 'tagline': '', 'extra': x}
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000227endfunction
228
229" Turn a match item "val" into an item for completion.
230" "val['match']" is the matching item.
231" "val['tagline']" is the tagline in which the last part was found.
232function! s:Tagline2item(val, brackets)
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000233 let line = a:val['tagline']
234 let word = a:val['match'] . a:brackets . s:GetAddition(line, a:val['match'], [a:val], a:brackets == '')
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000235 if has_key(a:val, 'extra')
236 return {'word': word, 'menu': a:val['extra']}
237 endif
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000238
239 " Isolate the command after the tag and filename.
240 let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
241 if s != ''
242 return {'word': word, 'menu': s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t'))}
243 endif
244 return {'word': word}
Bram Moolenaar280f1262006-01-30 00:14:18 +0000245endfunction
246
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000247" Turn a command from a tag line to something that is useful in the menu
248function! s:Tagcmd2extra(cmd, name, fname)
249 if a:cmd =~ '^/^'
250 " The command is a search command, useful to see what it is.
251 let x = matchstr(a:cmd, '^/^\zs.*\ze$/')
252 let x = substitute(x, a:name, '@@', '')
253 let x = substitute(x, '\\\(.\)', '\1', 'g')
254 let x = x . ' - ' . a:fname
255 elseif a:cmd =~ '^\d*$'
256 " The command is a line number, the file name is more useful.
257 let x = a:fname . ' - ' . a:cmd
258 else
259 " Not recognized, use command and file name.
260 let x = a:cmd . ' - ' . a:fname
261 endif
262 return x
263endfunction
Bram Moolenaar280f1262006-01-30 00:14:18 +0000264
Bram Moolenaara4a08382005-09-09 19:52:02 +0000265" Find composing type in "lead" and match items[0] with it.
266" Repeat this recursively for items[1], if it's there.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000267" When resolving typedefs "depth" is used to avoid infinite recursion.
Bram Moolenaara4a08382005-09-09 19:52:02 +0000268" Return the list of matches.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000269function! s:Nextitem(lead, items, depth)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000270
271 " Use the text up to the variable name and split it in tokens.
272 let tokens = split(a:lead, '\s\+\|\<')
273
274 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000275 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000276 for tidx in range(len(tokens))
277
Bram Moolenaara4a08382005-09-09 19:52:02 +0000278 " Recognize "struct foobar" and "union foobar".
279 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000280 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000281 break
282 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000283
Bram Moolenaara4a08382005-09-09 19:52:02 +0000284 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000285 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000286 continue
287 endif
288
289 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000290 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000291 for tagidx in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000292 " New ctags has the "typename" field.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000293 if has_key(diclist[tagidx], 'typename')
294 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000295 continue
296 endif
297
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000298 " Only handle typedefs here.
299 if diclist[tagidx]['kind'] != 't'
300 continue
301 endif
302
303 " For old ctags we recognize "typedef struct aaa" and
304 " "typedef union bbb" in the tags file command.
305 let cmd = diclist[tagidx]['cmd']
306 let ei = matchend(cmd, 'typedef\s\+')
307 if ei > 1
308 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
309 if len(cmdtokens) > 1
310 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
311 let name = ''
312 " Use the first identifier after the "struct" or "union"
313 for ti in range(len(cmdtokens) - 1)
314 if cmdtokens[ti] =~ '^\w'
315 let name = cmdtokens[ti]
316 break
317 endif
318 endfor
319 if name != ''
320 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items))
321 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000322 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000323 " Could be "typedef other_T some_T".
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000324 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000325 endif
326 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000327 endif
328 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000329 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000330 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000331 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000332 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000333
Bram Moolenaara4a08382005-09-09 19:52:02 +0000334 return res
335endfunction
336
337
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000338" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000339" Return a list with resulting matches.
340" Each match is a dictionary with "match" and "tagline" entries.
341function! s:StructMembers(typename, items)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000342 " Todo: What about local structures?
343 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
344 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000345 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000346 endif
347
348 let typename = a:typename
349 let qflist = []
350 while 1
Bram Moolenaardcca87b2005-09-10 19:24:59 +0000351 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaara4a08382005-09-09 19:52:02 +0000352 let qflist = getqflist()
353 if len(qflist) > 0 || match(typename, "::") < 0
354 break
355 endif
356 " No match for "struct:context::name", remove "context::" and try again.
357 let typename = substitute(typename, ':[^:]*::', ':', '')
358 endwhile
359
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000360 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000361 for l in qflist
362 let memb = matchstr(l['text'], '[^\t]*')
363 if memb =~ '^' . a:items[0]
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000364 call add(matches, {'match': memb, 'tagline': l['text']})
Bram Moolenaara4a08382005-09-09 19:52:02 +0000365 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000366 endfor
367
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000368 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000369 " Skip over [...] items
370 let idx = 1
371 while 1
372 if idx >= len(a:items)
373 return matches " No further items, return the result.
374 endif
375 if a:items[idx][0] != '['
376 break
377 endif
378 let idx += 1
379 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000380
381 " More items following. For each of the possible members find the
382 " matching following members.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000383 return s:SearchMembers(matches, a:items[idx :])
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000384 endif
385
386 " Failed to find anything.
387 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000388endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000389
390" For matching members, find matches for following items.
391function! s:SearchMembers(matches, items)
392 let res = []
393 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000394 let typename = ''
395 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000396 if has_key(a:matches[i].dict, 'typename')
397 let typename = a:matches[i].dict['typename']
398 endif
399 let line = "\t" . a:matches[i].dict['cmd']
400 else
401 let line = a:matches[i]['tagline']
402 let e = matchend(line, '\ttypename:')
403 if e > 0
404 " Use typename field
405 let typename = matchstr(line, '[^\t]*', e)
406 endif
407 endif
408 if typename != ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000409 call extend(res, s:StructMembers(typename, a:items))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000410 else
411 " Use the search command (the declaration itself).
412 let s = match(line, '\t\zs/^')
413 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000414 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000415 if e > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000416 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000417 endif
418 endif
419 endif
420 endfor
421 return res
422endfunc