blob: c71852530434ebf871096c97aaf998794de0d9dd [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 Moolenaarf75a9632005-09-13 21:20:47 +00004" Last Change: 2005 Sep 13
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
13 while start > 0
14 if line[start - 1] =~ '\w\|\.'
15 let start -= 1
16 elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
17 let start -= 2
18 else
19 break
20 endif
21 endwhile
22 return start
23 endif
24
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000025 " Return list of matches.
26
27 " Split item in words, keep empty word after "." or "->".
28 " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
29 let items = split(a:base, '\.\|->', 1)
30 if len(items) <= 1
Bram Moolenaare344bea2005-09-01 20:46:49 +000031 " Only one part, no "." or "->": complete from tags file.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000032 " When local completion is wanted CTRL-N would have been used.
33 return map(taglist('^' . a:base), 'v:val["name"]')
Bram Moolenaare344bea2005-09-01 20:46:49 +000034 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +000035
Bram Moolenaara4a08382005-09-09 19:52:02 +000036 " Find the variable items[0].
37 " 1. in current function (like with "gd")
38 " 2. in tags file(s) (like with ":tag")
39 " 3. in current file (like with "gD")
40 let res = []
Bram Moolenaarf75a9632005-09-13 21:20:47 +000041 if searchdecl(items[0], 0, 1) == 0
Bram Moolenaardd2436f2005-09-05 22:14:46 +000042 " Found, now figure out the type.
43 " TODO: join previous line if it makes sense
44 let line = getline('.')
45 let col = col('.')
Bram Moolenaarf75a9632005-09-13 21:20:47 +000046 let res = s:Nextitem(strpart(line, 0, col), items[1:])
Bram Moolenaara4a08382005-09-09 19:52:02 +000047 endif
48
49 if len(res) == 0
50 " Find the variable in the tags file(s)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000051 let diclist = taglist('^' . items[0] . '$')
52
53 let res = []
Bram Moolenaardd2436f2005-09-05 22:14:46 +000054 for i in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +000055 " New ctags has the "typename" field.
56 if has_key(diclist[i], 'typename')
Bram Moolenaarf75a9632005-09-13 21:20:47 +000057 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:]))
Bram Moolenaara4a08382005-09-09 19:52:02 +000058 endif
59
60 " For a variable use the command, which must be a search pattern that
61 " shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +000062 if diclist[i]['kind'] == 'v'
63 let line = diclist[i]['cmd']
64 if line[0] == '/' && line[1] == '^'
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000065 let col = match(line, items[0])
Bram Moolenaarf75a9632005-09-13 21:20:47 +000066 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:]))
Bram Moolenaardd2436f2005-09-05 22:14:46 +000067 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +000068 endif
69 endfor
70 endif
71
Bram Moolenaara4a08382005-09-09 19:52:02 +000072 if len(res) == 0 && searchdecl(items[0], 1) == 0
73 " Found, now figure out the type.
74 " TODO: join previous line if it makes sense
75 let line = getline('.')
76 let col = col('.')
Bram Moolenaarf75a9632005-09-13 21:20:47 +000077 let res = s:Nextitem(strpart(line, 0, col), items[1:])
78 endif
79
80 " If the one and only match was what's already there and it is a composite
81 " type, add a "." or "->".
82 if len(res) == 1 && res[0]['match'] == items[-1] && len(s:SearchMembers(res, [''])) > 0
83 " If there is a '*' before the name use "->".
84 if match(res[0]['tagline'], '\*\s*' . res[0]['match']) > 0
85 let res[0]['match'] .= '->'
86 else
87 let res[0]['match'] .= '.'
88 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +000089 endif
90
91 " The basetext is up to the last "." or "->" and won't be changed. The
92 " matching members are concatenated to this.
93 let basetext = matchstr(a:base, '.*\(\.\|->\)')
Bram Moolenaarf75a9632005-09-13 21:20:47 +000094 return map(res, 'basetext . v:val["match"]')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000095endfunc
Bram Moolenaardd2436f2005-09-05 22:14:46 +000096
Bram Moolenaara4a08382005-09-09 19:52:02 +000097" Find composing type in "lead" and match items[0] with it.
98" Repeat this recursively for items[1], if it's there.
99" Return the list of matches.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000100function! s:Nextitem(lead, items)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000101
102 " Use the text up to the variable name and split it in tokens.
103 let tokens = split(a:lead, '\s\+\|\<')
104
105 " Try to recognize the type of the variable. This is rough guessing...
Bram Moolenaara4a08382005-09-09 19:52:02 +0000106 let res = []
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000107 for tidx in range(len(tokens))
108
Bram Moolenaara4a08382005-09-09 19:52:02 +0000109 " Recognize "struct foobar" and "union foobar".
110 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000111 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000112 break
113 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000114
Bram Moolenaara4a08382005-09-09 19:52:02 +0000115 " TODO: add more reserved words
116 if index(['int', 'float', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
117 continue
118 endif
119
120 " Use the tags file to find out if this is a typedef.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000121 let diclist = taglist('^' . tokens[tidx] . '$')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000122 for tagidx in range(len(diclist))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000123 " New ctags has the "typename" field.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000124 if has_key(diclist[tagidx], 'typename')
125 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000126 continue
127 endif
128
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000129 " Only handle typedefs here.
130 if diclist[tagidx]['kind'] != 't'
131 continue
132 endif
133
134 " For old ctags we recognize "typedef struct aaa" and
135 " "typedef union bbb" in the tags file command.
136 let cmd = diclist[tagidx]['cmd']
137 let ei = matchend(cmd, 'typedef\s\+')
138 if ei > 1
139 let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
140 if len(cmdtokens) > 1
141 if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
142 let name = ''
143 " Use the first identifier after the "struct" or "union"
144 for ti in range(len(cmdtokens) - 1)
145 if cmdtokens[ti] =~ '^\w'
146 let name = cmdtokens[ti]
147 break
148 endif
149 endfor
150 if name != ''
151 call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items))
152 endif
153 else
154 " Could be "typedef other_T some_T".
155 call extend(res, s:Nextitem(cmdtokens[0], a:items))
156 endif
157 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000158 endif
159 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000160 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000161 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000162 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000163 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000164
Bram Moolenaara4a08382005-09-09 19:52:02 +0000165 return res
166endfunction
167
168
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000169" Return a list with resulting matches.
170" Each match is a dictionary with "match" and "tagline" entries.
171function! s:StructMembers(typename, items)
Bram Moolenaara4a08382005-09-09 19:52:02 +0000172 " Todo: What about local structures?
173 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
174 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000175 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000176 endif
177
178 let typename = a:typename
179 let qflist = []
180 while 1
Bram Moolenaardcca87b2005-09-10 19:24:59 +0000181 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
Bram Moolenaara4a08382005-09-09 19:52:02 +0000182 let qflist = getqflist()
183 if len(qflist) > 0 || match(typename, "::") < 0
184 break
185 endif
186 " No match for "struct:context::name", remove "context::" and try again.
187 let typename = substitute(typename, ':[^:]*::', ':', '')
188 endwhile
189
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000190 let matches = []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000191 for l in qflist
192 let memb = matchstr(l['text'], '[^\t]*')
193 if memb =~ '^' . a:items[0]
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000194 call add(matches, {'match': memb, 'tagline': l['text']})
Bram Moolenaara4a08382005-09-09 19:52:02 +0000195 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000196 endfor
197
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000198 if len(matches) > 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000199 " No further items, return the result.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000200 if len(a:items) == 1
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000201 return matches
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000202 endif
203
204 " More items following. For each of the possible members find the
205 " matching following members.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000206 return s:SearchMembers(matches, a:items[1:])
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000207 endif
208
209 " Failed to find anything.
210 return []
Bram Moolenaare344bea2005-09-01 20:46:49 +0000211endfunction
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000212
213" For matching members, find matches for following items.
214function! s:SearchMembers(matches, items)
215 let res = []
216 for i in range(len(a:matches))
217 let line = a:matches[i]['tagline']
218 let e = matchend(line, '\ttypename:')
219 if e > 0
220 " Use typename field
221 let name = matchstr(line, '[^\t]*', e)
222 call extend(res, s:StructMembers(name, a:items))
223 else
224 " Use the search command (the declaration itself).
225 let s = match(line, '\t\zs/^')
226 if s > 0
227 let e = match(line, a:matches[i]['match'], s)
228 if e > 0
229 call extend(res, s:Nextitem(strpart(line, s, e - s), a:items))
230 endif
231 endif
232 endif
233 endfor
234 return res
235endfunc