3 # Functions for dealing with symbols.
4 # - LSP symbol menu and for searching symbols across the workspace.
6 # - jump to a symbol definition, declaration, type definition or
9 import './options.vim' as opt
11 import './outline.vim'
13 # Initialize the highlight group and the text property type used for
14 # document symbol search
16 # Use a high priority value to override other highlights in the line
17 hlset([{name: 'LspSymbolName', default: true, linksto: 'Search'}])
18 prop_type_add('LspSymbolNameProp', {highlight: 'LspSymbolName',
22 hlset([{name: 'LspSymbolRange', default: true, linksto: 'Visual'}])
23 prop_type_add('LspSymbolRangeProp', {highlight: 'LspSymbolRange',
29 # Handle keys pressed when the workspace symbol popup menu is displayed
30 def FilterSymbols(lspserver: dict<any>, popupID: number, key: string): bool
31 var key_handled: bool = false
32 var update_popup: bool = false
33 var query: string = lspserver.workspaceSymbolQuery
35 if key == "\<BS>" || key == "\<C-H>"
36 # Erase one character from the filter text
42 elseif key == "\<C-U>"
43 # clear the filter text
47 elseif key == "\<C-F>"
50 || key == "\<PageDown>"
55 # scroll the popup window
56 var cmd: string = 'normal! ' .. (key == "\<C-N>" ? 'j' : key == "\<C-P>" ? 'k' : key)
57 win_execute(popupID, cmd)
59 elseif key == "\<Up>" || key == "\<Down>"
60 # Use native Vim handling for these keys
62 elseif key =~ '^\f$' || key == "\<Space>"
63 # Filter the names based on the typed key and keys typed before
70 # Update the popup with the new list of symbol names
71 popupID->popup_settext('')
73 lspserver.workspaceQuery(query, false)
75 []->setwinvar(popupID, 'LspSymbolTable')
77 :echo $'Symbol: {query}'
80 # Update the workspace symbol query string
81 lspserver.workspaceSymbolQuery = query
87 return popupID->popup_filter_menu(key)
90 # Jump to the location of a symbol selected in the popup menu
91 def JumpToWorkspaceSymbol(cmdmods: string, popupID: number, result: number): void
92 # clear the message displayed at the command-line
100 var symTbl: list<dict<any>> = popupID->getwinvar('LspSymbolTable', [])
105 # Save the current location in the tag stack
106 util.PushCursorToTagStack()
108 # if the selected file is already present in a window, then jump to it
109 var fname: string = symTbl[result - 1].file
110 var bnr = fname->bufnr()
112 var winList: list<number> = bnr->win_findbuf()
114 # Not present in any window
115 if &modified || &buftype != ''
116 # the current buffer is modified or is not a normal buffer, then
117 # open the file in a new window
118 exe $'split {symTbl[result - 1].file}'
120 exe $'confirm edit {symTbl[result - 1].file}'
123 # If the target buffer is opened in the current window, then don't
126 # If the target buffer is opened in a window in the current tab
128 var winID = fname->bufwinid()
130 # not present in the current tab page. Use the first window.
137 exe $'{cmdmods} split {symTbl[result - 1].file}'
139 # Set the previous cursor location mark. Instead of using setpos(), m' is
140 # used so that the current location is added to the jump list.
142 setcursorcharpos(symTbl[result - 1].pos.line + 1,
143 util.GetCharIdxWithoutCompChar(bufnr(),
144 symTbl[result - 1].pos) + 1)
150 # display a list of symbols from the workspace
151 def ShowSymbolMenu(lspserver: dict<any>, query: string, cmdmods: string)
152 # Create the popup menu
153 var lnum = &lines - &cmdheight - 2 - 10
155 title: 'Workspace Symbol Search',
167 filter: function(FilterSymbols, [lspserver]),
168 callback: function('JumpToWorkspaceSymbol', [cmdmods])
170 lspserver.workspaceSymbolPopup = popup_menu([], popupAttr)
171 lspserver.workspaceSymbolQuery = query
172 prop_type_add('lspworkspacesymbol',
173 {bufnr: lspserver.workspaceSymbolPopup->winbufnr(),
175 :echo $'Symbol: {query}'
178 # Convert a file name to <filename> (<dirname>) format.
179 # Make sure the popup doesn't occupy the entire screen by reducing the width.
180 def MakeMenuName(popupWidth: number, fname: string): string
181 var filename: string = fname->fnamemodify(':t')
182 var flen: number = filename->len()
183 var dirname: string = fname->fnamemodify(':h')
185 if fname->len() > popupWidth && flen < popupWidth
186 # keep the full file name and reduce directory name length
187 # keep some characters at the beginning and end (equally).
188 # 6 spaces are used for "..." and " ()"
189 var dirsz = (popupWidth - flen - 6) / 2
190 dirname = dirname[: dirsz] .. '...' .. dirname[-dirsz : ]
192 var str: string = filename
194 str ..= $' ({dirname}/)'
199 # process the 'workspace/symbol' reply from the LSP server
200 # Result: SymbolInformation[] | null
201 export def WorkspaceSymbolPopup(lspserver: dict<any>, query: string,
202 symInfo: list<dict<any>>, cmdmods: string)
203 var symbols: list<dict<any>> = []
204 var symbolType: string
208 # Create a symbol popup menu if it is not present
209 if lspserver.workspaceSymbolPopup->winbufnr() == -1
210 ShowSymbolMenu(lspserver, query, cmdmods)
213 for symbol in symInfo
214 if !symbol->has_key('location')
215 # ignore entries without location information
219 # interface SymbolInformation
220 fileName = util.LspUriToFile(symbol.location.uri)
222 symName = symbol.name
223 if symbol->has_key('containerName') && symbol.containerName != ''
224 symName = $'{symbol.containerName}::{symName}'
226 symName ..= $' [{SymbolKindToName(symbol.kind)}]'
227 symName ..= ' ' .. MakeMenuName(
228 lspserver.workspaceSymbolPopup->popup_getpos().core_width,
231 symbols->add({name: symName,
233 pos: symbol.location.range.start})
235 symbols->setwinvar(lspserver.workspaceSymbolPopup, 'LspSymbolTable')
236 lspserver.workspaceSymbolPopup->popup_settext(
237 symbols->copy()->mapnew('v:val.name'))
240 # map the LSP symbol kind number to string
241 export def SymbolKindToName(symkind: number): string
242 var symbolMap: list<string> = [
274 return symbolMap[symkind]
277 def UpdatePeekFilePopup(lspserver: dict<any>, locations: list<dict<any>>)
278 if lspserver.peekSymbolPopup->winbufnr() == -1
282 lspserver.peekSymbolFilePopup->popup_close()
284 var n = line('.', lspserver.peekSymbolPopup) - 1
285 var [uri, range] = util.LspLocationParse(locations[n])
286 var fname: string = util.LspUriToFile(uri)
288 var bnr: number = fname->bufnr()
290 bnr = fname->bufadd()
294 title: $"{fname->fnamemodify(':t')} ({fname->fnamemodify(':h')})",
299 minwidth: winwidth(0) - 38,
300 maxwidth: winwidth(0) - 38,
308 lspserver.peekSymbolFilePopup = popup_create(bnr, popupAttrs)
309 var cmds =<< trim eval END
311 [{range.start.line + 1}, 1]->cursor()
314 win_execute(lspserver.peekSymbolFilePopup, cmds)
316 lspserver.peekSymbolFilePopup->clearmatches()
317 var start_col = util.GetLineByteFromPos(bnr, range.start) + 1
318 var end_col = util.GetLineByteFromPos(bnr, range.end)
319 var pos = [[range.start.line + 1,
320 start_col, end_col - start_col + 1]]
321 matchaddpos('Search', pos, 10, -1, {window: lspserver.peekSymbolFilePopup})
324 def LocPopupFilter(lspserver: dict<any>, locations: list<dict<any>>,
325 popup_id: number, key: string): bool
326 popup_filter_menu(popup_id, key)
327 if lspserver.peekSymbolPopup->winbufnr() == -1
328 if lspserver.peekSymbolFilePopup->winbufnr() != -1
329 lspserver.peekSymbolFilePopup->popup_close()
331 lspserver.peekSymbolPopup = -1
332 lspserver.peekSymbolFilePopup = -1
334 UpdatePeekFilePopup(lspserver, locations)
339 def LocPopupCallback(lspserver: dict<any>, locations: list<dict<any>>,
340 popup_id: number, selIdx: number)
341 if lspserver.peekSymbolFilePopup->winbufnr() != -1
342 lspserver.peekSymbolFilePopup->popup_close()
344 lspserver.peekSymbolPopup = -1
346 util.PushCursorToTagStack()
347 util.JumpToLspLocation(locations[selIdx - 1], '')
351 # Display the locations in a popup menu. Display the corresponding file in
352 # an another popup window.
353 def PeekLocations(lspserver: dict<any>, locations: list<dict<any>>,
355 if lspserver.peekSymbolPopup->winbufnr() != -1
356 # If the symbol popup window is already present, close it.
357 lspserver.peekSymbolPopup->popup_close()
360 var w: number = &columns
361 var fnamelen = float2nr(w * 0.4)
363 var menuItems: list<string> = []
365 var [uri, range] = util.LspLocationParse(loc)
366 var fname: string = util.LspUriToFile(uri)
367 var bnr: number = fname->bufnr()
369 bnr = fname->bufadd()
371 :silent! bnr->bufload()
373 var lnum = range.start.line + 1
374 var text: string = bnr->getbufline(lnum)->get(0, '')
375 menuItems->add($'{lnum}: {text}')
383 col: winwidth(0) - 34,
390 filter: function(LocPopupFilter, [lspserver, locations]),
391 callback: function(LocPopupCallback, [lspserver, locations])
393 lspserver.peekSymbolPopup = popup_menu(menuItems, popupAttrs)
394 UpdatePeekFilePopup(lspserver, locations)
397 export def ShowLocations(lspserver: dict<any>, locations: list<dict<any>>,
398 peekSymbol: bool, title: string)
400 PeekLocations(lspserver, locations, title)
404 # create a loclist the location of the locations
405 var qflist: list<dict<any>> = []
407 var [uri, range] = util.LspLocationParse(loc)
408 var fname: string = util.LspUriToFile(uri)
409 var bnr: number = fname->bufnr()
411 bnr = fname->bufadd()
413 :silent! bnr->bufload()
414 var text: string = bnr->getbufline(range.start.line + 1)->get(0, '')->trim("\t ", 1)
415 qflist->add({filename: fname,
416 lnum: range.start.line + 1,
417 col: util.GetLineByteFromPos(bnr, range.start) + 1,
421 var save_winid = win_getid()
423 if opt.lspOptions.useQuickfixForLocations
424 setqflist([], ' ', {title: title, items: qflist})
425 var mods: string = ''
428 setloclist(0, [], ' ', {title: title, items: qflist})
429 var mods: string = ''
433 if !opt.lspOptions.keepFocusInReferences
434 save_winid->win_gotoid()
438 # Key filter callback function used for the symbol popup window.
439 # Vim doesn't close the popup window when the escape key is pressed.
440 # This is function supports that.
441 def SymbolFilterCB(lspserver: dict<any>, id: number, key: string): bool
443 lspserver.peekSymbolPopup->popup_close()
450 # Display the file specified by LSP "LocationLink" in a popup window and
451 # highlight the range in "location".
452 def PeekSymbolLocation(lspserver: dict<any>, location: dict<any>)
453 var [uri, range] = util.LspLocationParse(location)
454 var fname = util.LspUriToFile(uri)
455 var bnum = fname->bufadd()
457 # Failed to create or find a buffer
460 :silent! bnum->bufload()
462 if lspserver.peekSymbolPopup->winbufnr() != -1
463 # If the symbol popup window is already present, close it.
464 lspserver.peekSymbolPopup->popup_close()
466 var CbFunc = function(SymbolFilterCB, [lspserver])
468 title: $"{fnamemodify(fname, ':t')} ({fnamemodify(fname, ':h')})",
480 lspserver.peekSymbolPopup = popup_atcursor(bnum, popupAttrs)
482 # Highlight the symbol name and center the line in the popup
483 var pwid = lspserver.peekSymbolPopup
484 var pwbuf = pwid->winbufnr()
485 var pos: list<number> = []
486 var start_col: number
488 start_col = util.GetLineByteFromPos(pwbuf, range.start) + 1
489 end_col = util.GetLineByteFromPos(pwbuf, range.end) + 1
490 pos->add(range.start.line + 1)
491 pos->extend([start_col, end_col - start_col])
492 matchaddpos('Search', [pos], 10, 101, {window: pwid})
493 var cmds =<< trim eval END
494 [{range.start.line + 1}, 1]->cursor()
497 win_execute(pwid, cmds, 'silent!')
500 # Jump to the definition, declaration or implementation of a symbol.
501 # Also, used to peek at the definition, declaration or implementation of a
503 export def GotoSymbol(lspserver: dict<any>, location: dict<any>,
504 peekSymbol: bool, cmdmods: string)
506 PeekSymbolLocation(lspserver, location)
508 # Save the current cursor location in the tag stack.
509 util.PushCursorToTagStack()
510 util.JumpToLspLocation(location, cmdmods)
514 # Process the LSP server reply message for a 'textDocument/definition' request
515 # and return a list of Dicts in a format accepted by the 'tagfunc' option.
516 export def TagFunc(lspserver: dict<any>,
517 taglocations: list<dict<any>>,
518 pat: string): list<dict<any>>
519 var retval: list<dict<any>>
521 for tagloc in taglocations
525 var [uri, range] = util.LspLocationParse(tagloc)
526 tagitem.filename = util.LspUriToFile(uri)
527 var bnr = util.LspUriToBufnr(uri)
528 var startByteIdx = util.GetLineByteFromPos(bnr, range.start)
529 tagitem.cmd = $"/\\%{range.start.line + 1}l\\%{startByteIdx + 1}c"
537 # process SymbolInformation[]
538 def ProcessSymbolInfoTable(lspserver: dict<any>,
540 symbolInfoTable: list<dict<any>>,
541 symbolTypeTable: dict<list<dict<any>>>,
542 symbolLineTable: list<dict<any>>)
544 var symbolType: string
546 var r: dict<dict<number>>
547 var symInfo: dict<any>
549 for syminfo in symbolInfoTable
550 fname = util.LspUriToFile(syminfo.location.uri)
551 symbolType = SymbolKindToName(syminfo.kind)
553 if syminfo->has_key('containerName')
554 if syminfo.containerName != ''
555 name ..= $' [{syminfo.containerName}]'
558 r = syminfo.location.range
559 lspserver.decodeRange(bnr, r)
561 if !symbolTypeTable->has_key(symbolType)
562 symbolTypeTable[symbolType] = []
564 symInfo = {name: name, range: r}
565 symbolTypeTable[symbolType]->add(symInfo)
566 symbolLineTable->add(symInfo)
570 # process DocumentSymbol[]
571 def ProcessDocSymbolTable(lspserver: dict<any>,
573 docSymbolTable: list<dict<any>>,
574 symbolTypeTable: dict<list<dict<any>>>,
575 symbolLineTable: list<dict<any>>)
576 var symbolType: string
578 var r: dict<dict<number>>
579 var symInfo: dict<any>
580 var symbolDetail: string
581 var childSymbols: dict<list<dict<any>>>
583 for syminfo in docSymbolTable
585 symbolType = SymbolKindToName(syminfo.kind)
586 r = syminfo.selectionRange
587 lspserver.decodeRange(bnr, r)
588 if syminfo->has_key('detail')
589 symbolDetail = syminfo.detail
591 if !symbolTypeTable->has_key(symbolType)
592 symbolTypeTable[symbolType] = []
595 if syminfo->has_key('children')
596 ProcessDocSymbolTable(lspserver, bnr, syminfo.children, childSymbols,
599 symInfo = {name: name, range: r, detail: symbolDetail,
600 children: childSymbols}
601 symbolTypeTable[symbolType]->add(symInfo)
602 symbolLineTable->add(symInfo)
606 # process the 'textDocument/documentSymbol' reply from the LSP server
607 # Open a symbols window and display the symbols as a tree
608 # Result: DocumentSymbol[] | SymbolInformation[] | null
609 export def DocSymbolOutline(lspserver: dict<any>, docSymbol: any, fname: string)
610 var bnr = fname->bufnr()
611 var symbolTypeTable: dict<list<dict<any>>> = {}
612 var symbolLineTable: list<dict<any>> = []
614 if docSymbol->empty()
615 # No symbols defined for this file. Clear the outline window.
616 outline.UpdateOutlineWindow(fname, symbolTypeTable, symbolLineTable)
620 if docSymbol[0]->has_key('location')
621 # SymbolInformation[]
622 ProcessSymbolInfoTable(lspserver, bnr, docSymbol, symbolTypeTable,
626 ProcessDocSymbolTable(lspserver, bnr, docSymbol, symbolTypeTable,
630 # sort the symbols by line number
631 symbolLineTable->sort((a, b) => a.range.start.line - b.range.start.line)
632 outline.UpdateOutlineWindow(fname, symbolTypeTable, symbolLineTable)
635 # Process the list of symbols (LSP interface "SymbolInformation") in
636 # "symbolInfoTable". For each symbol, create the name to display in the popup
637 # menu along with the symbol range and return the List.
638 def GetSymbolsInfoTable(lspserver: dict<any>,
640 symbolInfoTable: list<dict<any>>): list<dict<any>>
641 var symbolTable: list<dict<any>> = []
642 var symbolType: string
644 var containerName: string
645 var r: dict<dict<number>>
647 for syminfo in symbolInfoTable
648 symbolType = SymbolKindToName(syminfo.kind)
649 name = $'{symbolType} : {syminfo.name}'
650 if syminfo->has_key('containerName') && !syminfo.containerName->empty()
651 name ..= $' [{syminfo.containerName}]'
653 r = syminfo.location.range
654 lspserver.decodeRange(bnr, r)
656 symbolTable->add({name: name, range: r, selectionRange: {}})
662 # Process the list of symbols (LSP interface "DocumentSymbol") in
663 # "docSymbolTable". For each symbol, create the name to display in the popup
664 # menu along with the symbol range and return the List in "symbolTable"
665 def GetSymbolsDocSymbol(lspserver: dict<any>,
667 docSymbolTable: list<dict<any>>,
668 symbolTable: list<dict<any>>,
669 parentName: string = '')
670 var symbolType: string
672 var r: dict<dict<number>>
673 var sr: dict<dict<number>>
674 var symInfo: dict<any>
676 for syminfo in docSymbolTable
677 var symName = syminfo.name
678 symbolType = SymbolKindToName(syminfo.kind)->tolower()
679 sr = syminfo.selectionRange
680 lspserver.decodeRange(bnr, sr)
682 lspserver.decodeRange(bnr, r)
683 name = $'{symbolType} : {symName}'
685 name ..= $' [{parentName}]'
687 # TODO: Should include syminfo.detail? Will it clutter the menu?
688 symInfo = {name: name, range: r, selectionRange: sr}
689 symbolTable->add(symInfo)
691 if syminfo->has_key('children')
692 # Process all the child symbols
693 GetSymbolsDocSymbol(lspserver, bnr, syminfo.children, symbolTable,
699 # Highlight the name and the range of lines for the symbol at symTbl[symIdx]
700 def SymbolHighlight(symTbl: list<dict<any>>, symIdx: number)
701 prop_remove({type: 'LspSymbolNameProp', all: true})
702 prop_remove({type: 'LspSymbolRangeProp', all: true})
707 var r = symTbl[symIdx].range
711 var rangeStart = r.start
713 var start_lnum = rangeStart.line + 1
714 var start_col = rangeStart.character + 1
715 var end_lnum = rangeEnd.line + 1
717 var last_lnum = line('$')
718 if end_lnum > line('$')
720 end_col = col([last_lnum, '$'])
722 end_col = rangeEnd.character + 1
724 prop_add(start_lnum, start_col,
725 {type: 'LspSymbolRangeProp',
728 cursor(start_lnum, 1)
731 var sr = symTbl[symIdx].selectionRange
735 rangeStart = sr.start
737 prop_add(rangeStart.line + 1, 1,
738 {type: 'LspSymbolNameProp',
739 start_col: rangeStart.character + 1,
740 end_lnum: rangeEnd.line + 1,
741 end_col: rangeEnd.character + 1})
744 # Callback invoked when an item is selected in the symbol popup menu
745 # "symTbl" - list of symbols
746 # "symInputPopup" - Symbol search input popup window ID
747 # "save_curpos" - Cursor position before invoking the symbol search. If the
748 # symbol search is canceled, restore the cursor to this
750 def SymbolMenuItemSelected(symPopupMenu: number,
752 var symTblFiltered = symPopupMenu->getwinvar('symbolTableFiltered', [])
753 var symInputPopup = symPopupMenu->getwinvar('symbolInputPopup', 0)
754 var save_curpos = symPopupMenu->getwinvar('saveCurPos', [])
756 # Restore the cursor to the location where the command was invoked
757 setpos('.', save_curpos)
760 # A symbol is selected in the popup menu
762 # Set the previous cursor location mark. Instead of using setpos(), m' is
763 # used so that the current location is added to the jump list.
766 # Jump to the selected symbol location
767 var r = symTblFiltered[result - 1].selectionRange
768 setcursorcharpos(r.start.line + 1,
769 util.GetCharIdxWithoutCompChar(bufnr(), r.start) + 1)
771 symInputPopup->popup_close()
772 prop_remove({type: 'LspSymbolNameProp', all: true})
773 prop_remove({type: 'LspSymbolRangeProp', all: true})
776 # Key filter function for the symbol popup menu.
777 def SymbolMenuFilterKey(symPopupMenu: number,
779 var keyHandled = false
780 var updateInputPopup = false
781 var inputText = symPopupMenu->getwinvar('inputText', '')
782 var symInputPopup = symPopupMenu->getwinvar('symbolInputPopup', 0)
784 if key == "\<BS>" || key == "\<C-H>"
785 # Erase a character in the input popup
786 if inputText->len() >= 1
787 inputText = inputText[: -2]
789 updateInputPopup = true
791 elseif key == "\<C-U>"
792 # Erase all the characters in the input popup
795 updateInputPopup = true
796 elseif key == "\<C-F>"
798 || key == "\<PageUp>"
799 || key == "\<PageDown>"
800 || key == "\<C-Home>"
804 # scroll the symbol popup window
805 var cmd: string = 'normal! ' .. (key == "\<C-N>" ? 'j' :
806 key == "\<C-P>" ? 'k' : key)
807 win_execute(symPopupMenu, cmd)
810 # A keyword character is typed. Add to the input text and update the
814 updateInputPopup = true
817 var symTblFiltered: list<dict<any>> = []
818 symTblFiltered = symPopupMenu->getwinvar('symbolTableFiltered', [])
821 # Update the input popup with the new text and update the symbol popup
822 # window with the matching symbol names.
823 symInputPopup->popup_settext(inputText)
825 var symbolTable = symPopupMenu->getwinvar('symbolTable')
826 symTblFiltered = symbolTable->deepcopy()
827 var symbolMatchPos: list<list<number>> = []
829 # Get the list of symbols fuzzy matching the entered text
831 var t = symTblFiltered->matchfuzzypos(inputText, {key: 'name'})
832 symTblFiltered = t[0]
833 symbolMatchPos = t[1]
836 var popupText: list<dict<any>>
837 var text: list<dict<any>>
838 if !symbolMatchPos->empty()
839 # Generate a list of symbol names and the corresponding text properties
840 # to highlight the matching characters.
841 popupText = symTblFiltered->mapnew((idx, val): dict<any> => ({
843 props: symbolMatchPos[idx]->mapnew((_, w: number): dict<any> => ({
846 type: 'LspSymbolMatch'}
850 popupText = symTblFiltered->mapnew((idx, val): dict<string> => {
851 return {text: val.name}
854 symPopupMenu->popup_settext(popupText)
856 # Select the first symbol and highlight the corresponding text range
857 win_execute(symPopupMenu, 'cursor(1, 1)')
858 SymbolHighlight(symTblFiltered, 0)
861 # Save the filtered symbol table and the search text in popup window
863 setwinvar(symPopupMenu, 'inputText', inputText)
864 setwinvar(symPopupMenu, 'symbolTableFiltered', symTblFiltered)
867 # Use the default handler for the key
868 symPopupMenu->popup_filter_menu(key)
871 # Highlight the name and range of the selected symbol
872 var lnum = line('.', symPopupMenu) - 1
874 SymbolHighlight(symTblFiltered, lnum)
880 # Display the symbols popup menu
881 def SymbolPopupMenu(symbolTable: list<dict<any>>)
882 var curLine = line('.')
885 # Get the names of all the symbols. Also get the index of the symbol under
887 var symNames = symbolTable->mapnew((idx, val): string => {
889 if !r->empty() && curSymIdx == 0
890 if curLine >= r.start.line + 1 && curLine <= r.end.line + 1
897 var symInputPopupAttr = {
898 title: 'Select Symbol',
911 var symInputPopup = popup_create('', symInputPopupAttr)
913 var symNamesPopupattr = {
923 border: [0, 0, 0, 0],
924 callback: SymbolMenuItemSelected,
925 filter: SymbolMenuFilterKey,
927 var symPopupMenu = popup_menu(symNames, symNamesPopupattr)
929 # Save the state in the popup menu window variables
930 setwinvar(symPopupMenu, 'symbolTable', symbolTable)
931 setwinvar(symPopupMenu, 'symbolTableFiltered', symbolTable->deepcopy())
932 setwinvar(symPopupMenu, 'symbolInputPopup', symInputPopup)
933 setwinvar(symPopupMenu, 'saveCurPos', getcurpos())
934 prop_type_add('LspSymbolMatch', {bufnr: symPopupMenu->winbufnr(),
938 # Start with the symbol under the cursor
939 var cmds =<< trim eval END
940 [{curSymIdx + 1}, 1]->cursor()
943 win_execute(symPopupMenu, cmds, 'silent!')
945 # Highlight the name and range of the first symbol
946 SymbolHighlight(symbolTable, curSymIdx)
949 # process the 'textDocument/documentSymbol' reply from the LSP server
950 # Result: DocumentSymbol[] | SymbolInformation[] | null
951 # Display the symbols in a popup window and jump to the selected symbol
952 export def DocSymbolPopup(lspserver: dict<any>, docSymbol: any, fname: string)
953 var symList: list<dict<any>> = []
955 if docSymbol->empty()
959 var bnr = fname->bufnr()
961 if docSymbol[0]->has_key('location')
962 # SymbolInformation[]
963 symList = GetSymbolsInfoTable(lspserver, bnr, docSymbol)
966 GetSymbolsDocSymbol(lspserver, bnr, docSymbol, symList)
970 SymbolPopupMenu(symList)
973 # vim: tabstop=8 shiftwidth=2 softtabstop=2