summaryrefslogtreecommitdiff
path: root/runtime/autoload/ccomplete.vim
blob: 2d54ea6d2d683a26001f2e1d92abf4d9f7e85c2f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
" Vim completion script
" Language:	C
" Maintainer:	Bram Moolenaar <Bram@vim.org>
" Last Change:	2005 Sep 07

function! ccomplete#Complete(findstart, base)
  if a:findstart
    " Locate the start of the item, including "." and "->".
    let line = getline('.')
    let start = col('.') - 1
    while start > 0
      if line[start - 1] =~ '\w\|\.'
	let start -= 1
      elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
	let start -= 2
      else
	break
      endif
    endwhile
    return start
  endif

  " Return list of matches.

  " Split item in words, keep empty word after "." or "->".
  " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
  let items = split(a:base, '\.\|->', 1)
  if len(items) <= 1
    " Only one part, no "." or "->": complete from tags file.
    " When local completion is wanted CTRL-N would have been used.
    return map(taglist('^' . a:base), 'v:val["name"]')
  endif

  let basetext = matchstr(a:base, '.*\(\.\|->\)')

  " Find variable locally in current function, current file or tags file.
  if searchdecl(items[0]) == 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('.')
    let res = ccomplete#Nextitem(strpart(line, 0, col), items[1:], basetext)
  else
    " Find the variable in the tags file
    let diclist = taglist('^' . items[0] . '$')

    let res = []
    for i in range(len(diclist))
      " For now we only recognize a variable.
      " The command in the tags file must be a search pattern that shows the
      " declaration of the variable.
      if diclist[i]['kind'] == 'v'
	let line = diclist[i]['cmd']
	if line[0] == '/' && line[1] == '^'
	  let line = strpart(line, 2)		" Remove /^ from the cmd
	  let col = match(line, items[0])
	  call extend(res, ccomplete#Nextitem(strpart(line, 0, col), items[1:], basetext)
	endif
      endif
    endfor
  endif

  return res
endfunc

function! ccomplete#Nextitem(lead, items, basetext)

  " 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...
  let members = []
  let taglines = []
  for tidx in range(len(tokens))

    " Recognize 'struct foobar'.
    if tokens[tidx] == 'struct' && tidx + 1 < len(tokens)
      let [members, taglines] = ccomplete#StructMembers(tokens[tidx + 1], a:items[0])
      break
    endif

    " Recognize a typedef: 'foobar_t'.
    let diclist = taglist('^' . tokens[tidx] . '$')
    for i in range(len(diclist))
      " For now we only recognize "typedef struct foobar".
      " The command in the tags file must be a search pattern that shows the
      " typedef.
      let cmd = diclist[i]['cmd']
      let ci = matchend(cmd, 'typedef\s\+struct\s\+')
      if ci > 1
	let name = matchstr(cmd, '\w*', ci)
	let [m, l] = ccomplete#StructMembers(name, a:items[0])
	call extend(members, m)
	call extend(taglines, l)
      endif
    endfor
    if len(members) > 0
      break
    endif

  endfor

  if len(members) > 0
    if len(a:items) == 1
      return map(members, 'a:basetext . v:val')
    endif

    " More items following.  For each of the possible members find the
    " matching following members.
    let res = []
    for i in range(len(members))
      let line = taglines[i]
      let memb = members[i]
      let s = match(line, '\t\zs/^')
      if s > 0
	let e = match(line, members[i], s)
	if e > 0
	  call extend(res, ccomplete#Nextitem(strpart(line, s, e - s), a:items[1:], a:basetext))
	endif
      endif
    endfor
    return res
  endif

  " Failed to find anything.
  return []
endfunction


" Return a list with two lists:
" - a list of members of structure "name" starting with string "item".
" - a list of the tag lines where the member is defined.
function! ccomplete#StructMembers(name, item)
  " Todo: Use all tags files; What about local structures?
  exe 'vimgrep /\<struct:' . a:name . '\>/j tags'

  let members = []
  let taglines = []
  for l in getqflist()
    let memb = matchstr(l['text'], '[^\t]*')
    if memb =~ '^' . a:item
      call add(members, memb)
      call add(taglines, l['text'])
    endif
  endfor
  return [members, taglines]
endfunction