1" Vim completion script 2" Language: C 3" Maintainer: Bram Moolenaar <[email protected]> 4" Last Change: 2005 Sep 13 5 6 7" This function is used for the 'omnifunc' option. 8function! ccomplete#Complete(findstart, base) 9 if a:findstart 10 " Locate the start of the item, including "." and "->". 11 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 25 " 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 31 " Only one part, no "." or "->": complete from tags file. 32 " When local completion is wanted CTRL-N would have been used. 33 return map(taglist('^' . a:base), 'v:val["name"]') 34 endif 35 36 " 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 = [] 41 if searchdecl(items[0], 0, 1) == 0 42 " Found, now figure out the type. 43 " TODO: join previous line if it makes sense 44 let line = getline('.') 45 let col = col('.') 46 let res = s:Nextitem(strpart(line, 0, col), items[1:]) 47 endif 48 49 if len(res) == 0 50 " Find the variable in the tags file(s) 51 let diclist = taglist('^' . items[0] . '$') 52 53 let res = [] 54 for i in range(len(diclist)) 55 " New ctags has the "typename" field. 56 if has_key(diclist[i], 'typename') 57 call extend(res, s:StructMembers(diclist[i]['typename'], items[1:])) 58 endif 59 60 " For a variable use the command, which must be a search pattern that 61 " shows the declaration of the variable. 62 if diclist[i]['kind'] == 'v' 63 let line = diclist[i]['cmd'] 64 if line[0] == '/' && line[1] == '^' 65 let col = match(line, items[0]) 66 call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:])) 67 endif 68 endif 69 endfor 70 endif 71 72 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('.') 77 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 89 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, '.*\(\.\|->\)') 94 return map(res, 'basetext . v:val["match"]') 95endfunc 96 97" 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. 100function! s:Nextitem(lead, items) 101 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... 106 let res = [] 107 for tidx in range(len(tokens)) 108 109 " Recognize "struct foobar" and "union foobar". 110 if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens) 111 let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items) 112 break 113 endif 114 115 " 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. 121 let diclist = taglist('^' . tokens[tidx] . '$') 122 for tagidx in range(len(diclist)) 123 " New ctags has the "typename" field. 124 if has_key(diclist[tagidx], 'typename') 125 call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items)) 126 continue 127 endif 128 129 " 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 158 endif 159 endfor 160 if len(res) > 0 161 break 162 endif 163 endfor 164 165 return res 166endfunction 167 168 169" Return a list with resulting matches. 170" Each match is a dictionary with "match" and "tagline" entries. 171function! s:StructMembers(typename, items) 172 " Todo: What about local structures? 173 let fnames = join(map(tagfiles(), 'escape(v:val, " \\")')) 174 if fnames == '' 175 return [] 176 endif 177 178 let typename = a:typename 179 let qflist = [] 180 while 1 181 exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames 182 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 190 let matches = [] 191 for l in qflist 192 let memb = matchstr(l['text'], '[^\t]*') 193 if memb =~ '^' . a:items[0] 194 call add(matches, {'match': memb, 'tagline': l['text']}) 195 endif 196 endfor 197 198 if len(matches) > 0 199 " No further items, return the result. 200 if len(a:items) == 1 201 return matches 202 endif 203 204 " More items following. For each of the possible members find the 205 " matching following members. 206 return s:SearchMembers(matches, a:items[1:]) 207 endif 208 209 " Failed to find anything. 210 return [] 211endfunction 212 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 236