blob: 8fde2d33ebb09e21bf5733ee1293637a47db7afe [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 Moolenaar1ef15e32006-02-01 21:56:25 +00004" Last Change: 2006 Jan 30
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 Moolenaarcaa0fcf2005-09-07 21:21:14 +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
27 else
28 break
29 endif
30 endwhile
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000031
32 " Return the column of the last word, which is going to be changed.
33 " Remember the text that comes before it in s:prepended.
34 if lastword == -1
35 let s:prepended = ''
36 return start
37 endif
38 let s:prepended = strpart(line, start, lastword - start)
39 return lastword
Bram Moolenaare344bea2005-09-01 20:46:49 +000040 endif
41
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000042 " Return list of matches.
43
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000044 let base = s:prepended . a:base
45
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000046 " Split item in words, keep empty word after "." or "->".
47 " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000048 let items = split(base, '\.\|->', 1)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000049 if len(items) <= 1
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000050 " Don't do anything for an empty base, would result in all the tags in the
51 " tags file.
52 if base == ''
53 return []
54 endif
55
Bram Moolenaare344bea2005-09-01 20:46:49 +000056 " Only one part, no "." or "->": complete from tags file.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000057 " When local completion is wanted CTRL-N would have been used.
Bram Moolenaar280f1262006-01-30 00:14:18 +000058 return map(taglist('^' . base), 's:Tag2item(v:val)')
Bram Moolenaare344bea2005-09-01 20:46:49 +000059 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +000060
Bram Moolenaara4a08382005-09-09 19:52:02 +000061 " Find the variable items[0].
62 " 1. in current function (like with "gd")
63 " 2. in tags file(s) (like with ":tag")
64 " 3. in current file (like with "gD")
65 let res = []
Bram Moolenaarf75a9632005-09-13 21:20:47 +000066 if searchdecl(items[0], 0, 1) == 0
Bram Moolenaardd2436f2005-09-05 22:14:46 +000067 " Found, now figure out the type.
68 " TODO: join previous line if it makes sense
69 let line = getline('.')
70 let col = col('.')
Bram Moolenaarf75a9632005-09-13 21:20:47 +000071 let res = s:Nextitem(strpart(line, 0, col), items[1:])
Bram Moolenaara4a08382005-09-09 19:52:02 +000072 endif
73
74 if len(res) == 0
75 " Find the variable in the tags file(s)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000076 let diclist = taglist('^' . items[0] . '$')
77
78 let res = []
Bram Moolenaardd2436f2005-09-05 22:14:46 +000079 for i in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +000080 " New ctags has the "typename" field.
81 if has_key(diclist[i], 'typename')
Bram Moolenaarf75a9632005-09-13 21:20:47 +000082 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:]))
Bram Moolenaara4a08382005-09-09 19:52:02 +000083 endif
84
85 " For a variable use the command, which must be a search pattern that
86 " shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +000087 if diclist[i]['kind'] == 'v'
88 let line = diclist[i]['cmd']
89 if line[0] == '/' && line[1] == '^'
Bram Moolenaare3226be2005-12-18 22:10:00 +000090 let col = match(line, '\<' . items[0] . '\>')
Bram Moolenaarf75a9632005-09-13 21:20:47 +000091 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:]))
Bram Moolenaardd2436f2005-09-05 22:14:46 +000092 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +000093 endif
94 endfor
95 endif
96
Bram Moolenaara4a08382005-09-09 19:52:02 +000097 if len(res) == 0 && searchdecl(items[0], 1) == 0
98 " Found, now figure out the type.
99 " TODO: join previous line if it makes sense
100 let line = getline('.')
101 let col = col('.')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000102 let res = s:Nextitem(strpart(line, 0, col), items[1:])
103 endif
104
105 " If the one and only match was what's already there and it is a composite
106 " type, add a "." or "->".
107 if len(res) == 1 && res[0]['match'] == items[-1] && len(s:SearchMembers(res, [''])) > 0
108 " If there is a '*' before the name use "->".
Bram Moolenaar280f1262006-01-30 00:14:18 +0000109 if match(res[0]['tagline'], '\*\s*' . res[0]['match'] . '\>') > 0
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000110 let res[0]['match'] .= '->'
111 else
112 let res[0]['match'] .= '.'
113 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000114 endif
115
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000116 return map(res, 'v:val["match"]')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000117endfunc
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000118
Bram Moolenaar280f1262006-01-30 00:14:18 +0000119"
120" Turn the tag info "val" into an item for completion.
121" "val" is is an item in the list returned by taglist().
122function! s:Tag2item(val)
123 if has_key(a:val, "kind") && a:val["kind"] == 'v'
124 if len(s:SearchMembers([{'match': a:val["name"], 'dict': a:val}], [''])) > 0
125 " If there is a '*' before the name use "->". This assumes the command
126 " is a search pattern!
127 if match(a:val['cmd'], '\*\s*' . a:val['name'] . '\>') > 0
128 return a:val["name"] . '->'
129 else
130 return a:val["name"] . '.'
131 endif
132 endif
133 endif
134 return a:val["name"]
135endfunction
136
137
Bram Moolenaara4a08382005-09-09 19:52:02 +0000138" Find composing type in "lead" and match items[0] with it.
139" Repeat this recursively for items[1], if it's there.
140" Return the list of matches.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000141function! s:Nextitem(lead, items)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000142
143 " Use the text up to the variable name and split it in tokens.
144 let tokens = split(a:lead, '\s\+\|\<')
145
146 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000147 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000148 for tidx in range(len(tokens))
149
Bram Moolenaara4a08382005-09-09 19:52:02 +0000150 " Recognize "struct foobar" and "union foobar".
151 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000152 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000153 break
154 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000155
Bram Moolenaara4a08382005-09-09 19:52:02 +0000156 " TODO: add more reserved words
157 if index(['int', 'float', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
158 continue
159 endif
160
161 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000162 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000163 for tagidx in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000164 " New ctags has the "typename" field.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000165 if has_key(diclist[tagidx], 'typename')
166 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000167 continue
168 endif
169
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000170 " Only handle typedefs here.
171 if diclist[tagidx]['kind'] != 't'
172 continue
173 endif
174
175 " For old ctags we recognize "typedef struct aaa" and
176 " "typedef union bbb" in the tags file command.
177 let cmd = diclist[tagidx]['cmd']
178 let ei = matchend(cmd, 'typedef\s\+')
179 if ei > 1
180 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
181 if len(cmdtokens) > 1
182 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
183 let name = ''
184 " Use the first identifier after the "struct" or "union"
185 for ti in range(len(cmdtokens) - 1)
186 if cmdtokens[ti] =~ '^\w'
187 let name = cmdtokens[ti]
188 break
189 endif
190 endfor
191 if name != ''
192 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items))
193 endif
194 else
195 " Could be "typedef other_T some_T".
196 call extend(res, s:Nextitem(cmdtokens[0], a:items))
197 endif
198 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000199 endif
200 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000201 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000202 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000203 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000204 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000205
Bram Moolenaara4a08382005-09-09 19:52:02 +0000206 return res
207endfunction
208
209
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000210" Return a list with resulting matches.
211" Each match is a dictionary with "match" and "tagline" entries.
212function! s:StructMembers(typename, items)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000213 " Todo: What about local structures?
214 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
215 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000216 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000217 endif
218
219 let typename = a:typename
220 let qflist = []
221 while 1
Bram Moolenaardcca87b2005-09-10 19:24:59 +0000222 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaara4a08382005-09-09 19:52:02 +0000223 let qflist = getqflist()
224 if len(qflist) > 0 || match(typename, "::") < 0
225 break
226 endif
227 " No match for "struct:context::name", remove "context::" and try again.
228 let typename = substitute(typename, ':[^:]*::', ':', '')
229 endwhile
230
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000231 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000232 for l in qflist
233 let memb = matchstr(l['text'], '[^\t]*')
234 if memb =~ '^' . a:items[0]
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000235 call add(matches, {'match': memb, 'tagline': l['text']})
Bram Moolenaara4a08382005-09-09 19:52:02 +0000236 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000237 endfor
238
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000239 if len(matches) > 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000240 " No further items, return the result.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000241 if len(a:items) == 1
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000242 return matches
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000243 endif
244
245 " More items following. For each of the possible members find the
246 " matching following members.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000247 return s:SearchMembers(matches, a:items[1:])
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000248 endif
249
250 " Failed to find anything.
251 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000252endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000253
254" For matching members, find matches for following items.
255function! s:SearchMembers(matches, items)
256 let res = []
257 for i in range(len(a:matches))
Bram Moolenaar280f1262006-01-30 00:14:18 +0000258 let typename = ''
259 if has_key(a:matches[i], 'dict')
260 "if a:matches[i].dict['name'] == "gui"
261 "echomsg string(a:matches[i].dict)
262 "endif
263 if has_key(a:matches[i].dict, 'typename')
264 let typename = a:matches[i].dict['typename']
265 endif
266 let line = "\t" . a:matches[i].dict['cmd']
267 else
268 let line = a:matches[i]['tagline']
269 let e = matchend(line, '\ttypename:')
270 if e > 0
271 " Use typename field
272 let typename = matchstr(line, '[^\t]*', e)
273 endif
274 endif
275 if typename != ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000276 call extend(res, s:StructMembers(name, a:items))
277 else
278 " Use the search command (the declaration itself).
279 let s = match(line, '\t\zs/^')
280 if s > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000281 let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000282 if e > 0
Bram Moolenaar280f1262006-01-30 00:14:18 +0000283 "if a:matches[i].dict['name'] == "gui"
284 "echomsg strpart(line, s, e - s)
285 "endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000286 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items))
287 endif
288 endif
289 endif
290 endfor
291 return res
292endfunc