blob: a2210d71b0eb092d107eb9f8c7c889eaf3e4b0e9 [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 Moolenaard5cdbeb2005-10-10 20:59:28 +00004" Last Change: 2005 Oct 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 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 Moolenaard5cdbeb2005-10-10 20:59:28 +000058 return map(taglist('^' . base), 'v:val["name"]')
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 Moolenaarcaa0fcf2005-09-07 21:21:14 +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 "->".
109 if match(res[0]['tagline'], '\*\s*' . res[0]['match']) > 0
110 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 Moolenaara4a08382005-09-09 19:52:02 +0000119" Find composing type in "lead" and match items[0] with it.
120" Repeat this recursively for items[1], if it's there.
121" Return the list of matches.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000122function! s:Nextitem(lead, items)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000123
124 " Use the text up to the variable name and split it in tokens.
125 let tokens = split(a:lead, '\s\+\|\<')
126
127 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000128 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000129 for tidx in range(len(tokens))
130
Bram Moolenaara4a08382005-09-09 19:52:02 +0000131 " Recognize "struct foobar" and "union foobar".
132 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000133 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000134 break
135 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000136
Bram Moolenaara4a08382005-09-09 19:52:02 +0000137 " TODO: add more reserved words
138 if index(['int', 'float', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
139 continue
140 endif
141
142 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000143 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000144 for tagidx in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000145 " New ctags has the "typename" field.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000146 if has_key(diclist[tagidx], 'typename')
147 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000148 continue
149 endif
150
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000151 " Only handle typedefs here.
152 if diclist[tagidx]['kind'] != 't'
153 continue
154 endif
155
156 " For old ctags we recognize "typedef struct aaa" and
157 " "typedef union bbb" in the tags file command.
158 let cmd = diclist[tagidx]['cmd']
159 let ei = matchend(cmd, 'typedef\s\+')
160 if ei > 1
161 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
162 if len(cmdtokens) > 1
163 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
164 let name = ''
165 " Use the first identifier after the "struct" or "union"
166 for ti in range(len(cmdtokens) - 1)
167 if cmdtokens[ti] =~ '^\w'
168 let name = cmdtokens[ti]
169 break
170 endif
171 endfor
172 if name != ''
173 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items))
174 endif
175 else
176 " Could be "typedef other_T some_T".
177 call extend(res, s:Nextitem(cmdtokens[0], a:items))
178 endif
179 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000180 endif
181 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000182 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000183 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000184 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000185 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000186
Bram Moolenaara4a08382005-09-09 19:52:02 +0000187 return res
188endfunction
189
190
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000191" Return a list with resulting matches.
192" Each match is a dictionary with "match" and "tagline" entries.
193function! s:StructMembers(typename, items)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000194 " Todo: What about local structures?
195 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
196 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000197 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000198 endif
199
200 let typename = a:typename
201 let qflist = []
202 while 1
Bram Moolenaardcca87b2005-09-10 19:24:59 +0000203 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaara4a08382005-09-09 19:52:02 +0000204 let qflist = getqflist()
205 if len(qflist) > 0 || match(typename, "::") < 0
206 break
207 endif
208 " No match for "struct:context::name", remove "context::" and try again.
209 let typename = substitute(typename, ':[^:]*::', ':', '')
210 endwhile
211
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000212 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000213 for l in qflist
214 let memb = matchstr(l['text'], '[^\t]*')
215 if memb =~ '^' . a:items[0]
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000216 call add(matches, {'match': memb, 'tagline': l['text']})
Bram Moolenaara4a08382005-09-09 19:52:02 +0000217 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000218 endfor
219
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000220 if len(matches) > 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000221 " No further items, return the result.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000222 if len(a:items) == 1
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000223 return matches
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000224 endif
225
226 " More items following. For each of the possible members find the
227 " matching following members.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000228 return s:SearchMembers(matches, a:items[1:])
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000229 endif
230
231 " Failed to find anything.
232 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000233endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000234
235" For matching members, find matches for following items.
236function! s:SearchMembers(matches, items)
237 let res = []
238 for i in range(len(a:matches))
239 let line = a:matches[i]['tagline']
240 let e = matchend(line, '\ttypename:')
241 if e > 0
242 " Use typename field
243 let name = matchstr(line, '[^\t]*', e)
244 call extend(res, s:StructMembers(name, a:items))
245 else
246 " Use the search command (the declaration itself).
247 let s = match(line, '\t\zs/^')
248 if s > 0
249 let e = match(line, a:matches[i]['match'], s)
250 if e > 0
251 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items))
252 endif
253 endif
254 endif
255 endfor
256 return res
257endfunc