blob: 5e0c2aea352055adcc5dd78442106e9bb4ed5631 [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 Moolenaar0e5bd962006-02-04 00:59:56 +00004" Last Change: 2006 Feb 03
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)
216 if has_key(a:val, "kind")
217 if a:val["kind"] == 'v'
218 return {'match': a:val['name'], 'tagline': "\t" . a:val['cmd'], 'dict': a:val}
219 endif
220 if a:val["kind"] == 'f'
221 return {'match': a:val['name'] . '(', 'tagline': ""}
222 endif
223 endif
224 return {'match': a:val['name'], 'tagline': ''}
225endfunction
226
227" Turn a match item "val" into an item for completion.
228" "val['match']" is the matching item.
229" "val['tagline']" is the tagline in which the last part was found.
230function! s:Tagline2item(val, brackets)
231 return a:val['match'] . a:brackets . s:GetAddition(a:val['tagline'], a:val['match'], [a:val], a:brackets == '')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000232endfunction
233
234
Bram Moolenaara4a08382005-09-09 19:52:02 +0000235" Find composing type in "lead" and match items[0] with it.
236" Repeat this recursively for items[1], if it's there.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000237" When resolving typedefs "depth" is used to avoid infinite recursion.
Bram Moolenaara4a08382005-09-09 19:52:02 +0000238" Return the list of matches.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000239function! s:Nextitem(lead, items, depth)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000240
241 " Use the text up to the variable name and split it in tokens.
242 let tokens = split(a:lead, '\s\+\|\<')
243
244 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000245 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000246 for tidx in range(len(tokens))
247
Bram Moolenaara4a08382005-09-09 19:52:02 +0000248 " Recognize "struct foobar" and "union foobar".
249 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000250 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000251 break
252 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000253
Bram Moolenaara4a08382005-09-09 19:52:02 +0000254 " TODO: add more reserved words
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000255 if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000256 continue
257 endif
258
259 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000260 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000261 for tagidx in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000262 " New ctags has the "typename" field.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000263 if has_key(diclist[tagidx], 'typename')
264 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000265 continue
266 endif
267
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000268 " Only handle typedefs here.
269 if diclist[tagidx]['kind'] != 't'
270 continue
271 endif
272
273 " For old ctags we recognize "typedef struct aaa" and
274 " "typedef union bbb" in the tags file command.
275 let cmd = diclist[tagidx]['cmd']
276 let ei = matchend(cmd, 'typedef\s\+')
277 if ei > 1
278 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
279 if len(cmdtokens) > 1
280 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
281 let name = ''
282 " Use the first identifier after the "struct" or "union"
283 for ti in range(len(cmdtokens) - 1)
284 if cmdtokens[ti] =~ '^\w'
285 let name = cmdtokens[ti]
286 break
287 endif
288 endfor
289 if name != ''
290 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items))
291 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000292 elseif a:depth < 10
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000293 " Could be "typedef other_T some_T".
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000294 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000295 endif
296 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000297 endif
298 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000299 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000300 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000301 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000302 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000303
Bram Moolenaara4a08382005-09-09 19:52:02 +0000304 return res
305endfunction
306
307
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000308" Search for members of structure "typename" in tags files.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000309" Return a list with resulting matches.
310" Each match is a dictionary with "match" and "tagline" entries.
311function! s:StructMembers(typename, items)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000312 " Todo: What about local structures?
313 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
314 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000315 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000316 endif
317
318 let typename = a:typename
319 let qflist = []
320 while 1
Bram Moolenaardcca87b2005-09-10 19:24:59 +0000321 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaara4a08382005-09-09 19:52:02 +0000322 let qflist = getqflist()
323 if len(qflist) > 0 || match(typename, "::") < 0
324 break
325 endif
326 " No match for "struct:context::name", remove "context::" and try again.
327 let typename = substitute(typename, ':[^:]*::', ':', '')
328 endwhile
329
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000330 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000331 for l in qflist
332 let memb = matchstr(l['text'], '[^\t]*')
333 if memb =~ '^' . a:items[0]
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000334 call add(matches, {'match': memb, 'tagline': l['text']})
Bram Moolenaara4a08382005-09-09 19:52:02 +0000335 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000336 endfor
337
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000338 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000339 " Skip over [...] items
340 let idx = 1
341 while 1
342 if idx >= len(a:items)
343 return matches " No further items, return the result.
344 endif
345 if a:items[idx][0] != '['
346 break
347 endif
348 let idx += 1
349 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000350
351 " More items following. For each of the possible members find the
352 " matching following members.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000353 return s:SearchMembers(matches, a:items[idx :])
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000354 endif
355
356 " Failed to find anything.
357 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000358endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000359
360" For matching members, find matches for following items.
361function! s:SearchMembers(matches, items)
362 let res = []
363 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000364 let typename = ''
365 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000366 if has_key(a:matches[i].dict, 'typename')
367 let typename = a:matches[i].dict['typename']
368 endif
369 let line = "\t" . a:matches[i].dict['cmd']
370 else
371 let line = a:matches[i]['tagline']
372 let e = matchend(line, '\ttypename:')
373 if e > 0
374 " Use typename field
375 let typename = matchstr(line, '[^\t]*', e)
376 endif
377 endif
378 if typename != ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000379 call extend(res, s:StructMembers(typename, a:items))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000380 else
381 " Use the search command (the declaration itself).
382 let s = match(line, '\t\zs/^')
383 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000384 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000385 if e > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000386 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000387 endif
388 endif
389 endif
390 endfor
391 return res
392endfunc