blob: 7a840b12e5da998d1b302885c915a4f9983e42cf [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 Moolenaar8b6144b2006-02-08 09:20:24 +00004" Last Change: 2006 Feb 06
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 Moolenaar8b6144b2006-02-08 09:20:24 +0000216 let x = substitute(a:val['cmd'], '^/^', '', '')
217 let x = substitute(x, '$/$', '', '')
218 let x = substitute(x, a:val['name'], '@@', '')
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000219 if has_key(a:val, "kind")
220 if a:val["kind"] == 'v'
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000221 return {'match': a:val['name'], 'tagline': "\t" . a:val['cmd'], 'dict': a:val, 'extra': x}
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000222 endif
223 if a:val["kind"] == 'f'
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000224 return {'match': a:val['name'] . '(', 'tagline': "", 'extra': x}
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000225 endif
226 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000227 return {'match': a:val['name'], 'tagline': '', 'extra': x}
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000228endfunction
229
230" Turn a match item "val" into an item for completion.
231" "val['match']" is the matching item.
232" "val['tagline']" is the tagline in which the last part was found.
233function! s:Tagline2item(val, brackets)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000234 let word = a:val['match'] . a:brackets . s:GetAddition(a:val['tagline'], a:val['match'], [a:val], a:brackets == '')
235 if has_key(a:val, 'extra')
236 return {'word': word, 'menu': a:val['extra']}
237 endif
238 return {'word': word, 'menu': substitute(a:val['tagline'], word, '@@', '')}
Bram Moolenaar280f1262006-01-30 00:14:18 +0000239endfunction
240
241
Bram Moolenaara4a08382005-09-09 19:52:02 +0000242" Find composing type in "lead" and match items[0] with it.
243" Repeat this recursively for items[1], if it's there.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000244" When resolving typedefs "depth" is used to avoid infinite recursion.
Bram Moolenaara4a08382005-09-09 19:52:02 +0000245" Return the list of matches.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000246function! s:Nextitem(lead, items, depth)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000247
248 " Use the text up to the variable name and split it in tokens.
249 let tokens = split(a:lead, '\s\+\|\<')
250
251 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000252 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000253 for tidx in range(len(tokens))
254
Bram Moolenaara4a08382005-09-09 19:52:02 +0000255 " Recognize "struct foobar" and "union foobar".
256 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000257 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000258 break
259 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000260
Bram Moolenaara4a08382005-09-09 19:52:02 +0000261 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000262 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000263 continue
264 endif
265
266 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000267 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000268 for tagidx in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000269 " New ctags has the "typename" field.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000270 if has_key(diclist[tagidx], 'typename')
271 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000272 continue
273 endif
274
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000275 " Only handle typedefs here.
276 if diclist[tagidx]['kind'] != 't'
277 continue
278 endif
279
280 " For old ctags we recognize "typedef struct aaa" and
281 " "typedef union bbb" in the tags file command.
282 let cmd = diclist[tagidx]['cmd']
283 let ei = matchend(cmd, 'typedef\s\+')
284 if ei > 1
285 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
286 if len(cmdtokens) > 1
287 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
288 let name = ''
289 " Use the first identifier after the "struct" or "union"
290 for ti in range(len(cmdtokens) - 1)
291 if cmdtokens[ti] =~ '^\w'
292 let name = cmdtokens[ti]
293 break
294 endif
295 endfor
296 if name != ''
297 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items))
298 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000299 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000300 " Could be "typedef other_T some_T".
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000301 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000302 endif
303 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000304 endif
305 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000306 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000307 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000308 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000309 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000310
Bram Moolenaara4a08382005-09-09 19:52:02 +0000311 return res
312endfunction
313
314
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000315" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000316" Return a list with resulting matches.
317" Each match is a dictionary with "match" and "tagline" entries.
318function! s:StructMembers(typename, items)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000319 " Todo: What about local structures?
320 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
321 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000322 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000323 endif
324
325 let typename = a:typename
326 let qflist = []
327 while 1
Bram Moolenaardcca87b2005-09-10 19:24:59 +0000328 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaara4a08382005-09-09 19:52:02 +0000329 let qflist = getqflist()
330 if len(qflist) > 0 || match(typename, "::") < 0
331 break
332 endif
333 " No match for "struct:context::name", remove "context::" and try again.
334 let typename = substitute(typename, ':[^:]*::', ':', '')
335 endwhile
336
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000337 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000338 for l in qflist
339 let memb = matchstr(l['text'], '[^\t]*')
340 if memb =~ '^' . a:items[0]
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000341 call add(matches, {'match': memb, 'tagline': l['text']})
Bram Moolenaara4a08382005-09-09 19:52:02 +0000342 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000343 endfor
344
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000345 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000346 " Skip over [...] items
347 let idx = 1
348 while 1
349 if idx >= len(a:items)
350 return matches " No further items, return the result.
351 endif
352 if a:items[idx][0] != '['
353 break
354 endif
355 let idx += 1
356 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000357
358 " More items following. For each of the possible members find the
359 " matching following members.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000360 return s:SearchMembers(matches, a:items[idx :])
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000361 endif
362
363 " Failed to find anything.
364 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000365endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000366
367" For matching members, find matches for following items.
368function! s:SearchMembers(matches, items)
369 let res = []
370 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000371 let typename = ''
372 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000373 if has_key(a:matches[i].dict, 'typename')
374 let typename = a:matches[i].dict['typename']
375 endif
376 let line = "\t" . a:matches[i].dict['cmd']
377 else
378 let line = a:matches[i]['tagline']
379 let e = matchend(line, '\ttypename:')
380 if e > 0
381 " Use typename field
382 let typename = matchstr(line, '[^\t]*', e)
383 endif
384 endif
385 if typename != ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000386 call extend(res, s:StructMembers(typename, a:items))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000387 else
388 " Use the search command (the declaration itself).
389 let s = match(line, '\t\zs/^')
390 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000391 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000392 if e > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000393 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000394 endif
395 endif
396 endif
397 endfor
398 return res
399endfunc