0
0
mirror of https://github.com/vim/vim.git synced 2025-07-04 23:07:33 -04:00
vim/runtime/autoload/ccomplete.vim

293 lines
8.3 KiB
VimL
Raw Normal View History

2005-09-01 20:46:49 +00:00
" Vim completion script
" Language: C
" Maintainer: Bram Moolenaar <Bram@vim.org>
2006-02-01 21:56:25 +00:00
" Last Change: 2006 Jan 30
2005-09-01 20:46:49 +00:00
2005-09-09 19:52:02 +00:00
2005-09-13 21:20:47 +00:00
" This function is used for the 'omnifunc' option.
2005-09-01 20:46:49 +00:00
function! ccomplete#Complete(findstart, base)
if a:findstart
2005-09-07 21:21:14 +00:00
" Locate the start of the item, including "." and "->".
2005-09-01 20:46:49 +00:00
let line = getline('.')
let start = col('.') - 1
2005-10-10 20:59:28 +00:00
let lastword = -1
2005-09-01 20:46:49 +00:00
while start > 0
2005-10-10 20:59:28 +00:00
if line[start - 1] =~ '\w'
let start -= 1
elseif line[start - 1] =~ '\.'
if lastword == -1
let lastword = start
endif
2005-09-01 20:46:49 +00:00
let start -= 1
elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
2005-10-10 20:59:28 +00:00
if lastword == -1
let lastword = start
endif
2005-09-01 20:46:49 +00:00
let start -= 2
else
break
endif
endwhile
2005-10-10 20:59:28 +00:00
" Return the column of the last word, which is going to be changed.
" Remember the text that comes before it in s:prepended.
if lastword == -1
let s:prepended = ''
return start
endif
let s:prepended = strpart(line, start, lastword - start)
return lastword
2005-09-01 20:46:49 +00:00
endif
2005-09-07 21:21:14 +00:00
" Return list of matches.
2005-10-10 20:59:28 +00:00
let base = s:prepended . a:base
2005-09-07 21:21:14 +00:00
" Split item in words, keep empty word after "." or "->".
" "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
2005-10-10 20:59:28 +00:00
let items = split(base, '\.\|->', 1)
2005-09-07 21:21:14 +00:00
if len(items) <= 1
2005-10-10 20:59:28 +00:00
" Don't do anything for an empty base, would result in all the tags in the
" tags file.
if base == ''
return []
endif
2005-09-01 20:46:49 +00:00
" Only one part, no "." or "->": complete from tags file.
2005-09-07 21:21:14 +00:00
" When local completion is wanted CTRL-N would have been used.
2006-01-30 00:14:18 +00:00
return map(taglist('^' . base), 's:Tag2item(v:val)')
2005-09-01 20:46:49 +00:00
endif
2005-09-05 22:14:46 +00:00
2005-09-09 19:52:02 +00:00
" Find the variable items[0].
" 1. in current function (like with "gd")
" 2. in tags file(s) (like with ":tag")
" 3. in current file (like with "gD")
let res = []
2005-09-13 21:20:47 +00:00
if searchdecl(items[0], 0, 1) == 0
2005-09-05 22:14:46 +00:00
" Found, now figure out the type.
" TODO: join previous line if it makes sense
let line = getline('.')
let col = col('.')
2005-09-13 21:20:47 +00:00
let res = s:Nextitem(strpart(line, 0, col), items[1:])
2005-09-09 19:52:02 +00:00
endif
if len(res) == 0
" Find the variable in the tags file(s)
2005-09-07 21:21:14 +00:00
let diclist = taglist('^' . items[0] . '$')
let res = []
2005-09-05 22:14:46 +00:00
for i in range(len(diclist))
2005-09-09 19:52:02 +00:00
" New ctags has the "typename" field.
if has_key(diclist[i], 'typename')
2005-09-13 21:20:47 +00:00
call extend(res, s:StructMembers(diclist[i]['typename'], items[1:]))
2005-09-09 19:52:02 +00:00
endif
" For a variable use the command, which must be a search pattern that
" shows the declaration of the variable.
2005-09-05 22:14:46 +00:00
if diclist[i]['kind'] == 'v'
let line = diclist[i]['cmd']
if line[0] == '/' && line[1] == '^'
2005-12-18 22:10:00 +00:00
let col = match(line, '\<' . items[0] . '\>')
2005-09-13 21:20:47 +00:00
call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:]))
2005-09-05 22:14:46 +00:00
endif
endif
endfor
endif
2005-09-09 19:52:02 +00:00
if len(res) == 0 && searchdecl(items[0], 1) == 0
" Found, now figure out the type.
" TODO: join previous line if it makes sense
let line = getline('.')
let col = col('.')
2005-09-13 21:20:47 +00:00
let res = s:Nextitem(strpart(line, 0, col), items[1:])
endif
" If the one and only match was what's already there and it is a composite
" type, add a "." or "->".
if len(res) == 1 && res[0]['match'] == items[-1] && len(s:SearchMembers(res, [''])) > 0
" If there is a '*' before the name use "->".
2006-01-30 00:14:18 +00:00
if match(res[0]['tagline'], '\*\s*' . res[0]['match'] . '\>') > 0
2005-09-13 21:20:47 +00:00
let res[0]['match'] .= '->'
else
let res[0]['match'] .= '.'
endif
2005-09-09 19:52:02 +00:00
endif
2005-10-10 20:59:28 +00:00
return map(res, 'v:val["match"]')
2005-09-07 21:21:14 +00:00
endfunc
2006-01-30 00:14:18 +00:00
"
" Turn the tag info "val" into an item for completion.
" "val" is is an item in the list returned by taglist().
function! s:Tag2item(val)
if has_key(a:val, "kind") && a:val["kind"] == 'v'
if len(s:SearchMembers([{'match': a:val["name"], 'dict': a:val}], [''])) > 0
" If there is a '*' before the name use "->". This assumes the command
" is a search pattern!
if match(a:val['cmd'], '\*\s*' . a:val['name'] . '\>') > 0
return a:val["name"] . '->'
else
return a:val["name"] . '.'
endif
endif
endif
return a:val["name"]
endfunction
2005-09-09 19:52:02 +00:00
" Find composing type in "lead" and match items[0] with it.
" Repeat this recursively for items[1], if it's there.
" Return the list of matches.
2005-09-13 21:20:47 +00:00
function! s:Nextitem(lead, items)
2005-09-05 22:14:46 +00:00
2005-09-07 21:21:14 +00:00
" Use the text up to the variable name and split it in tokens.
let tokens = split(a:lead, '\s\+\|\<')
" Try to recognize the type of the variable. This is rough guessing...
2005-09-09 19:52:02 +00:00
let res = []
2005-09-07 21:21:14 +00:00
for tidx in range(len(tokens))
2005-09-09 19:52:02 +00:00
" Recognize "struct foobar" and "union foobar".
if (tokens[tidx] == 'struct' || tokens[tidx] == 'union') && tidx + 1 < len(tokens)
2005-09-13 21:20:47 +00:00
let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items)
2005-09-05 22:14:46 +00:00
break
endif
2005-09-07 21:21:14 +00:00
2005-09-09 19:52:02 +00:00
" TODO: add more reserved words
if index(['int', 'float', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0
continue
endif
" Use the tags file to find out if this is a typedef.
2005-09-07 21:21:14 +00:00
let diclist = taglist('^' . tokens[tidx] . '$')
2005-09-13 21:20:47 +00:00
for tagidx in range(len(diclist))
2005-09-09 19:52:02 +00:00
" New ctags has the "typename" field.
2005-09-13 21:20:47 +00:00
if has_key(diclist[tagidx], 'typename')
call extend(res, s:StructMembers(diclist[tagidx]['typename'], a:items))
continue
endif
" Only handle typedefs here.
if diclist[tagidx]['kind'] != 't'
2005-09-09 19:52:02 +00:00
continue
endif
2005-09-13 21:20:47 +00:00
" For old ctags we recognize "typedef struct aaa" and
" "typedef union bbb" in the tags file command.
let cmd = diclist[tagidx]['cmd']
let ei = matchend(cmd, 'typedef\s\+')
if ei > 1
let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<')
if len(cmdtokens) > 1
if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union'
let name = ''
" Use the first identifier after the "struct" or "union"
for ti in range(len(cmdtokens) - 1)
if cmdtokens[ti] =~ '^\w'
let name = cmdtokens[ti]
break
endif
endfor
if name != ''
call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items))
endif
else
" Could be "typedef other_T some_T".
call extend(res, s:Nextitem(cmdtokens[0], a:items))
endif
endif
2005-09-07 21:21:14 +00:00
endif
endfor
2005-09-09 19:52:02 +00:00
if len(res) > 0
2005-09-07 21:21:14 +00:00
break
2005-09-05 22:14:46 +00:00
endif
2005-09-09 19:52:02 +00:00
endfor
return res
endfunction
2005-09-07 21:21:14 +00:00
2005-09-09 19:52:02 +00:00
2005-09-13 21:20:47 +00:00
" Return a list with resulting matches.
" Each match is a dictionary with "match" and "tagline" entries.
function! s:StructMembers(typename, items)
2005-09-09 19:52:02 +00:00
" Todo: What about local structures?
let fnames = join(map(tagfiles(), 'escape(v:val, " \\")'))
if fnames == ''
2005-09-13 21:20:47 +00:00
return []
2005-09-09 19:52:02 +00:00
endif
let typename = a:typename
let qflist = []
while 1
2005-09-10 19:24:59 +00:00
exe 'silent! vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
2005-09-09 19:52:02 +00:00
let qflist = getqflist()
if len(qflist) > 0 || match(typename, "::") < 0
break
endif
" No match for "struct:context::name", remove "context::" and try again.
let typename = substitute(typename, ':[^:]*::', ':', '')
endwhile
2005-09-13 21:20:47 +00:00
let matches = []
2005-09-09 19:52:02 +00:00
for l in qflist
let memb = matchstr(l['text'], '[^\t]*')
if memb =~ '^' . a:items[0]
2005-09-13 21:20:47 +00:00
call add(matches, {'match': memb, 'tagline': l['text']})
2005-09-09 19:52:02 +00:00
endif
2005-09-05 22:14:46 +00:00
endfor
2005-09-13 21:20:47 +00:00
if len(matches) > 0
2005-09-09 19:52:02 +00:00
" No further items, return the result.
2005-09-07 21:21:14 +00:00
if len(a:items) == 1
2005-09-13 21:20:47 +00:00
return matches
2005-09-07 21:21:14 +00:00
endif
" More items following. For each of the possible members find the
" matching following members.
2005-09-13 21:20:47 +00:00
return s:SearchMembers(matches, a:items[1:])
2005-09-07 21:21:14 +00:00
endif
" Failed to find anything.
return []
2005-09-01 20:46:49 +00:00
endfunction
2005-09-13 21:20:47 +00:00
" For matching members, find matches for following items.
function! s:SearchMembers(matches, items)
let res = []
for i in range(len(a:matches))
2006-01-30 00:14:18 +00:00
let typename = ''
if has_key(a:matches[i], 'dict')
"if a:matches[i].dict['name'] == "gui"
"echomsg string(a:matches[i].dict)
"endif
if has_key(a:matches[i].dict, 'typename')
let typename = a:matches[i].dict['typename']
endif
let line = "\t" . a:matches[i].dict['cmd']
else
let line = a:matches[i]['tagline']
let e = matchend(line, '\ttypename:')
if e > 0
" Use typename field
let typename = matchstr(line, '[^\t]*', e)
endif
endif
if typename != ''
2005-09-13 21:20:47 +00:00
call extend(res, s:StructMembers(name, a:items))
else
" Use the search command (the declaration itself).
let s = match(line, '\t\zs/^')
if s > 0
2006-01-30 00:14:18 +00:00
let e = match(line, '\<' . a:matches[i]['match'] . '\>', s)
2005-09-13 21:20:47 +00:00
if e > 0
2006-01-30 00:14:18 +00:00
"if a:matches[i].dict['name'] == "gui"
"echomsg strpart(line, s, e - s)
"endif
2005-09-13 21:20:47 +00:00
call extend(res, s:Nextitem(strpart(line, s, e - s), a:items))
endif
endif
endif
endfor
return res
endfunc