Mercurial > vim
annotate runtime/autoload/ccomplete.vim @ 33762:7b26c36e9b3b v9.0.2102
patch 9.0.2102: matchparen highlight not cleared in completion mode
Commit: https://github.com/vim/vim/commit/9588666360e94de3ff58d4bc79aa9148fbf5fc44
Author: Christian Brabandt <cb@256bit.org>
Date: Sun Nov 12 16:55:01 2023 +0100
patch 9.0.2102: matchparen highlight not cleared in completion mode
Problem: matchparen highlight not cleared in completion mode
Solution: Clear matchparen highlighting in completion mode
Remove hard-coded hack in insexpand.c to clear the :3match before
displaying the completion menu.
Add a test for matchparen highlighting. While at it, move all test tests
related to the matchparen plugin into a separate test file.
closes: #13493
closes: #13524
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sun, 12 Nov 2023 17:00:04 +0100 |
parents | 4027cefc2aab |
children | 9dfa17769328 |
rev | line source |
---|---|
26779 | 1 vim9script noclear |
502 | 2 |
26779 | 3 # Vim completion script |
32770
4027cefc2aab
Farewell to Bram and dedicate upcoming Vim 9.1 to him (#12749)
Christian Brabandt <cb@256bit.org>
parents:
27492
diff
changeset
|
4 # Language: C |
4027cefc2aab
Farewell to Bram and dedicate upcoming Vim 9.1 to him (#12749)
Christian Brabandt <cb@256bit.org>
parents:
27492
diff
changeset
|
5 # Maintainer: The Vim Project <https://github.com/vim/vim> |
4027cefc2aab
Farewell to Bram and dedicate upcoming Vim 9.1 to him (#12749)
Christian Brabandt <cb@256bit.org>
parents:
27492
diff
changeset
|
6 # Last Change: 2023 Aug 10 |
26779 | 7 # Rewritten in Vim9 script by github user lacygoill |
32770
4027cefc2aab
Farewell to Bram and dedicate upcoming Vim 9.1 to him (#12749)
Christian Brabandt <cb@256bit.org>
parents:
27492
diff
changeset
|
8 # Former Maintainer: Bram Moolenaar <Bram@vim.org> |
516 | 9 |
26779 | 10 var prepended: string |
11 var grepCache: dict<list<dict<any>>> | |
12 | |
13 # This function is used for the 'omnifunc' option. | |
27492 | 14 export def Complete(findstart: bool, abase: string): any # {{{1 |
26779 | 15 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 | |
502 | 20 while start > 0 |
548 | 21 if line[start - 1] =~ '\w' |
26779 | 22 --start |
548 | 23 elseif line[start - 1] =~ '\.' |
26779 | 24 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 | |
654 | 34 elseif line[start - 1] == ']' |
26779 | 35 # 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 | |
502 | 49 else |
26779 | 50 break |
502 | 51 endif |
52 endwhile | |
548 | 53 |
26779 | 54 # Return the column of the last word, which is going to be changed. |
55 # Remember the text that comes before it in prepended. | |
548 | 56 if lastword == -1 |
26779 | 57 prepended = '' |
58 return byteidx(line, start) | |
548 | 59 endif |
26779 | 60 prepended = line[start : lastword - 1] |
61 return byteidx(line, lastword) | |
502 | 62 endif |
63 | |
26779 | 64 # Return list of matches. |
511 | 65 |
26779 | 66 var base: string = prepended .. abase |
548 | 67 |
26779 | 68 # Don't do anything for an empty base, would result in all the tags in the |
69 # tags file. | |
654 | 70 if base == '' |
71 return [] | |
72 endif | |
73 | |
26779 | 74 # init cache for vimgrep to empty |
75 grepCache = {} | |
720 | 76 |
26779 | 77 # 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 | |
654 | 84 while 1 |
26779 | 85 var e: number = base->charidx(match(base, '\.\|->\|\[', s)) |
654 | 86 if e < 0 |
87 if s == 0 || base[s - 1] != ']' | |
26779 | 88 items->add(base[s :]) |
654 | 89 endif |
90 break | |
91 endif | |
92 if s == 0 || base[s - 1] != ']' | |
26779 | 93 items->add(base[s : e - 1]) |
548 | 94 endif |
654 | 95 if base[e] == '.' |
26779 | 96 # skip over '.' |
97 s = e + 1 | |
654 | 98 elseif base[e] == '-' |
26779 | 99 # skip over '->' |
100 s = e + 2 | |
654 | 101 else |
26779 | 102 # 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 | |
654 | 116 endwhile |
26779 | 117 ++e |
118 items->add(base[s : e - 1]) | |
119 ++arrays | |
120 s = e | |
654 | 121 endif |
122 endwhile | |
505 | 123 |
26779 | 124 # 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 | |
1624 | 137 while line[col2] != ';' |
26779 | 138 --col2 |
1624 | 139 endwhile |
26779 | 140 line = line[col2 + 1 :] |
141 col -= col2 | |
1624 | 142 endif |
26779 | 143 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 | |
1624 | 147 while line[col2] != ',' |
26779 | 148 --col2 |
1624 | 149 endwhile |
26779 | 150 if line[col2 + 1 : col - 1] =~ ' *[^ ][^ ]* *[^ ]' |
151 line = line[col2 + 1 :] | |
152 col -= col2 | |
1624 | 153 endif |
154 endif | |
654 | 155 if len(items) == 1 |
26779 | 156 # 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 ..= '[' | |
654 | 162 else |
26779 | 163 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 | |
654 | 172 endif |
26779 | 173 res = [{match: match, tagline: '', kind: kind, info: line}] |
14668 | 174 elseif len(items) == arrays + 1 |
26779 | 175 # 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}] | |
654 | 181 else |
26779 | 182 # Completing "var.", "var.something", etc. |
183 res = line[: col - 1]->Nextitem(items[1 :], 0, true) | |
654 | 184 endif |
185 endif | |
186 | |
14668 | 187 if len(items) == 1 || len(items) == arrays + 1 |
26779 | 188 # Only one part, no "." or "->": complete from tags file. |
189 var tags: list<dict<any>> | |
14668 | 190 if len(items) == 1 |
26779 | 191 tags = taglist('^' .. base) |
14668 | 192 else |
26779 | 193 tags = taglist('^' .. items[0] .. '$') |
14668 | 194 endif |
734 | 195 |
26779 | 196 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'])) | |
734 | 205 |
27492 | 206 res = res->extend(tags->map((_, v: dict<any>) => Tag2item(v))) |
516 | 207 endif |
208 | |
209 if len(res) == 0 | |
26779 | 210 # 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. | |
213 ->filter((_, v: dict<string>): bool => | |
214 v->has_key('kind') ? v.kind != 'm' : true) | |
734 | 215 |
26779 | 216 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') | |
27492 | 220 res = res->extend(diclist[i]['typename']->StructMembers(items[1 :], true)) |
26779 | 221 elseif diclist[i]->has_key('typeref') |
27492 | 222 res = res->extend(diclist[i]['typeref']->StructMembers(items[1 :], true)) |
516 | 223 endif |
224 | |
26779 | 225 # For a variable use the command, which must be a search pattern that |
226 # shows the declaration of the variable. | |
505 | 227 if diclist[i]['kind'] == 'v' |
26779 | 228 var line: string = diclist[i]['cmd'] |
229 if line[: 1] == '/^' | |
230 var col: number = line->charidx(match(line, '\<' .. items[0] .. '\>')) | |
27492 | 231 res = res->extend(line[2 : col - 1]->Nextitem(items[1 :], 0, true)) |
26779 | 232 endif |
505 | 233 endif |
234 endfor | |
235 endif | |
236 | |
26779 | 237 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) | |
523 | 243 endif |
244 | |
26779 | 245 # 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 = '' | |
654 | 248 while last >= 0 |
249 if items[last][0] != '[' | |
250 break | |
523 | 251 endif |
26779 | 252 brackets = items[last] .. brackets |
253 --last | |
654 | 254 endwhile |
516 | 255 |
26779 | 256 return res->map((_, v: dict<any>): dict<string> => Tagline2item(v, brackets)) |
257 enddef | |
511 | 258 |
26779 | 259 def GetAddition( # {{{1 |
27492 | 260 line: string, |
261 match: string, | |
262 memarg: list<dict<any>>, | |
263 bracket: bool): string | |
26779 | 264 # Guess if the item is an array. |
265 if bracket && match(line, match .. '\s*\[') > 0 | |
654 | 266 return '[' |
267 endif | |
268 | |
26779 | 269 # 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 | |
654 | 273 return '->' |
274 else | |
275 return '.' | |
276 endif | |
277 endif | |
278 return '' | |
26779 | 279 enddef |
654 | 280 |
26779 | 281 def 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']} | |
734 | 287 |
26779 | 288 res['extra'] = Tagcmd2extra(val['cmd'], val['name'], val['filename']) |
666 | 289 |
26779 | 290 var s: string = Dict2info(val) |
734 | 291 if s != '' |
26779 | 292 res['info'] = s |
734 | 293 endif |
294 | |
26779 | 295 res['tagline'] = '' |
296 if val->has_key('kind') | |
297 var kind: string = val['kind'] | |
298 res['kind'] = kind | |
734 | 299 if kind == 'v' |
26779 | 300 res['tagline'] = "\t" .. val['cmd'] |
301 res['dict'] = val | |
734 | 302 elseif kind == 'f' |
26779 | 303 res['match'] = val['name'] .. '(' |
648 | 304 endif |
305 endif | |
734 | 306 |
307 return res | |
26779 | 308 enddef |
654 | 309 |
26779 | 310 def 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)) | |
800 | 315 if k == 'cmd' |
26779 | 316 info ..= dict['cmd'] |
317 ->matchstr('/^\s*\zs.*\ze$/') | |
318 ->substitute('\\\(.\)', '\1', 'g') | |
800 | 319 else |
26779 | 320 var dictk: any = dict[k] |
321 if typename(dictk) != 'string' | |
322 info ..= dictk->string() | |
323 else | |
324 info ..= dictk | |
325 endif | |
800 | 326 endif |
26779 | 327 info ..= "\n" |
800 | 328 endfor |
329 return info | |
26779 | 330 enddef |
800 | 331 |
26779 | 332 def 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> | |
800 | 336 if len(l) >= 3 |
26779 | 337 d['name'] = l[0] |
338 d['filename'] = l[1] | |
339 d['cmd'] = l[2] | |
340 var n: number = 2 | |
800 | 341 if l[2] =~ '^/' |
26779 | 342 # Find end of cmd, it may contain Tabs. |
800 | 343 while n < len(l) && l[n] !~ '/;"$' |
26779 | 344 ++n |
345 d['cmd'] ..= ' ' .. l[n] | |
800 | 346 endwhile |
347 endif | |
26779 | 348 for i: number in range(n + 1, len(l) - 1) |
800 | 349 if l[i] == 'file:' |
26779 | 350 d['static'] = 1 |
800 | 351 elseif l[i] !~ ':' |
26779 | 352 d['kind'] = l[i] |
800 | 353 else |
26779 | 354 d[l[i]->matchstr('[^:]*')] = l[i]->matchstr(':\zs.*') |
800 | 355 endif |
356 endfor | |
357 endif | |
358 | |
359 return d | |
26779 | 360 enddef |
800 | 361 |
26779 | 362 def 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} | |
734 | 369 |
26779 | 370 if val->has_key('info') |
371 # Use info from Tag2item(). | |
372 res['info'] = val['info'] | |
734 | 373 else |
26779 | 374 # Parse the tag line and add each part to the "info" entry. |
375 var s: string = ParseTagline(line)->Dict2info() | |
734 | 376 if s != '' |
26779 | 377 res['info'] = s |
734 | 378 endif |
379 endif | |
380 | |
26779 | 381 if val->has_key('kind') |
382 res['kind'] = val['kind'] | |
734 | 383 elseif add == '(' |
26779 | 384 res['kind'] = 'f' |
734 | 385 else |
26779 | 386 var s: string = line->matchstr('\t\(kind:\)\=\zs\S\ze\(\t\|$\)') |
734 | 387 if s != '' |
26779 | 388 res['kind'] = s |
734 | 389 endif |
390 endif | |
391 | |
26779 | 392 if val->has_key('extra') |
393 res['menu'] = val['extra'] | |
734 | 394 return res |
659 | 395 endif |
666 | 396 |
26779 | 397 # Isolate the command after the tag and filename. |
398 var s: string = line->matchstr('[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)') | |
666 | 399 if s != '' |
26779 | 400 res['menu'] = s->Tagcmd2extra(val['match'], line->matchstr('[^\t]*\t\zs[^\t]*\ze\t')) |
666 | 401 endif |
734 | 402 return res |
26779 | 403 enddef |
648 | 404 |
26779 | 405 def Tagcmd2extra( # {{{1 |
27492 | 406 cmd: string, |
407 name: string, | |
408 fname: string): string | |
26779 | 409 # 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 | |
666 | 421 else |
26779 | 422 # Not recognized, use command and file name. |
423 x = cmd .. ' - ' .. fname | |
666 | 424 endif |
425 return x | |
26779 | 426 enddef |
511 | 427 |
26779 | 428 def Nextitem( # {{{1 |
27492 | 429 lead: string, |
430 items: list<string>, | |
431 depth: number, | |
432 all: bool): list<dict<string>> | |
26779 | 433 # 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. | |
511 | 437 |
26779 | 438 # Use the text up to the variable name and split it in tokens. |
439 var tokens: list<string> = split(lead, '\s\+\|\<') | |
505 | 440 |
26779 | 441 # 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() | |
444 | |
445 # Skip tokens starting with a non-ID character. | |
720 | 446 if tokens[tidx] !~ '^\h' |
447 continue | |
448 endif | |
449 | |
26779 | 450 # 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) | |
511 | 458 break |
459 endif | |
460 | |
26779 | 461 # TODO: add more reserved words |
462 if ['int', 'short', 'char', 'float', | |
463 'double', 'static', 'unsigned', 'extern']->index(tokens[tidx]) >= 0 | |
516 | 464 continue |
465 endif | |
466 | |
26779 | 467 # 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] | |
734 | 471 |
26779 | 472 # New ctags has the "typeref" field. Patched version has "typename". |
473 if item->has_key('typeref') | |
27492 | 474 res = res->extend(item['typeref']->StructMembers(items, all)) |
26779 | 475 continue |
800 | 476 endif |
26779 | 477 if item->has_key('typename') |
27492 | 478 res = res->extend(item['typename']->StructMembers(items, all)) |
26779 | 479 continue |
523 | 480 endif |
481 | |
26779 | 482 # Only handle typedefs here. |
734 | 483 if item['kind'] != 't' |
26779 | 484 continue |
734 | 485 endif |
486 | |
26779 | 487 # Skip matches local to another file. |
488 if item->has_key('static') && item['static'] | |
489 && bufnr('%') != bufnr(item['filename']) | |
490 continue | |
516 | 491 endif |
492 | |
26779 | 493 # 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\+')) | |
523 | 497 if ei > 1 |
26779 | 498 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 != '' | |
27492 | 512 res = res->extend(StructMembers(cmdtokens[0] .. ':' .. name, items, all)) |
26779 | 513 endif |
514 elseif depth < 10 | |
515 # Could be "typedef other_T some_T". | |
27492 | 516 res = res->extend(cmdtokens[0]->Nextitem(items, depth + 1, all)) |
26779 | 517 endif |
518 endif | |
511 | 519 endif |
520 endfor | |
516 | 521 if len(res) > 0 |
505 | 522 break |
523 endif | |
516 | 524 endfor |
511 | 525 |
516 | 526 return res |
26779 | 527 enddef |
516 | 528 |
26779 | 529 def StructMembers( # {{{1 |
27492 | 530 atypename: string, |
531 items: list<string>, | |
532 all: bool): list<dict<string>> | |
516 | 533 |
26779 | 534 # 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() | |
516 | 543 if fnames == '' |
523 | 544 return [] |
516 | 545 endif |
546 | |
26779 | 547 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 | |
720 | 556 endif |
716 | 557 else |
26779 | 558 n = '' |
716 | 559 endif |
720 | 560 if !cached |
561 while 1 | |
26779 | 562 execute 'silent! keepjumps noautocmd ' |
563 .. n .. 'vimgrep ' .. '/\t' .. typename .. '\(\t\|$\)/j ' | |
564 .. fnames | |
720 | 565 |
26779 | 566 qflist = getqflist() |
567 if len(qflist) > 0 || match(typename, '::') < 0 | |
568 break | |
720 | 569 endif |
26779 | 570 # No match for "struct:context::name", remove "context::" and try again. |
571 typename = typename->substitute(':[^:]*::', ':', '') | |
720 | 572 endwhile |
573 | |
26779 | 574 if !all |
575 # Store the result to be able to use it again later. | |
576 grepCache[typename] = qflist | |
516 | 577 endif |
720 | 578 endif |
516 | 579 |
26779 | 580 # Skip over [...] items |
581 var idx: number = 0 | |
582 var target: string | |
14668 | 583 while 1 |
26779 | 584 if idx >= len(items) |
585 target = '' # No further items, matching all members | |
14668 | 586 break |
587 endif | |
26779 | 588 if items[idx][0] != '[' |
589 target = items[idx] | |
14668 | 590 break |
591 endif | |
26779 | 592 ++idx |
14668 | 593 endwhile |
26779 | 594 # 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']} | |
734 | 603 |
26779 | 604 # 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 | |
734 | 612 |
26779 | 613 matches->add(item) |
734 | 614 endif |
516 | 615 endif |
511 | 616 endfor |
505 | 617 |
523 | 618 if len(matches) > 0 |
26779 | 619 # Skip over next [...] items |
620 ++idx | |
654 | 621 while 1 |
26779 | 622 if idx >= len(items) |
623 return matches # No further items, return the result. | |
654 | 624 endif |
26779 | 625 if items[idx][0] != '[' |
626 break | |
654 | 627 endif |
26779 | 628 ++idx |
654 | 629 endwhile |
505 | 630 |
26779 | 631 # More items following. For each of the possible members find the |
632 # matching following members. | |
633 return SearchMembers(matches, items[idx :], all) | |
511 | 634 endif |
635 | |
26779 | 636 # Failed to find anything. |
511 | 637 return [] |
26779 | 638 enddef |
639 | |
640 def SearchMembers( # {{{1 | |
27492 | 641 matches: list<dict<any>>, |
642 items: list<string>, | |
643 all: bool): list<dict<string>> | |
523 | 644 |
26779 | 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'] | |
648 | 656 endif |
26779 | 657 line = "\t" .. matches[i]['dict']['cmd'] |
648 | 658 else |
26779 | 659 line = matches[i]['tagline'] |
660 var eb: number = matchend(line, '\ttypename:') | |
661 var e: number = charidx(line, eb) | |
800 | 662 if e < 0 |
26779 | 663 eb = matchend(line, '\ttyperef:') |
664 e = charidx(line, eb) | |
800 | 665 endif |
648 | 666 if e > 0 |
26779 | 667 # Use typename field |
668 typename = line->matchstr('[^\t]*', eb) | |
648 | 669 endif |
670 endif | |
716 | 671 |
648 | 672 if typename != '' |
27492 | 673 res = res->extend(StructMembers(typename, items, all)) |
523 | 674 else |
26779 | 675 # Use the search command (the declaration itself). |
676 var sb: number = line->match('\t\zs/^') | |
677 var s: number = charidx(line, sb) | |
523 | 678 if s > 0 |
26779 | 679 var e: number = line |
680 ->charidx(match(line, '\<' .. matches[i]['match'] .. '\>', sb)) | |
681 if e > 0 | |
27492 | 682 res = res->extend(line[s : e - 1]->Nextitem(items, 0, all)) |
26779 | 683 endif |
523 | 684 endif |
685 endif | |
26779 | 686 if !all && len(res) > 0 |
716 | 687 break |
688 endif | |
523 | 689 endfor |
690 return res | |
26779 | 691 enddef |
692 #}}}1 | |
3237 | 693 |
26779 | 694 # vim: noet sw=2 sts=2 |