blob: 55e45b0a525f953a5e08fec0bf1940162b9c3cab [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 Moolenaar1f35bf92006-03-07 22:38:47 +00004" Last Change: 2006 Mar 07
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
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000126 let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000127 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.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000139 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000140 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 Moolenaar1f35bf92006-03-07 22:38:47 +0000156 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 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 Moolenaar1f35bf92006-03-07 22:38:47 +0000165 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1))
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 Moolenaar1f35bf92006-03-07 22:38:47 +0000176 let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1)
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.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000200 if len(s:SearchMembers(a:memarg, [''], 0)) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000201 " 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.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000251 let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/')
252 let x = substitute(x, '\<' . a:name . '\>', '@@', '')
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000253 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 Moolenaar1f35bf92006-03-07 22:38:47 +0000269function! s:Nextitem(lead, items, depth, all)
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 Moolenaar1f35bf92006-03-07 22:38:47 +0000280 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all)
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')
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000294 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items, a:all))
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 != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000320 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000321 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 Moolenaar1f35bf92006-03-07 22:38:47 +0000324 call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all))
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.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000341" When "all" is non-zero find all, otherwise just return 1 if there is any
342" member.
343function! s:StructMembers(typename, items, all)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000344 " Todo: What about local structures?
345 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
346 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000347 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000348 endif
349
350 let typename = a:typename
351 let qflist = []
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000352 if a:all == 0
353 let n = '1' " stop at first found match
354 else
355 let n = ''
356 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000357 while 1
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000358 exe 'silent! ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaara4a08382005-09-09 19:52:02 +0000359 let qflist = getqflist()
360 if len(qflist) > 0 || match(typename, "::") < 0
361 break
362 endif
363 " No match for "struct:context::name", remove "context::" and try again.
364 let typename = substitute(typename, ':[^:]*::', ':', '')
365 endwhile
366
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000367 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000368 for l in qflist
369 let memb = matchstr(l['text'], '[^\t]*')
370 if memb =~ '^' . a:items[0]
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000371 call add(matches, {'match': memb, 'tagline': l['text']})
Bram Moolenaara4a08382005-09-09 19:52:02 +0000372 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000373 endfor
374
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000375 if len(matches) > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000376 " Skip over [...] items
377 let idx = 1
378 while 1
379 if idx >= len(a:items)
380 return matches " No further items, return the result.
381 endif
382 if a:items[idx][0] != '['
383 break
384 endif
385 let idx += 1
386 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000387
388 " More items following. For each of the possible members find the
389 " matching following members.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000390 return s:SearchMembers(matches, a:items[idx :], a:all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000391 endif
392
393 " Failed to find anything.
394 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000395endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000396
397" For matching members, find matches for following items.
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000398" When "all" is non-zero find all, otherwise just return 1 if there is any
399" member.
400function! s:SearchMembers(matches, items, all)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000401 let res = []
402 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000403 let typename = ''
404 if has_key(a:matches[i], 'dict')
Bram Moolenaar280f1262006-01-30 00:14:18 +0000405 if has_key(a:matches[i].dict, 'typename')
406 let typename = a:matches[i].dict['typename']
407 endif
408 let line = "\t" . a:matches[i].dict['cmd']
409 else
410 let line = a:matches[i]['tagline']
411 let e = matchend(line, '\ttypename:')
412 if e > 0
413 " Use typename field
414 let typename = matchstr(line, '[^\t]*', e)
415 endif
416 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000417
Bram Moolenaar280f1262006-01-30 00:14:18 +0000418 if typename != ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000419 call extend(res, s:StructMembers(typename, a:items, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000420 else
421 " Use the search command (the declaration itself).
422 let s = match(line, '\t\zs/^')
423 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000424 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000425 if e > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000426 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000427 endif
428 endif
429 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000430 if a:all == 0 && len(res) > 0
431 break
432 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000433 endfor
434 return res
435endfunc