blob: 355f724d0f4249c03633db5906dbcbbcb3701049 [file] [log] [blame]
Bram Moolenaara4d131d2021-12-27 21:33:07 +00001vim9script noclear
Bram Moolenaare344bea2005-09-01 20:46:49 +00002
Bram Moolenaara4d131d2021-12-27 21:33:07 +00003# Vim completion script
Christian Brabandte978b452023-08-13 10:33:05 +02004# Language: C
5# Maintainer: The Vim Project <https://github.com/vim/vim>
Lars T. Kyllingstadd9ec6762024-06-06 18:37:08 +02006# Last Change: 2024 Jun 06
Bram Moolenaara4d131d2021-12-27 21:33:07 +00007# Rewritten in Vim9 script by github user lacygoill
Christian Brabandte978b452023-08-13 10:33:05 +02008# Former Maintainer: Bram Moolenaar <Bram@vim.org>
Bram Moolenaara4a08382005-09-09 19:52:02 +00009
Bram Moolenaara4d131d2021-12-27 21:33:07 +000010var prepended: string
11var grepCache: dict<list<dict<any>>>
12
13# This function is used for the 'omnifunc' option.
Bram Moolenaarc4573eb2022-01-31 15:40:56 +000014export def Complete(findstart: bool, abase: string): any # {{{1
Bram Moolenaara4d131d2021-12-27 21:33:07 +000015 if findstart
16 # Locate the start of the item, including ".", "->" and "[...]".
17 var line: string = getline('.')
18 var start: number = charcol('.') - 1
19 var lastword: number = -1
Bram Moolenaare344bea2005-09-01 20:46:49 +000020 while start > 0
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000021 if line[start - 1] =~ '\w'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000022 --start
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000023 elseif line[start - 1] =~ '\.'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000024 if lastword == -1
25 lastword = start
26 endif
27 --start
28 elseif start > 1 && line[start - 2] == '-'
29 && line[start - 1] == '>'
30 if lastword == -1
31 lastword = start
32 endif
33 start -= 2
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000034 elseif line[start - 1] == ']'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000035 # Skip over [...].
36 var n: number = 0
37 --start
38 while start > 0
39 --start
40 if line[start] == '['
41 if n == 0
42 break
43 endif
44 --n
45 elseif line[start] == ']' # nested []
46 ++n
47 endif
48 endwhile
Bram Moolenaare344bea2005-09-01 20:46:49 +000049 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +000050 break
Bram Moolenaare344bea2005-09-01 20:46:49 +000051 endif
52 endwhile
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000053
Bram Moolenaara4d131d2021-12-27 21:33:07 +000054 # Return the column of the last word, which is going to be changed.
55 # Remember the text that comes before it in prepended.
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000056 if lastword == -1
Bram Moolenaara4d131d2021-12-27 21:33:07 +000057 prepended = ''
58 return byteidx(line, start)
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000059 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +000060 prepended = line[start : lastword - 1]
61 return byteidx(line, lastword)
Bram Moolenaare344bea2005-09-01 20:46:49 +000062 endif
63
Bram Moolenaara4d131d2021-12-27 21:33:07 +000064 # Return list of matches.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +000065
Bram Moolenaara4d131d2021-12-27 21:33:07 +000066 var base: string = prepended .. abase
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000067
Bram Moolenaara4d131d2021-12-27 21:33:07 +000068 # Don't do anything for an empty base, would result in all the tags in the
69 # tags file.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000070 if base == ''
71 return []
72 endif
73
Bram Moolenaara4d131d2021-12-27 21:33:07 +000074 # init cache for vimgrep to empty
75 grepCache = {}
Bram Moolenaar1056d982006-03-09 22:37:52 +000076
Bram Moolenaara4d131d2021-12-27 21:33:07 +000077 # Split item in words, keep empty word after "." or "->".
78 # "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc.
79 # We can't use split, because we need to skip nested [...].
80 # "aa[...]" -> ['aa', '[...]'], "aa.bb[...]" -> ['aa', 'bb', '[...]'], etc.
81 var items: list<string>
82 var s: number = 0
83 var arrays: number = 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000084 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +000085 var e: number = base->charidx(match(base, '\.\|->\|\[', s))
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000086 if e < 0
87 if s == 0 || base[s - 1] != ']'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000088 items->add(base[s :])
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000089 endif
90 break
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000091 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000092 if s == 0 || base[s - 1] != ']'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000093 items->add(base[s : e - 1])
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000094 endif
95 if base[e] == '.'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000096 # skip over '.'
97 s = e + 1
Bram Moolenaar0e5bd962006-02-04 00:59:56 +000098 elseif base[e] == '-'
Bram Moolenaara4d131d2021-12-27 21:33:07 +000099 # skip over '->'
100 s = e + 2
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000101 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000102 # Skip over [...].
103 var n: number = 0
104 s = e
105 ++e
106 while e < strcharlen(base)
107 if base[e] == ']'
108 if n == 0
109 break
110 endif
111 --n
112 elseif base[e] == '[' # nested [...]
113 ++n
114 endif
115 ++e
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000116 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000117 ++e
118 items->add(base[s : e - 1])
119 ++arrays
120 s = e
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000121 endif
122 endwhile
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000123
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000124 # Find the variable items[0].
125 # 1. in current function (like with "gd")
126 # 2. in tags file(s) (like with ":tag")
127 # 3. in current file (like with "gD")
128 var res: list<dict<any>>
129 if items[0]->searchdecl(false, true) == 0
130 # Found, now figure out the type.
131 # TODO: join previous line if it makes sense
132 var line: string = getline('.')
133 var col: number = charcol('.')
134 if line[: col - 1]->stridx(';') >= 0
135 # Handle multiple declarations on the same line.
136 var col2: number = col - 1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000137 while line[col2] != ';'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000138 --col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000139 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000140 line = line[col2 + 1 :]
141 col -= col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000142 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000143 if line[: col - 1]->stridx(',') >= 0
144 # Handle multiple declarations on the same line in a function
145 # declaration.
146 var col2: number = col - 1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000147 while line[col2] != ','
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000148 --col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000149 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000150 if line[col2 + 1 : col - 1] =~ ' *[^ ][^ ]* *[^ ]'
151 line = line[col2 + 1 :]
152 col -= col2
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000153 endif
154 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000155 if len(items) == 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000156 # Completing one word and it's a local variable: May add '[', '.' or
157 # '->'.
158 var match: string = items[0]
159 var kind: string = 'v'
160 if match(line, '\<' .. match .. '\s*\[') > 0
161 match ..= '['
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000162 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000163 res = line[: col - 1]->Nextitem([''], 0, true)
164 if len(res) > 0
165 # There are members, thus add "." or "->".
166 if match(line, '\*[ \t(]*' .. match .. '\>') > 0
167 match ..= '->'
168 else
169 match ..= '.'
170 endif
171 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000172 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000173 res = [{match: match, tagline: '', kind: kind, info: line}]
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200174 elseif len(items) == arrays + 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000175 # Completing one word and it's a local array variable: build tagline
176 # from declaration line
177 var match: string = items[0]
178 var kind: string = 'v'
179 var tagline: string = "\t/^" .. line .. '$/'
180 res = [{match: match, tagline: tagline, kind: kind, info: line}]
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000181 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000182 # Completing "var.", "var.something", etc.
183 res = line[: col - 1]->Nextitem(items[1 :], 0, true)
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000184 endif
185 endif
186
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200187 if len(items) == 1 || len(items) == arrays + 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000188 # Only one part, no "." or "->": complete from tags file.
189 var tags: list<dict<any>>
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200190 if len(items) == 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000191 tags = taglist('^' .. base)
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200192 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000193 tags = taglist('^' .. items[0] .. '$')
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200194 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000195
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000196 tags
197 # Remove members, these can't appear without something in front.
198 ->filter((_, v: dict<any>): bool =>
199 v->has_key('kind') ? v.kind != 'm' : true)
200 # Remove static matches in other files.
201 ->filter((_, v: dict<any>): bool =>
202 !v->has_key('static')
203 || !v['static']
204 || bufnr('%') == bufnr(v['filename']))
Bram Moolenaareb94e552006-03-11 21:35:11 +0000205
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000206 res = res->extend(tags->map((_, v: dict<any>) => Tag2item(v)))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000207 endif
208
209 if len(res) == 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000210 # Find the variable in the tags file(s)
211 var diclist: list<dict<any>> = taglist('^' .. items[0] .. '$')
212 # Remove members, these can't appear without something in front.
Lars T. Kyllingstadd9ec6762024-06-06 18:37:08 +0200213 ->filter((_, v: dict<any>): bool =>
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000214 v->has_key('kind') ? v.kind != 'm' : true)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000215
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000216 res = []
217 for i: number in len(diclist)->range()
218 # New ctags has the "typeref" field. Patched version has "typename".
219 if diclist[i]->has_key('typename')
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000220 res = res->extend(diclist[i]['typename']->StructMembers(items[1 :], true))
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000221 elseif diclist[i]->has_key('typeref')
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000222 res = res->extend(diclist[i]['typeref']->StructMembers(items[1 :], true))
Bram Moolenaara4a08382005-09-09 19:52:02 +0000223 endif
224
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000225 # For a variable use the command, which must be a search pattern that
226 # shows the declaration of the variable.
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000227 if diclist[i]['kind'] == 'v'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000228 var line: string = diclist[i]['cmd']
229 if line[: 1] == '/^'
230 var col: number = line->charidx(match(line, '\<' .. items[0] .. '\>'))
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000231 res = res->extend(line[2 : col - 1]->Nextitem(items[1 :], 0, true))
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000232 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000233 endif
234 endfor
235 endif
236
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000237 if len(res) == 0 && items[0]->searchdecl(true) == 0
238 # Found, now figure out the type.
239 # TODO: join previous line if it makes sense
240 var line: string = getline('.')
241 var col: number = charcol('.')
242 res = line[: col - 1]->Nextitem(items[1 :], 0, true)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000243 endif
244
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000245 # If the last item(s) are [...] they need to be added to the matches.
246 var last: number = len(items) - 1
247 var brackets: string = ''
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000248 while last >= 0
249 if items[last][0] != '['
250 break
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000251 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000252 brackets = items[last] .. brackets
253 --last
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000254 endwhile
Bram Moolenaara4a08382005-09-09 19:52:02 +0000255
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000256 return res->map((_, v: dict<any>): dict<string> => Tagline2item(v, brackets))
257enddef
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000258
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000259def GetAddition( # {{{1
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000260 line: string,
261 match: string,
262 memarg: list<dict<any>>,
263 bracket: bool): string
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000264 # Guess if the item is an array.
265 if bracket && match(line, match .. '\s*\[') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000266 return '['
267 endif
268
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000269 # Check if the item has members.
270 if SearchMembers(memarg, [''], false)->len() > 0
271 # If there is a '*' before the name use "->".
272 if match(line, '\*[ \t(]*' .. match .. '\>') > 0
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000273 return '->'
274 else
275 return '.'
Bram Moolenaar280f1262006-01-30 00:14:18 +0000276 endif
277 endif
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000278 return ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000279enddef
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000280
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000281def Tag2item(val: dict<any>): dict<any> # {{{1
282# Turn the tag info "val" into an item for completion.
283# "val" is is an item in the list returned by taglist().
284# If it is a variable we may add "." or "->". Don't do it for other types,
285# such as a typedef, by not including the info that GetAddition() uses.
286 var res: dict<any> = {match: val['name']}
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000287
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000288 res['extra'] = Tagcmd2extra(val['cmd'], val['name'], val['filename'])
Bram Moolenaareb94e552006-03-11 21:35:11 +0000289
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000290 var s: string = Dict2info(val)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000291 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000292 res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000293 endif
294
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000295 res['tagline'] = ''
296 if val->has_key('kind')
297 var kind: string = val['kind']
298 res['kind'] = kind
Bram Moolenaareb94e552006-03-11 21:35:11 +0000299 if kind == 'v'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000300 res['tagline'] = "\t" .. val['cmd']
301 res['dict'] = val
Bram Moolenaareb94e552006-03-11 21:35:11 +0000302 elseif kind == 'f'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000303 res['match'] = val['name'] .. '('
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000304 endif
305 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000306
307 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000308enddef
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000309
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000310def Dict2info(dict: dict<any>): string # {{{1
311# Use all the items in dictionary for the "info" entry.
312 var info: string = ''
313 for k: string in dict->keys()->sort()
314 info ..= k .. repeat(' ', 10 - strlen(k))
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000315 if k == 'cmd'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000316 info ..= dict['cmd']
317 ->matchstr('/^\s*\zs.*\ze$/')
318 ->substitute('\\\(.\)', '\1', 'g')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000319 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000320 var dictk: any = dict[k]
321 if typename(dictk) != 'string'
322 info ..= dictk->string()
323 else
324 info ..= dictk
325 endif
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000326 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000327 info ..= "\n"
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000328 endfor
329 return info
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000330enddef
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000331
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000332def ParseTagline(line: string): dict<any> # {{{1
333# Parse a tag line and return a dictionary with items like taglist()
334 var l: list<string> = split(line, "\t")
335 var d: dict<any>
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000336 if len(l) >= 3
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000337 d['name'] = l[0]
338 d['filename'] = l[1]
339 d['cmd'] = l[2]
340 var n: number = 2
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000341 if l[2] =~ '^/'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000342 # Find end of cmd, it may contain Tabs.
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000343 while n < len(l) && l[n] !~ '/;"$'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000344 ++n
345 d['cmd'] ..= ' ' .. l[n]
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000346 endwhile
347 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000348 for i: number in range(n + 1, len(l) - 1)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000349 if l[i] == 'file:'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000350 d['static'] = 1
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000351 elseif l[i] !~ ':'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000352 d['kind'] = l[i]
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000353 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000354 d[l[i]->matchstr('[^:]*')] = l[i]->matchstr(':\zs.*')
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000355 endif
356 endfor
357 endif
358
359 return d
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000360enddef
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000361
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000362def Tagline2item(val: dict<any>, brackets: string): dict<string> # {{{1
363# Turn a match item "val" into an item for completion.
364# "val['match']" is the matching item.
365# "val['tagline']" is the tagline in which the last part was found.
366 var line: string = val['tagline']
367 var add: string = GetAddition(line, val['match'], [val], brackets == '')
368 var res: dict<string> = {word: val['match'] .. brackets .. add}
Bram Moolenaareb94e552006-03-11 21:35:11 +0000369
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000370 if val->has_key('info')
371 # Use info from Tag2item().
372 res['info'] = val['info']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000373 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000374 # Parse the tag line and add each part to the "info" entry.
375 var s: string = ParseTagline(line)->Dict2info()
Bram Moolenaareb94e552006-03-11 21:35:11 +0000376 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000377 res['info'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000378 endif
379 endif
380
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000381 if val->has_key('kind')
382 res['kind'] = val['kind']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000383 elseif add == '('
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000384 res['kind'] = 'f'
Bram Moolenaareb94e552006-03-11 21:35:11 +0000385 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000386 var s: string = line->matchstr('\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
Bram Moolenaareb94e552006-03-11 21:35:11 +0000387 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000388 res['kind'] = s
Bram Moolenaareb94e552006-03-11 21:35:11 +0000389 endif
390 endif
391
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000392 if val->has_key('extra')
393 res['menu'] = val['extra']
Bram Moolenaareb94e552006-03-11 21:35:11 +0000394 return res
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000395 endif
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000396
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000397 # Isolate the command after the tag and filename.
398 var s: string = line->matchstr('[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)')
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000399 if s != ''
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000400 res['menu'] = s->Tagcmd2extra(val['match'], line->matchstr('[^\t]*\t\zs[^\t]*\ze\t'))
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000401 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000402 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000403enddef
Bram Moolenaar280f1262006-01-30 00:14:18 +0000404
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000405def Tagcmd2extra( # {{{1
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000406 cmd: string,
407 name: string,
408 fname: string): string
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000409# Turn a command from a tag line to something that is useful in the menu
410 var x: string
411 if cmd =~ '^/^'
412 # The command is a search command, useful to see what it is.
413 x = cmd
414 ->matchstr('^/^\s*\zs.*\ze$/')
415 ->substitute('\<' .. name .. '\>', '@@', '')
416 ->substitute('\\\(.\)', '\1', 'g')
417 .. ' - ' .. fname
418 elseif cmd =~ '^\d*$'
419 # The command is a line number, the file name is more useful.
420 x = fname .. ' - ' .. cmd
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000421 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000422 # Not recognized, use command and file name.
423 x = cmd .. ' - ' .. fname
Bram Moolenaarf52c7252006-02-10 23:23:57 +0000424 endif
425 return x
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000426enddef
Bram Moolenaar280f1262006-01-30 00:14:18 +0000427
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000428def Nextitem( # {{{1
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000429 lead: string,
430 items: list<string>,
431 depth: number,
432 all: bool): list<dict<string>>
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000433# Find composing type in "lead" and match items[0] with it.
434# Repeat this recursively for items[1], if it's there.
435# When resolving typedefs "depth" is used to avoid infinite recursion.
436# Return the list of matches.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000437
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000438 # Use the text up to the variable name and split it in tokens.
439 var tokens: list<string> = split(lead, '\s\+\|\<')
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000440
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000441 # Try to recognize the type of the variable. This is rough guessing...
442 var res: list<dict<string>>
443 for tidx: number in len(tokens)->range()
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000444
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000445 # Skip tokens starting with a non-ID character.
Bram Moolenaar1056d982006-03-09 22:37:52 +0000446 if tokens[tidx] !~ '^\h'
447 continue
448 endif
449
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000450 # Recognize "struct foobar" and "union foobar".
451 # Also do "class foobar" when it's C++ after all (doesn't work very well
452 # though).
453 if (tokens[tidx] == 'struct'
454 || tokens[tidx] == 'union'
455 || tokens[tidx] == 'class')
456 && tidx + 1 < len(tokens)
457 res = StructMembers(tokens[tidx] .. ':' .. tokens[tidx + 1], items, all)
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000458 break
459 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000460
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000461 # TODO: add more reserved words
462 if ['int', 'short', 'char', 'float',
463 'double', 'static', 'unsigned', 'extern']->index(tokens[tidx]) >= 0
Bram Moolenaara4a08382005-09-09 19:52:02 +0000464 continue
465 endif
466
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000467 # Use the tags file to find out if this is a typedef.
468 var diclist: list<dict<any>> = taglist('^' .. tokens[tidx] .. '$')
469 for tagidx: number in len(diclist)->range()
470 var item: dict<any> = diclist[tagidx]
Bram Moolenaareb94e552006-03-11 21:35:11 +0000471
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000472 # New ctags has the "typeref" field. Patched version has "typename".
473 if item->has_key('typeref')
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000474 res = res->extend(item['typeref']->StructMembers(items, all))
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000475 continue
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000476 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000477 if item->has_key('typename')
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000478 res = res->extend(item['typename']->StructMembers(items, all))
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000479 continue
Bram Moolenaara4a08382005-09-09 19:52:02 +0000480 endif
481
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000482 # Only handle typedefs here.
Bram Moolenaareb94e552006-03-11 21:35:11 +0000483 if item['kind'] != 't'
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000484 continue
Bram Moolenaareb94e552006-03-11 21:35:11 +0000485 endif
486
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000487 # Skip matches local to another file.
488 if item->has_key('static') && item['static']
489 && bufnr('%') != bufnr(item['filename'])
490 continue
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000491 endif
492
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000493 # For old ctags we recognize "typedef struct aaa" and
494 # "typedef union bbb" in the tags file command.
495 var cmd: string = item['cmd']
496 var ei: number = cmd->charidx(matchend(cmd, 'typedef\s\+'))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000497 if ei > 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000498 var cmdtokens: list<string> = cmd[ei :]->split('\s\+\|\<')
499 if len(cmdtokens) > 1
500 if cmdtokens[0] == 'struct'
501 || cmdtokens[0] == 'union'
502 || cmdtokens[0] == 'class'
503 var name: string = ''
504 # Use the first identifier after the "struct" or "union"
505 for ti: number in (len(cmdtokens) - 1)->range()
506 if cmdtokens[ti] =~ '^\w'
507 name = cmdtokens[ti]
508 break
509 endif
510 endfor
511 if name != ''
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000512 res = res->extend(StructMembers(cmdtokens[0] .. ':' .. name, items, all))
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000513 endif
514 elseif depth < 10
515 # Could be "typedef other_T some_T".
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000516 res = res->extend(cmdtokens[0]->Nextitem(items, depth + 1, all))
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000517 endif
518 endif
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000519 endif
520 endfor
Bram Moolenaara4a08382005-09-09 19:52:02 +0000521 if len(res) > 0
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000522 break
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000523 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000524 endfor
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000525
Bram Moolenaara4a08382005-09-09 19:52:02 +0000526 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000527enddef
Bram Moolenaara4a08382005-09-09 19:52:02 +0000528
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000529def StructMembers( # {{{1
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000530 atypename: string,
531 items: list<string>,
532 all: bool): list<dict<string>>
Bram Moolenaara4a08382005-09-09 19:52:02 +0000533
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000534# Search for members of structure "typename" in tags files.
535# Return a list with resulting matches.
536# Each match is a dictionary with "match" and "tagline" entries.
537# When "all" is true find all, otherwise just return 1 if there is any member.
538
539 # Todo: What about local structures?
540 var fnames: string = tagfiles()
541 ->map((_, v: string) => escape(v, ' \#%'))
542 ->join()
Bram Moolenaara4a08382005-09-09 19:52:02 +0000543 if fnames == ''
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000544 return []
Bram Moolenaara4a08382005-09-09 19:52:02 +0000545 endif
546
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000547 var typename: string = atypename
548 var qflist: list<dict<any>>
549 var cached: number = 0
550 var n: string
551 if !all
552 n = '1' # stop at first found match
553 if grepCache->has_key(typename)
554 qflist = grepCache[typename]
555 cached = 1
Bram Moolenaar1056d982006-03-09 22:37:52 +0000556 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000557 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000558 n = ''
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000559 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000560 if !cached
561 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000562 execute 'silent! keepjumps noautocmd '
563 .. n .. 'vimgrep ' .. '/\t' .. typename .. '\(\t\|$\)/j '
564 .. fnames
Bram Moolenaar1056d982006-03-09 22:37:52 +0000565
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000566 qflist = getqflist()
567 if len(qflist) > 0 || match(typename, '::') < 0
568 break
Bram Moolenaar1056d982006-03-09 22:37:52 +0000569 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000570 # No match for "struct:context::name", remove "context::" and try again.
571 typename = typename->substitute(':[^:]*::', ':', '')
Bram Moolenaar1056d982006-03-09 22:37:52 +0000572 endwhile
573
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000574 if !all
575 # Store the result to be able to use it again later.
576 grepCache[typename] = qflist
Bram Moolenaara4a08382005-09-09 19:52:02 +0000577 endif
Bram Moolenaar1056d982006-03-09 22:37:52 +0000578 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000579
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000580 # Skip over [...] items
581 var idx: number = 0
582 var target: string
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200583 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000584 if idx >= len(items)
585 target = '' # No further items, matching all members
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200586 break
587 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000588 if items[idx][0] != '['
589 target = items[idx]
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200590 break
591 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000592 ++idx
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200593 endwhile
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000594 # Put matching members in matches[].
595 var matches: list<dict<string>>
596 for l: dict<any> in qflist
597 var memb: string = l['text']->matchstr('[^\t]*')
598 if memb =~ '^' .. target
599 # Skip matches local to another file.
600 if match(l['text'], "\tfile:") < 0
601 || bufnr('%') == l['text']->matchstr('\t\zs[^\t]*')->bufnr()
602 var item: dict<string> = {match: memb, tagline: l['text']}
Bram Moolenaareb94e552006-03-11 21:35:11 +0000603
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000604 # Add the kind of item.
605 var s: string = l['text']->matchstr('\t\(kind:\)\=\zs\S\ze\(\t\|$\)')
606 if s != ''
607 item['kind'] = s
608 if s == 'f'
609 item['match'] = memb .. '('
610 endif
611 endif
Bram Moolenaareb94e552006-03-11 21:35:11 +0000612
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000613 matches->add(item)
Bram Moolenaareb94e552006-03-11 21:35:11 +0000614 endif
Bram Moolenaara4a08382005-09-09 19:52:02 +0000615 endif
Bram Moolenaardd2436f2005-09-05 22:14:46 +0000616 endfor
617
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000618 if len(matches) > 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000619 # Skip over next [...] items
620 ++idx
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000621 while 1
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000622 if idx >= len(items)
623 return matches # No further items, return the result.
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000624 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000625 if items[idx][0] != '['
626 break
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000627 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000628 ++idx
Bram Moolenaar0e5bd962006-02-04 00:59:56 +0000629 endwhile
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000630
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000631 # More items following. For each of the possible members find the
632 # matching following members.
633 return SearchMembers(matches, items[idx :], all)
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000634 endif
635
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000636 # Failed to find anything.
Bram Moolenaarcaa0fcf2005-09-07 21:21:14 +0000637 return []
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000638enddef
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000639
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000640def SearchMembers( # {{{1
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000641 matches: list<dict<any>>,
642 items: list<string>,
643 all: bool): list<dict<string>>
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000644
645# For matching members, find matches for following items.
646# When "all" is true find all, otherwise just return 1 if there is any member.
647 var res: list<dict<string>>
648 for i: number in len(matches)->range()
649 var typename: string = ''
650 var line: string
651 if matches[i]->has_key('dict')
652 if matches[i]['dict']->has_key('typename')
653 typename = matches[i]['dict']['typename']
654 elseif matches[i]['dict']->has_key('typeref')
655 typename = matches[i]['dict']['typeref']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000656 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000657 line = "\t" .. matches[i]['dict']['cmd']
Bram Moolenaar280f1262006-01-30 00:14:18 +0000658 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000659 line = matches[i]['tagline']
660 var eb: number = matchend(line, '\ttypename:')
661 var e: number = charidx(line, eb)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000662 if e < 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000663 eb = matchend(line, '\ttyperef:')
664 e = charidx(line, eb)
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000665 endif
Bram Moolenaar280f1262006-01-30 00:14:18 +0000666 if e > 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000667 # Use typename field
668 typename = line->matchstr('[^\t]*', eb)
Bram Moolenaar280f1262006-01-30 00:14:18 +0000669 endif
670 endif
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000671
Bram Moolenaar280f1262006-01-30 00:14:18 +0000672 if typename != ''
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000673 res = res->extend(StructMembers(typename, items, all))
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000674 else
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000675 # Use the search command (the declaration itself).
676 var sb: number = line->match('\t\zs/^')
677 var s: number = charidx(line, sb)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000678 if s > 0
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000679 var e: number = line
680 ->charidx(match(line, '\<' .. matches[i]['match'] .. '\>', sb))
681 if e > 0
Bram Moolenaarc4573eb2022-01-31 15:40:56 +0000682 res = res->extend(line[s : e - 1]->Nextitem(items, 0, all))
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000683 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000684 endif
685 endif
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000686 if !all && len(res) > 0
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000687 break
688 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000689 endfor
690 return res
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000691enddef
692#}}}1
Bram Moolenaarb6b046b2011-12-30 13:11:27 +0100693
Bram Moolenaara4d131d2021-12-27 21:33:07 +0000694# vim: noet sw=2 sts=2