502
|
1 " Vim completion script
|
|
2 " Language: C
|
|
3 " Maintainer: Bram Moolenaar <Bram@vim.org>
|
610
|
4 " Last Change: 2005 Dec 18
|
502
|
5
|
516
|
6
|
523
|
7 " This function is used for the 'omnifunc' option.
|
502
|
8 function! ccomplete#Complete(findstart, base)
|
|
9 if a:findstart
|
511
|
10 " Locate the start of the item, including "." and "->".
|
502
|
11 let line = getline('.')
|
|
12 let start = col('.') - 1
|
548
|
13 let lastword = -1
|
502
|
14 while start > 0
|
548
|
15 if line[start - 1] =~ '\w'
|
|
16 let start -= 1
|
|
17 elseif line[start - 1] =~ '\.'
|
|
18 if lastword == -1
|
|
19 let lastword = start
|
|
20 endif
|
502
|
21 let start -= 1
|
|
22 elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
|
548
|
23 if lastword == -1
|
|
24 let lastword = start
|
|
25 endif
|
502
|
26 let start -= 2
|
|
27 else
|
|
28 break
|
|
29 endif
|
|
30 endwhile
|
548
|
31
|
|
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
|
502
|
40 endif
|
|
41
|
511
|
42 " Return list of matches.
|
|
43
|
548
|
44 let base = s:prepended . a:base
|
|
45
|
511
|
46 " Split item in words, keep empty word after "." or "->".
|
|
47 " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
|
548
|
48 let items = split(base, '\.\|->', 1)
|
511
|
49 if len(items) <= 1
|
548
|
50 " 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
|
502
|
56 " Only one part, no "." or "->": complete from tags file.
|
511
|
57 " When local completion is wanted CTRL-N would have been used.
|
548
|
58 return map(taglist('^' . base), 'v:val["name"]')
|
502
|
59 endif
|
505
|
60
|
516
|
61 " 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 = []
|
523
|
66 if searchdecl(items[0], 0, 1) == 0
|
505
|
67 " Found, now figure out the type.
|
|
68 " TODO: join previous line if it makes sense
|
|
69 let line = getline('.')
|
|
70 let col = col('.')
|
523
|
71 let res = s:Nextitem(strpart(line, 0, col), items[1:])
|
516
|
72 endif
|
|
73
|
|
74 if len(res) == 0
|
|
75 " Find the variable in the tags file(s)
|
511
|
76 let diclist = taglist('^' . items[0] . '$')
|
|
77
|
|
78 let res = []
|
505
|
79 for i in range(len(diclist))
|
516
|
80 " New ctags has the "typename" field.
|
|
81 if has_key(diclist[i], 'typename')
|
523
|
82 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:]))
|
516
|
83 endif
|
|
84
|
|
85 " For a variable use the command, which must be a search pattern that
|
|
86 " shows the declaration of the variable.
|
505
|
87 if diclist[i]['kind'] == 'v'
|
|
88 let line = diclist[i]['cmd']
|
|
89 if line[0] == '/' && line[1] == '^'
|
610
|
90 let col = match(line, '\<' . items[0] . '\>')
|
523
|
91 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:]))
|
505
|
92 endif
|
|
93 endif
|
|
94 endfor
|
|
95 endif
|
|
96
|
516
|
97 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('.')
|
523
|
102 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
|
516
|
114 endif
|
|
115
|
548
|
116 return map(res, 'v:val["match"]')
|
511
|
117 endfunc
|
|
118
|
516
|
119 " 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.
|
523
|
122 function! s:Nextitem(lead, items)
|
511
|
123
|
|
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...
|
516
|
128 let res = []
|
511
|
129 for tidx in range(len(tokens))
|
505
|
130
|
516
|
131 " Recognize "struct foobar" and "union foobar".
|
|
132 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
|
523
|
133 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
|
511
|
134 break
|
|
135 endif
|
|
136
|
516
|
137 " 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.
|
511
|
143 let diclist = taglist('^' . tokens[tidx] . '$')
|
523
|
144 for tagidx in range(len(diclist))
|
516
|
145 " New ctags has the "typename" field.
|
523
|
146 if has_key(diclist[tagidx], 'typename')
|
|
147 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
|
|
148 continue
|
|
149 endif
|
|
150
|
|
151 " Only handle typedefs here.
|
|
152 if diclist[tagidx]['kind'] != 't'
|
516
|
153 continue
|
|
154 endif
|
|
155
|
523
|
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
|
511
|
180 endif
|
|
181 endfor
|
516
|
182 if len(res) > 0
|
505
|
183 break
|
|
184 endif
|
516
|
185 endfor
|
511
|
186
|
516
|
187 return res
|
|
188 endfunction
|
|
189
|
|
190
|
523
|
191 " Return a list with resulting matches.
|
|
192 " Each match is a dictionary with "match" and "tagline" entries.
|
|
193 function! s:StructMembers(typename, items)
|
516
|
194 " Todo: What about local structures?
|
|
195 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
|
|
196 if fnames == ''
|
523
|
197 return []
|
516
|
198 endif
|
|
199
|
|
200 let typename = a:typename
|
|
201 let qflist = []
|
|
202 while 1
|
520
|
203 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
|
516
|
204 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
|
523
|
212 let matches = []
|
516
|
213 for l in qflist
|
|
214 let memb = matchstr(l['text'], '[^\t]*')
|
|
215 if memb =~ '^' . a:items[0]
|
523
|
216 call add(matches, {'match': memb, 'tagline': l['text']})
|
516
|
217 endif
|
511
|
218 endfor
|
505
|
219
|
523
|
220 if len(matches) > 0
|
516
|
221 " No further items, return the result.
|
511
|
222 if len(a:items) == 1
|
523
|
223 return matches
|
511
|
224 endif
|
505
|
225
|
511
|
226 " More items following. For each of the possible members find the
|
|
227 " matching following members.
|
523
|
228 return s:SearchMembers(matches, a:items[1:])
|
511
|
229 endif
|
|
230
|
|
231 " Failed to find anything.
|
|
232 return []
|
|
233 endfunction
|
523
|
234
|
|
235 " For matching members, find matches for following items.
|
|
236 function! 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
|
|
257 endfunc
|