1e344beadSBram Moolenaar" Vim completion script 2e344beadSBram Moolenaar" Language: C 3e344beadSBram Moolenaar" Maintainer: Bram Moolenaar <[email protected]> 4*4466ad6bSBram Moolenaar" Last Change: 2020 Nov 14 5e344beadSBram Moolenaar 6b6b046b2SBram Moolenaarlet s:cpo_save = &cpo 7b6b046b2SBram Moolenaarset cpo&vim 8a4a08388SBram Moolenaar 9f75a963eSBram Moolenaar" This function is used for the 'omnifunc' option. 10*4466ad6bSBram Moolenaarfunc ccomplete#Complete(findstart, base) 11e344beadSBram Moolenaar if a:findstart 120e5bd96fSBram Moolenaar " Locate the start of the item, including ".", "->" and "[...]". 13e344beadSBram Moolenaar let line = getline('.') 14e344beadSBram Moolenaar let start = col('.') - 1 15d5cdbeb8SBram Moolenaar let lastword = -1 16e344beadSBram Moolenaar while start > 0 17d5cdbeb8SBram Moolenaar if line[start - 1] =~ '\w' 18d5cdbeb8SBram Moolenaar let start -= 1 19d5cdbeb8SBram Moolenaar elseif line[start - 1] =~ '\.' 20d5cdbeb8SBram Moolenaar if lastword == -1 21d5cdbeb8SBram Moolenaar let lastword = start 22d5cdbeb8SBram Moolenaar endif 23e344beadSBram Moolenaar let start -= 1 24e344beadSBram Moolenaar elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>' 25d5cdbeb8SBram Moolenaar if lastword == -1 26d5cdbeb8SBram Moolenaar let lastword = start 27d5cdbeb8SBram Moolenaar endif 28e344beadSBram Moolenaar let start -= 2 290e5bd96fSBram Moolenaar elseif line[start - 1] == ']' 300e5bd96fSBram Moolenaar " Skip over [...]. 310e5bd96fSBram Moolenaar let n = 0 320e5bd96fSBram Moolenaar let start -= 1 330e5bd96fSBram Moolenaar while start > 0 340e5bd96fSBram Moolenaar let start -= 1 350e5bd96fSBram Moolenaar if line[start] == '[' 360e5bd96fSBram Moolenaar if n == 0 370e5bd96fSBram Moolenaar break 380e5bd96fSBram Moolenaar endif 390e5bd96fSBram Moolenaar let n -= 1 400e5bd96fSBram Moolenaar elseif line[start] == ']' " nested [] 410e5bd96fSBram Moolenaar let n += 1 420e5bd96fSBram Moolenaar endif 430e5bd96fSBram Moolenaar endwhile 44e344beadSBram Moolenaar else 45e344beadSBram Moolenaar break 46e344beadSBram Moolenaar endif 47e344beadSBram Moolenaar endwhile 48d5cdbeb8SBram Moolenaar 49d5cdbeb8SBram Moolenaar " Return the column of the last word, which is going to be changed. 50d5cdbeb8SBram Moolenaar " Remember the text that comes before it in s:prepended. 51d5cdbeb8SBram Moolenaar if lastword == -1 52d5cdbeb8SBram Moolenaar let s:prepended = '' 53e344beadSBram Moolenaar return start 54e344beadSBram Moolenaar endif 55d5cdbeb8SBram Moolenaar let s:prepended = strpart(line, start, lastword - start) 56d5cdbeb8SBram Moolenaar return lastword 57d5cdbeb8SBram Moolenaar endif 58e344beadSBram Moolenaar 59caa0fcfaSBram Moolenaar " Return list of matches. 60caa0fcfaSBram Moolenaar 61d5cdbeb8SBram Moolenaar let base = s:prepended . a:base 62d5cdbeb8SBram Moolenaar 63d5cdbeb8SBram Moolenaar " Don't do anything for an empty base, would result in all the tags in the 64d5cdbeb8SBram Moolenaar " tags file. 65d5cdbeb8SBram Moolenaar if base == '' 66d5cdbeb8SBram Moolenaar return [] 67d5cdbeb8SBram Moolenaar endif 68d5cdbeb8SBram Moolenaar 691056d988SBram Moolenaar " init cache for vimgrep to empty 701056d988SBram Moolenaar let s:grepCache = {} 711056d988SBram Moolenaar 720e5bd96fSBram Moolenaar " Split item in words, keep empty word after "." or "->". 730e5bd96fSBram Moolenaar " "aa" -> ['aa'], "aa." -> ['aa', ''], "aa.bb" -> ['aa', 'bb'], etc. 740e5bd96fSBram Moolenaar " We can't use split, because we need to skip nested [...]. 7520aac6c1SBram Moolenaar " "aa[...]" -> ['aa', '[...]'], "aa.bb[...]" -> ['aa', 'bb', '[...]'], etc. 760e5bd96fSBram Moolenaar let items = [] 770e5bd96fSBram Moolenaar let s = 0 7820aac6c1SBram Moolenaar let arrays = 0 790e5bd96fSBram Moolenaar while 1 800e5bd96fSBram Moolenaar let e = match(base, '\.\|->\|\[', s) 810e5bd96fSBram Moolenaar if e < 0 820e5bd96fSBram Moolenaar if s == 0 || base[s - 1] != ']' 830e5bd96fSBram Moolenaar call add(items, strpart(base, s)) 84e344beadSBram Moolenaar endif 850e5bd96fSBram Moolenaar break 860e5bd96fSBram Moolenaar endif 870e5bd96fSBram Moolenaar if s == 0 || base[s - 1] != ']' 880e5bd96fSBram Moolenaar call add(items, strpart(base, s, e - s)) 890e5bd96fSBram Moolenaar endif 900e5bd96fSBram Moolenaar if base[e] == '.' 910e5bd96fSBram Moolenaar let s = e + 1 " skip over '.' 920e5bd96fSBram Moolenaar elseif base[e] == '-' 930e5bd96fSBram Moolenaar let s = e + 2 " skip over '->' 940e5bd96fSBram Moolenaar else 950e5bd96fSBram Moolenaar " Skip over [...]. 960e5bd96fSBram Moolenaar let n = 0 970e5bd96fSBram Moolenaar let s = e 980e5bd96fSBram Moolenaar let e += 1 990e5bd96fSBram Moolenaar while e < len(base) 1000e5bd96fSBram Moolenaar if base[e] == ']' 1010e5bd96fSBram Moolenaar if n == 0 1020e5bd96fSBram Moolenaar break 1030e5bd96fSBram Moolenaar endif 1040e5bd96fSBram Moolenaar let n -= 1 1050e5bd96fSBram Moolenaar elseif base[e] == '[' " nested [...] 1060e5bd96fSBram Moolenaar let n += 1 1070e5bd96fSBram Moolenaar endif 1080e5bd96fSBram Moolenaar let e += 1 1090e5bd96fSBram Moolenaar endwhile 1100e5bd96fSBram Moolenaar let e += 1 1110e5bd96fSBram Moolenaar call add(items, strpart(base, s, e - s)) 11220aac6c1SBram Moolenaar let arrays += 1 1130e5bd96fSBram Moolenaar let s = e 1140e5bd96fSBram Moolenaar endif 1150e5bd96fSBram Moolenaar endwhile 116dd2436f3SBram Moolenaar 117a4a08388SBram Moolenaar " Find the variable items[0]. 118a4a08388SBram Moolenaar " 1. in current function (like with "gd") 119a4a08388SBram Moolenaar " 2. in tags file(s) (like with ":tag") 120a4a08388SBram Moolenaar " 3. in current file (like with "gD") 121a4a08388SBram Moolenaar let res = [] 122f75a963eSBram Moolenaar if searchdecl(items[0], 0, 1) == 0 123dd2436f3SBram Moolenaar " Found, now figure out the type. 124dd2436f3SBram Moolenaar " TODO: join previous line if it makes sense 125dd2436f3SBram Moolenaar let line = getline('.') 126dd2436f3SBram Moolenaar let col = col('.') 1278c8de839SBram Moolenaar if stridx(strpart(line, 0, col), ';') != -1 1288c8de839SBram Moolenaar " Handle multiple declarations on the same line. 1298c8de839SBram Moolenaar let col2 = col - 1 1308c8de839SBram Moolenaar while line[col2] != ';' 1318c8de839SBram Moolenaar let col2 -= 1 1328c8de839SBram Moolenaar endwhile 1338c8de839SBram Moolenaar let line = strpart(line, col2 + 1) 1348c8de839SBram Moolenaar let col -= col2 1358c8de839SBram Moolenaar endif 1368c8de839SBram Moolenaar if stridx(strpart(line, 0, col), ',') != -1 1378c8de839SBram Moolenaar " Handle multiple declarations on the same line in a function 1388c8de839SBram Moolenaar " declaration. 1398c8de839SBram Moolenaar let col2 = col - 1 1408c8de839SBram Moolenaar while line[col2] != ',' 1418c8de839SBram Moolenaar let col2 -= 1 1428c8de839SBram Moolenaar endwhile 1438c8de839SBram Moolenaar if strpart(line, col2 + 1, col - col2 - 1) =~ ' *[^ ][^ ]* *[^ ]' 1448c8de839SBram Moolenaar let line = strpart(line, col2 + 1) 1458c8de839SBram Moolenaar let col -= col2 1468c8de839SBram Moolenaar endif 1478c8de839SBram Moolenaar endif 1480e5bd96fSBram Moolenaar if len(items) == 1 1490e5bd96fSBram Moolenaar " Completing one word and it's a local variable: May add '[', '.' or 1500e5bd96fSBram Moolenaar " '->'. 1510e5bd96fSBram Moolenaar let match = items[0] 152eb94e559SBram Moolenaar let kind = 'v' 153eb94e559SBram Moolenaar if match(line, '\<' . match . '\s*\[') > 0 1540e5bd96fSBram Moolenaar let match .= '[' 1550e5bd96fSBram Moolenaar else 1561f35bf9cSBram Moolenaar let res = s:Nextitem(strpart(line, 0, col), [''], 0, 1) 1570e5bd96fSBram Moolenaar if len(res) > 0 1580e5bd96fSBram Moolenaar " There are members, thus add "." or "->". 1590e5bd96fSBram Moolenaar if match(line, '\*[ \t(]*' . match . '\>') > 0 1600e5bd96fSBram Moolenaar let match .= '->' 1610e5bd96fSBram Moolenaar else 1620e5bd96fSBram Moolenaar let match .= '.' 1630e5bd96fSBram Moolenaar endif 1640e5bd96fSBram Moolenaar endif 1650e5bd96fSBram Moolenaar endif 166eb94e559SBram Moolenaar let res = [{'match': match, 'tagline' : '', 'kind' : kind, 'info' : line}] 16720aac6c1SBram Moolenaar elseif len(items) == arrays + 1 16820aac6c1SBram Moolenaar " Completing one word and it's a local array variable: build tagline 16920aac6c1SBram Moolenaar " from declaration line 17020aac6c1SBram Moolenaar let match = items[0] 17120aac6c1SBram Moolenaar let kind = 'v' 17220aac6c1SBram Moolenaar let tagline = "\t/^" . line . '$/' 17320aac6c1SBram Moolenaar let res = [{'match': match, 'tagline' : tagline, 'kind' : kind, 'info' : line}] 1740e5bd96fSBram Moolenaar else 1750e5bd96fSBram Moolenaar " Completing "var.", "var.something", etc. 17600a927d6SBram Moolenaar let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1) 1770e5bd96fSBram Moolenaar endif 1780e5bd96fSBram Moolenaar endif 1790e5bd96fSBram Moolenaar 18020aac6c1SBram Moolenaar if len(items) == 1 || len(items) == arrays + 1 1810e5bd96fSBram Moolenaar " Only one part, no "." or "->": complete from tags file. 18220aac6c1SBram Moolenaar if len(items) == 1 183eb94e559SBram Moolenaar let tags = taglist('^' . base) 18420aac6c1SBram Moolenaar else 18520aac6c1SBram Moolenaar let tags = taglist('^' . items[0] . '$') 18620aac6c1SBram Moolenaar endif 187eb94e559SBram Moolenaar 188eb94e559SBram Moolenaar " Remove members, these can't appear without something in front. 189eb94e559SBram Moolenaar call filter(tags, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1') 190eb94e559SBram Moolenaar 191eb94e559SBram Moolenaar " Remove static matches in other files. 192eb94e559SBram Moolenaar call filter(tags, '!has_key(v:val, "static") || !v:val["static"] || bufnr("%") == bufnr(v:val["filename"])') 193eb94e559SBram Moolenaar 194eb94e559SBram Moolenaar call extend(res, map(tags, 's:Tag2item(v:val)')) 195a4a08388SBram Moolenaar endif 196a4a08388SBram Moolenaar 197a4a08388SBram Moolenaar if len(res) == 0 198a4a08388SBram Moolenaar " Find the variable in the tags file(s) 199caa0fcfaSBram Moolenaar let diclist = taglist('^' . items[0] . '$') 200caa0fcfaSBram Moolenaar 201eb94e559SBram Moolenaar " Remove members, these can't appear without something in front. 202eb94e559SBram Moolenaar call filter(diclist, 'has_key(v:val, "kind") ? v:val["kind"] != "m" : 1') 203eb94e559SBram Moolenaar 204caa0fcfaSBram Moolenaar let res = [] 205dd2436f3SBram Moolenaar for i in range(len(diclist)) 20676b92b28SBram Moolenaar " New ctags has the "typeref" field. Patched version has "typename". 207a4a08388SBram Moolenaar if has_key(diclist[i], 'typename') 2081f35bf9cSBram Moolenaar call extend(res, s:StructMembers(diclist[i]['typename'], items[1:], 1)) 20976b92b28SBram Moolenaar elseif has_key(diclist[i], 'typeref') 21076b92b28SBram Moolenaar call extend(res, s:StructMembers(diclist[i]['typeref'], items[1:], 1)) 211a4a08388SBram Moolenaar endif 212a4a08388SBram Moolenaar 213a4a08388SBram Moolenaar " For a variable use the command, which must be a search pattern that 214a4a08388SBram Moolenaar " shows the declaration of the variable. 215dd2436f3SBram Moolenaar if diclist[i]['kind'] == 'v' 216dd2436f3SBram Moolenaar let line = diclist[i]['cmd'] 217dd2436f3SBram Moolenaar if line[0] == '/' && line[1] == '^' 218e3226be9SBram Moolenaar let col = match(line, '\<' . items[0] . '\>') 2191f35bf9cSBram Moolenaar call extend(res, s:Nextitem(strpart(line, 2, col - 2), items[1:], 0, 1)) 220caa0fcfaSBram Moolenaar endif 221dd2436f3SBram Moolenaar endif 222dd2436f3SBram Moolenaar endfor 223dd2436f3SBram Moolenaar endif 224dd2436f3SBram Moolenaar 225a4a08388SBram Moolenaar if len(res) == 0 && searchdecl(items[0], 1) == 0 226a4a08388SBram Moolenaar " Found, now figure out the type. 227a4a08388SBram Moolenaar " TODO: join previous line if it makes sense 228a4a08388SBram Moolenaar let line = getline('.') 229a4a08388SBram Moolenaar let col = col('.') 2301f35bf9cSBram Moolenaar let res = s:Nextitem(strpart(line, 0, col), items[1:], 0, 1) 231f75a963eSBram Moolenaar endif 232f75a963eSBram Moolenaar 2330e5bd96fSBram Moolenaar " If the last item(s) are [...] they need to be added to the matches. 2340e5bd96fSBram Moolenaar let last = len(items) - 1 2350e5bd96fSBram Moolenaar let brackets = '' 2360e5bd96fSBram Moolenaar while last >= 0 2370e5bd96fSBram Moolenaar if items[last][0] != '[' 2380e5bd96fSBram Moolenaar break 239f75a963eSBram Moolenaar endif 2400e5bd96fSBram Moolenaar let brackets = items[last] . brackets 2410e5bd96fSBram Moolenaar let last -= 1 2420e5bd96fSBram Moolenaar endwhile 243a4a08388SBram Moolenaar 2440e5bd96fSBram Moolenaar return map(res, 's:Tagline2item(v:val, brackets)') 245caa0fcfaSBram Moolenaarendfunc 246dd2436f3SBram Moolenaar 247*4466ad6bSBram Moolenaarfunc s:GetAddition(line, match, memarg, bracket) 2480e5bd96fSBram Moolenaar " Guess if the item is an array. 2490e5bd96fSBram Moolenaar if a:bracket && match(a:line, a:match . '\s*\[') > 0 2500e5bd96fSBram Moolenaar return '[' 2510e5bd96fSBram Moolenaar endif 2520e5bd96fSBram Moolenaar 2530e5bd96fSBram Moolenaar " Check if the item has members. 2541f35bf9cSBram Moolenaar if len(s:SearchMembers(a:memarg, [''], 0)) > 0 2550e5bd96fSBram Moolenaar " If there is a '*' before the name use "->". 2560e5bd96fSBram Moolenaar if match(a:line, '\*[ \t(]*' . a:match . '\>') > 0 2570e5bd96fSBram Moolenaar return '->' 2580e5bd96fSBram Moolenaar else 2590e5bd96fSBram Moolenaar return '.' 2600e5bd96fSBram Moolenaar endif 2610e5bd96fSBram Moolenaar endif 2620e5bd96fSBram Moolenaar return '' 263*4466ad6bSBram Moolenaarendfunc 2640e5bd96fSBram Moolenaar 265280f126eSBram Moolenaar" Turn the tag info "val" into an item for completion. 266280f126eSBram Moolenaar" "val" is is an item in the list returned by taglist(). 2670e5bd96fSBram Moolenaar" If it is a variable we may add "." or "->". Don't do it for other types, 2680e5bd96fSBram Moolenaar" such as a typedef, by not including the info that s:GetAddition() uses. 269*4466ad6bSBram Moolenaarfunc s:Tag2item(val) 270eb94e559SBram Moolenaar let res = {'match': a:val['name']} 271f52c725cSBram Moolenaar 272eb94e559SBram Moolenaar let res['extra'] = s:Tagcmd2extra(a:val['cmd'], a:val['name'], a:val['filename']) 273eb94e559SBram Moolenaar 27476b92b28SBram Moolenaar let s = s:Dict2info(a:val) 275eb94e559SBram Moolenaar if s != '' 27676b92b28SBram Moolenaar let res['info'] = s 277eb94e559SBram Moolenaar endif 278eb94e559SBram Moolenaar 279eb94e559SBram Moolenaar let res['tagline'] = '' 2800e5bd96fSBram Moolenaar if has_key(a:val, "kind") 281eb94e559SBram Moolenaar let kind = a:val['kind'] 282eb94e559SBram Moolenaar let res['kind'] = kind 283eb94e559SBram Moolenaar if kind == 'v' 284eb94e559SBram Moolenaar let res['tagline'] = "\t" . a:val['cmd'] 285eb94e559SBram Moolenaar let res['dict'] = a:val 286eb94e559SBram Moolenaar elseif kind == 'f' 287eb94e559SBram Moolenaar let res['match'] = a:val['name'] . '(' 288280f126eSBram Moolenaar endif 289280f126eSBram Moolenaar endif 290eb94e559SBram Moolenaar 291eb94e559SBram Moolenaar return res 292*4466ad6bSBram Moolenaarendfunc 2930e5bd96fSBram Moolenaar 29476b92b28SBram Moolenaar" Use all the items in dictionary for the "info" entry. 295*4466ad6bSBram Moolenaarfunc s:Dict2info(dict) 29676b92b28SBram Moolenaar let info = '' 29776b92b28SBram Moolenaar for k in sort(keys(a:dict)) 29876b92b28SBram Moolenaar let info .= k . repeat(' ', 10 - len(k)) 29976b92b28SBram Moolenaar if k == 'cmd' 30076b92b28SBram Moolenaar let info .= substitute(matchstr(a:dict['cmd'], '/^\s*\zs.*\ze$/'), '\\\(.\)', '\1', 'g') 30176b92b28SBram Moolenaar else 30276b92b28SBram Moolenaar let info .= a:dict[k] 30376b92b28SBram Moolenaar endif 30476b92b28SBram Moolenaar let info .= "\n" 30576b92b28SBram Moolenaar endfor 30676b92b28SBram Moolenaar return info 30776b92b28SBram Moolenaarendfunc 30876b92b28SBram Moolenaar 30976b92b28SBram Moolenaar" Parse a tag line and return a dictionary with items like taglist() 310*4466ad6bSBram Moolenaarfunc s:ParseTagline(line) 31176b92b28SBram Moolenaar let l = split(a:line, "\t") 31276b92b28SBram Moolenaar let d = {} 31376b92b28SBram Moolenaar if len(l) >= 3 31476b92b28SBram Moolenaar let d['name'] = l[0] 31576b92b28SBram Moolenaar let d['filename'] = l[1] 31676b92b28SBram Moolenaar let d['cmd'] = l[2] 31776b92b28SBram Moolenaar let n = 2 31876b92b28SBram Moolenaar if l[2] =~ '^/' 31976b92b28SBram Moolenaar " Find end of cmd, it may contain Tabs. 32076b92b28SBram Moolenaar while n < len(l) && l[n] !~ '/;"$' 32176b92b28SBram Moolenaar let n += 1 32276b92b28SBram Moolenaar let d['cmd'] .= " " . l[n] 32376b92b28SBram Moolenaar endwhile 32476b92b28SBram Moolenaar endif 32576b92b28SBram Moolenaar for i in range(n + 1, len(l) - 1) 32676b92b28SBram Moolenaar if l[i] == 'file:' 32776b92b28SBram Moolenaar let d['static'] = 1 32876b92b28SBram Moolenaar elseif l[i] !~ ':' 32976b92b28SBram Moolenaar let d['kind'] = l[i] 33076b92b28SBram Moolenaar else 33176b92b28SBram Moolenaar let d[matchstr(l[i], '[^:]*')] = matchstr(l[i], ':\zs.*') 33276b92b28SBram Moolenaar endif 33376b92b28SBram Moolenaar endfor 33476b92b28SBram Moolenaar endif 33576b92b28SBram Moolenaar 33676b92b28SBram Moolenaar return d 337*4466ad6bSBram Moolenaarendfunc 33876b92b28SBram Moolenaar 3390e5bd96fSBram Moolenaar" Turn a match item "val" into an item for completion. 3400e5bd96fSBram Moolenaar" "val['match']" is the matching item. 3410e5bd96fSBram Moolenaar" "val['tagline']" is the tagline in which the last part was found. 342*4466ad6bSBram Moolenaarfunc s:Tagline2item(val, brackets) 343f52c725cSBram Moolenaar let line = a:val['tagline'] 344eb94e559SBram Moolenaar let add = s:GetAddition(line, a:val['match'], [a:val], a:brackets == '') 345eb94e559SBram Moolenaar let res = {'word': a:val['match'] . a:brackets . add } 346eb94e559SBram Moolenaar 347eb94e559SBram Moolenaar if has_key(a:val, 'info') 348eb94e559SBram Moolenaar " Use info from Tag2item(). 349eb94e559SBram Moolenaar let res['info'] = a:val['info'] 350eb94e559SBram Moolenaar else 35176b92b28SBram Moolenaar " Parse the tag line and add each part to the "info" entry. 35276b92b28SBram Moolenaar let s = s:Dict2info(s:ParseTagline(line)) 353eb94e559SBram Moolenaar if s != '' 35476b92b28SBram Moolenaar let res['info'] = s 355eb94e559SBram Moolenaar endif 356eb94e559SBram Moolenaar endif 357eb94e559SBram Moolenaar 358eb94e559SBram Moolenaar if has_key(a:val, 'kind') 359eb94e559SBram Moolenaar let res['kind'] = a:val['kind'] 360eb94e559SBram Moolenaar elseif add == '(' 361eb94e559SBram Moolenaar let res['kind'] = 'f' 362eb94e559SBram Moolenaar else 363eb94e559SBram Moolenaar let s = matchstr(line, '\t\(kind:\)\=\zs\S\ze\(\t\|$\)') 364eb94e559SBram Moolenaar if s != '' 365eb94e559SBram Moolenaar let res['kind'] = s 366eb94e559SBram Moolenaar endif 367eb94e559SBram Moolenaar endif 368eb94e559SBram Moolenaar 3698b6144bdSBram Moolenaar if has_key(a:val, 'extra') 370eb94e559SBram Moolenaar let res['menu'] = a:val['extra'] 371eb94e559SBram Moolenaar return res 3728b6144bdSBram Moolenaar endif 373f52c725cSBram Moolenaar 374f52c725cSBram Moolenaar " Isolate the command after the tag and filename. 375f52c725cSBram Moolenaar let s = matchstr(line, '[^\t]*\t[^\t]*\t\zs\(/^.*$/\|[^\t]*\)\ze\(;"\t\|\t\|$\)') 376f52c725cSBram Moolenaar if s != '' 377eb94e559SBram Moolenaar let res['menu'] = s:Tagcmd2extra(s, a:val['match'], matchstr(line, '[^\t]*\t\zs[^\t]*\ze\t')) 378f52c725cSBram Moolenaar endif 379eb94e559SBram Moolenaar return res 380*4466ad6bSBram Moolenaarendfunc 381280f126eSBram Moolenaar 382f52c725cSBram Moolenaar" Turn a command from a tag line to something that is useful in the menu 383*4466ad6bSBram Moolenaarfunc s:Tagcmd2extra(cmd, name, fname) 384f52c725cSBram Moolenaar if a:cmd =~ '^/^' 385f52c725cSBram Moolenaar " The command is a search command, useful to see what it is. 3861f35bf9cSBram Moolenaar let x = matchstr(a:cmd, '^/^\s*\zs.*\ze$/') 3871f35bf9cSBram Moolenaar let x = substitute(x, '\<' . a:name . '\>', '@@', '') 388f52c725cSBram Moolenaar let x = substitute(x, '\\\(.\)', '\1', 'g') 389f52c725cSBram Moolenaar let x = x . ' - ' . a:fname 390f52c725cSBram Moolenaar elseif a:cmd =~ '^\d*$' 391f52c725cSBram Moolenaar " The command is a line number, the file name is more useful. 392f52c725cSBram Moolenaar let x = a:fname . ' - ' . a:cmd 393f52c725cSBram Moolenaar else 394f52c725cSBram Moolenaar " Not recognized, use command and file name. 395f52c725cSBram Moolenaar let x = a:cmd . ' - ' . a:fname 396f52c725cSBram Moolenaar endif 397f52c725cSBram Moolenaar return x 398*4466ad6bSBram Moolenaarendfunc 399280f126eSBram Moolenaar 400a4a08388SBram Moolenaar" Find composing type in "lead" and match items[0] with it. 401a4a08388SBram Moolenaar" Repeat this recursively for items[1], if it's there. 4020e5bd96fSBram Moolenaar" When resolving typedefs "depth" is used to avoid infinite recursion. 403a4a08388SBram Moolenaar" Return the list of matches. 404*4466ad6bSBram Moolenaarfunc s:Nextitem(lead, items, depth, all) 405caa0fcfaSBram Moolenaar 406caa0fcfaSBram Moolenaar " Use the text up to the variable name and split it in tokens. 407caa0fcfaSBram Moolenaar let tokens = split(a:lead, '\s\+\|\<') 408caa0fcfaSBram Moolenaar 409caa0fcfaSBram Moolenaar " Try to recognize the type of the variable. This is rough guessing... 410a4a08388SBram Moolenaar let res = [] 411caa0fcfaSBram Moolenaar for tidx in range(len(tokens)) 412caa0fcfaSBram Moolenaar 4131056d988SBram Moolenaar " Skip tokens starting with a non-ID character. 4141056d988SBram Moolenaar if tokens[tidx] !~ '^\h' 4151056d988SBram Moolenaar continue 4161056d988SBram Moolenaar endif 4171056d988SBram Moolenaar 418a4a08388SBram Moolenaar " Recognize "struct foobar" and "union foobar". 4198b2d9c43SBram Moolenaar " Also do "class foobar" when it's C++ after all (doesn't work very well 4208b2d9c43SBram Moolenaar " though). 4218b2d9c43SBram Moolenaar if (tokens[tidx] == 'struct' || tokens[tidx] == 'union' || tokens[tidx] == 'class') && tidx + 1 < len(tokens) 4221f35bf9cSBram Moolenaar let res = s:StructMembers(tokens[tidx] . ':' . tokens[tidx + 1], a:items, a:all) 423dd2436f3SBram Moolenaar break 424dd2436f3SBram Moolenaar endif 425dd2436f3SBram Moolenaar 426a4a08388SBram Moolenaar " TODO: add more reserved words 4270e5bd96fSBram Moolenaar if index(['int', 'short', 'char', 'float', 'double', 'static', 'unsigned', 'extern'], tokens[tidx]) >= 0 428a4a08388SBram Moolenaar continue 429a4a08388SBram Moolenaar endif 430a4a08388SBram Moolenaar 431a4a08388SBram Moolenaar " Use the tags file to find out if this is a typedef. 432caa0fcfaSBram Moolenaar let diclist = taglist('^' . tokens[tidx] . '$') 433f75a963eSBram Moolenaar for tagidx in range(len(diclist)) 434eb94e559SBram Moolenaar let item = diclist[tagidx] 435eb94e559SBram Moolenaar 43676b92b28SBram Moolenaar " New ctags has the "typeref" field. Patched version has "typename". 43776b92b28SBram Moolenaar if has_key(item, 'typeref') 43876b92b28SBram Moolenaar call extend(res, s:StructMembers(item['typeref'], a:items, a:all)) 43976b92b28SBram Moolenaar continue 44076b92b28SBram Moolenaar endif 441eb94e559SBram Moolenaar if has_key(item, 'typename') 442eb94e559SBram Moolenaar call extend(res, s:StructMembers(item['typename'], a:items, a:all)) 443a4a08388SBram Moolenaar continue 444a4a08388SBram Moolenaar endif 445a4a08388SBram Moolenaar 446f75a963eSBram Moolenaar " Only handle typedefs here. 447eb94e559SBram Moolenaar if item['kind'] != 't' 448eb94e559SBram Moolenaar continue 449eb94e559SBram Moolenaar endif 450eb94e559SBram Moolenaar 451eb94e559SBram Moolenaar " Skip matches local to another file. 452eb94e559SBram Moolenaar if has_key(item, 'static') && item['static'] && bufnr('%') != bufnr(item['filename']) 453f75a963eSBram Moolenaar continue 454f75a963eSBram Moolenaar endif 455f75a963eSBram Moolenaar 456f75a963eSBram Moolenaar " For old ctags we recognize "typedef struct aaa" and 457f75a963eSBram Moolenaar " "typedef union bbb" in the tags file command. 458eb94e559SBram Moolenaar let cmd = item['cmd'] 459f75a963eSBram Moolenaar let ei = matchend(cmd, 'typedef\s\+') 460f75a963eSBram Moolenaar if ei > 1 461f75a963eSBram Moolenaar let cmdtokens = split(strpart(cmd, ei), '\s\+\|\<') 462f75a963eSBram Moolenaar if len(cmdtokens) > 1 4638b2d9c43SBram Moolenaar if cmdtokens[0] == 'struct' || cmdtokens[0] == 'union' || cmdtokens[0] == 'class' 464f75a963eSBram Moolenaar let name = '' 465f75a963eSBram Moolenaar " Use the first identifier after the "struct" or "union" 466f75a963eSBram Moolenaar for ti in range(len(cmdtokens) - 1) 467f75a963eSBram Moolenaar if cmdtokens[ti] =~ '^\w' 468f75a963eSBram Moolenaar let name = cmdtokens[ti] 469f75a963eSBram Moolenaar break 470f75a963eSBram Moolenaar endif 471f75a963eSBram Moolenaar endfor 472f75a963eSBram Moolenaar if name != '' 4731f35bf9cSBram Moolenaar call extend(res, s:StructMembers(cmdtokens[0] . ':' . name, a:items, a:all)) 474f75a963eSBram Moolenaar endif 4750e5bd96fSBram Moolenaar elseif a:depth < 10 476f75a963eSBram Moolenaar " Could be "typedef other_T some_T". 4771f35bf9cSBram Moolenaar call extend(res, s:Nextitem(cmdtokens[0], a:items, a:depth + 1, a:all)) 478f75a963eSBram Moolenaar endif 479f75a963eSBram Moolenaar endif 480caa0fcfaSBram Moolenaar endif 481caa0fcfaSBram Moolenaar endfor 482a4a08388SBram Moolenaar if len(res) > 0 483caa0fcfaSBram Moolenaar break 484caa0fcfaSBram Moolenaar endif 485a4a08388SBram Moolenaar endfor 486dd2436f3SBram Moolenaar 487a4a08388SBram Moolenaar return res 488*4466ad6bSBram Moolenaarendfunc 489a4a08388SBram Moolenaar 490a4a08388SBram Moolenaar 4910e5bd96fSBram Moolenaar" Search for members of structure "typename" in tags files. 492f75a963eSBram Moolenaar" Return a list with resulting matches. 493f75a963eSBram Moolenaar" Each match is a dictionary with "match" and "tagline" entries. 4941f35bf9cSBram Moolenaar" When "all" is non-zero find all, otherwise just return 1 if there is any 4951f35bf9cSBram Moolenaar" member. 496*4466ad6bSBram Moolenaarfunc s:StructMembers(typename, items, all) 497a4a08388SBram Moolenaar " Todo: What about local structures? 498862c27a0SBram Moolenaar let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) 499a4a08388SBram Moolenaar if fnames == '' 500f75a963eSBram Moolenaar return [] 501a4a08388SBram Moolenaar endif 502a4a08388SBram Moolenaar 503a4a08388SBram Moolenaar let typename = a:typename 504a4a08388SBram Moolenaar let qflist = [] 5051056d988SBram Moolenaar let cached = 0 5061f35bf9cSBram Moolenaar if a:all == 0 5071f35bf9cSBram Moolenaar let n = '1' " stop at first found match 5081056d988SBram Moolenaar if has_key(s:grepCache, a:typename) 5091056d988SBram Moolenaar let qflist = s:grepCache[a:typename] 5101056d988SBram Moolenaar let cached = 1 5111056d988SBram Moolenaar endif 5121f35bf9cSBram Moolenaar else 5131f35bf9cSBram Moolenaar let n = '' 5141f35bf9cSBram Moolenaar endif 5151056d988SBram Moolenaar if !cached 516a4a08388SBram Moolenaar while 1 51730b65817SBram Moolenaar exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames 5181056d988SBram Moolenaar 519a4a08388SBram Moolenaar let qflist = getqflist() 520a4a08388SBram Moolenaar if len(qflist) > 0 || match(typename, "::") < 0 521a4a08388SBram Moolenaar break 522a4a08388SBram Moolenaar endif 523a4a08388SBram Moolenaar " No match for "struct:context::name", remove "context::" and try again. 524a4a08388SBram Moolenaar let typename = substitute(typename, ':[^:]*::', ':', '') 525a4a08388SBram Moolenaar endwhile 526a4a08388SBram Moolenaar 5271056d988SBram Moolenaar if a:all == 0 5281056d988SBram Moolenaar " Store the result to be able to use it again later. 5291056d988SBram Moolenaar let s:grepCache[a:typename] = qflist 5301056d988SBram Moolenaar endif 5311056d988SBram Moolenaar endif 5321056d988SBram Moolenaar 53320aac6c1SBram Moolenaar " Skip over [...] items 53420aac6c1SBram Moolenaar let idx = 0 53520aac6c1SBram Moolenaar while 1 53620aac6c1SBram Moolenaar if idx >= len(a:items) 53720aac6c1SBram Moolenaar let target = '' " No further items, matching all members 53820aac6c1SBram Moolenaar break 53920aac6c1SBram Moolenaar endif 54020aac6c1SBram Moolenaar if a:items[idx][0] != '[' 54120aac6c1SBram Moolenaar let target = a:items[idx] 54220aac6c1SBram Moolenaar break 54320aac6c1SBram Moolenaar endif 54420aac6c1SBram Moolenaar let idx += 1 54520aac6c1SBram Moolenaar endwhile 546eb94e559SBram Moolenaar " Put matching members in matches[]. 547f75a963eSBram Moolenaar let matches = [] 548a4a08388SBram Moolenaar for l in qflist 549a4a08388SBram Moolenaar let memb = matchstr(l['text'], '[^\t]*') 55020aac6c1SBram Moolenaar if memb =~ '^' . target 551eb94e559SBram Moolenaar " Skip matches local to another file. 552eb94e559SBram Moolenaar if match(l['text'], "\tfile:") < 0 || bufnr('%') == bufnr(matchstr(l['text'], '\t\zs[^\t]*')) 553eb94e559SBram Moolenaar let item = {'match': memb, 'tagline': l['text']} 554eb94e559SBram Moolenaar 555eb94e559SBram Moolenaar " Add the kind of item. 556eb94e559SBram Moolenaar let s = matchstr(l['text'], '\t\(kind:\)\=\zs\S\ze\(\t\|$\)') 557eb94e559SBram Moolenaar if s != '' 558eb94e559SBram Moolenaar let item['kind'] = s 559eb94e559SBram Moolenaar if s == 'f' 560eb94e559SBram Moolenaar let item['match'] = memb . '(' 561eb94e559SBram Moolenaar endif 562eb94e559SBram Moolenaar endif 563eb94e559SBram Moolenaar 564eb94e559SBram Moolenaar call add(matches, item) 565eb94e559SBram Moolenaar endif 566a4a08388SBram Moolenaar endif 567caa0fcfaSBram Moolenaar endfor 568dd2436f3SBram Moolenaar 569f75a963eSBram Moolenaar if len(matches) > 0 57020aac6c1SBram Moolenaar " Skip over next [...] items 57120aac6c1SBram Moolenaar let idx += 1 5720e5bd96fSBram Moolenaar while 1 5730e5bd96fSBram Moolenaar if idx >= len(a:items) 5740e5bd96fSBram Moolenaar return matches " No further items, return the result. 575caa0fcfaSBram Moolenaar endif 5760e5bd96fSBram Moolenaar if a:items[idx][0] != '[' 5770e5bd96fSBram Moolenaar break 5780e5bd96fSBram Moolenaar endif 5790e5bd96fSBram Moolenaar let idx += 1 5800e5bd96fSBram Moolenaar endwhile 581caa0fcfaSBram Moolenaar 582caa0fcfaSBram Moolenaar " More items following. For each of the possible members find the 583caa0fcfaSBram Moolenaar " matching following members. 5841f35bf9cSBram Moolenaar return s:SearchMembers(matches, a:items[idx :], a:all) 585dd2436f3SBram Moolenaar endif 586dd2436f3SBram Moolenaar 587caa0fcfaSBram Moolenaar " Failed to find anything. 588caa0fcfaSBram Moolenaar return [] 589*4466ad6bSBram Moolenaarendfunc 590f75a963eSBram Moolenaar 591f75a963eSBram Moolenaar" For matching members, find matches for following items. 5921f35bf9cSBram Moolenaar" When "all" is non-zero find all, otherwise just return 1 if there is any 5931f35bf9cSBram Moolenaar" member. 594*4466ad6bSBram Moolenaarfunc s:SearchMembers(matches, items, all) 595f75a963eSBram Moolenaar let res = [] 596f75a963eSBram Moolenaar for i in range(len(a:matches)) 597280f126eSBram Moolenaar let typename = '' 598280f126eSBram Moolenaar if has_key(a:matches[i], 'dict') 599280f126eSBram Moolenaar if has_key(a:matches[i].dict, 'typename') 600280f126eSBram Moolenaar let typename = a:matches[i].dict['typename'] 60176b92b28SBram Moolenaar elseif has_key(a:matches[i].dict, 'typeref') 60276b92b28SBram Moolenaar let typename = a:matches[i].dict['typeref'] 603280f126eSBram Moolenaar endif 604280f126eSBram Moolenaar let line = "\t" . a:matches[i].dict['cmd'] 605280f126eSBram Moolenaar else 606f75a963eSBram Moolenaar let line = a:matches[i]['tagline'] 607f75a963eSBram Moolenaar let e = matchend(line, '\ttypename:') 60876b92b28SBram Moolenaar if e < 0 60976b92b28SBram Moolenaar let e = matchend(line, '\ttyperef:') 61076b92b28SBram Moolenaar endif 611f75a963eSBram Moolenaar if e > 0 612f75a963eSBram Moolenaar " Use typename field 613280f126eSBram Moolenaar let typename = matchstr(line, '[^\t]*', e) 614280f126eSBram Moolenaar endif 615280f126eSBram Moolenaar endif 6161f35bf9cSBram Moolenaar 617280f126eSBram Moolenaar if typename != '' 6181f35bf9cSBram Moolenaar call extend(res, s:StructMembers(typename, a:items, a:all)) 619f75a963eSBram Moolenaar else 620f75a963eSBram Moolenaar " Use the search command (the declaration itself). 621f75a963eSBram Moolenaar let s = match(line, '\t\zs/^') 622f75a963eSBram Moolenaar if s > 0 623280f126eSBram Moolenaar let e = match(line, '\<' . a:matches[i]['match'] . '\>', s) 624f75a963eSBram Moolenaar if e > 0 6251f35bf9cSBram Moolenaar call extend(res, s:Nextitem(strpart(line, s, e - s), a:items, 0, a:all)) 626f75a963eSBram Moolenaar endif 627f75a963eSBram Moolenaar endif 628f75a963eSBram Moolenaar endif 6291f35bf9cSBram Moolenaar if a:all == 0 && len(res) > 0 6301f35bf9cSBram Moolenaar break 6311f35bf9cSBram Moolenaar endif 632f75a963eSBram Moolenaar endfor 633f75a963eSBram Moolenaar return res 634f75a963eSBram Moolenaarendfunc 635b6b046b2SBram Moolenaar 636b6b046b2SBram Moolenaarlet &cpo = s:cpo_save 637b6b046b2SBram Moolenaarunlet s:cpo_save 638d1caa941SBram Moolenaar 639d1caa941SBram Moolenaar" vim: noet sw=2 sts=2 640