From 5a510fd65e130cf7dece549abf360896638270c0 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 20 Nov 2022 13:36:53 -0800 Subject: [PATCH] Use a popup window instead of the preview window to display the symbol references --- autoload/lsp/lspserver.vim | 1 + autoload/lsp/symbol.vim | 155 ++++++++++++++++++++++++++++++--- autoload/lsp/typehierarchy.vim | 11 +-- doc/lsp.txt | 13 +-- test/unit_tests.vim | 11 ++- 5 files changed, 164 insertions(+), 27 deletions(-) diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index 67ceb59..1319a95 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -1543,6 +1543,7 @@ export def NewLspServer(path: string, args: list, isSync: bool, initiali workspaceSymbolPopup: -1, workspaceSymbolQuery: '', peekSymbolPopup: -1, + peekSymbolFilePopup: -1, callHierarchyType: '', selection: {} } diff --git a/autoload/lsp/symbol.vim b/autoload/lsp/symbol.vim index 41d0b4d..5562d8a 100644 --- a/autoload/lsp/symbol.vim +++ b/autoload/lsp/symbol.vim @@ -128,7 +128,7 @@ def ShowSymbolMenu(lspserver: dict, query: string) var lnum = &lines - &cmdheight - 2 - 10 var popupAttr = { title: 'Workspace Symbol Search', - wrap: 0, + wrap: false, pos: 'topleft', line: lnum, col: 2, @@ -227,8 +227,132 @@ export def SymbolKindToName(symkind: number): string return symbolMap[symkind] enddef +def UpdatePeekFilePopup(lspserver: dict, refs: list>) + if lspserver.peekSymbolPopup->winbufnr() == -1 + return + endif + + lspserver.peekSymbolFilePopup->popup_close() + + var n = line('.', lspserver.peekSymbolPopup) - 1 + var fname: string = util.LspUriToFile(refs[n].uri) + + var bnr: number = fname->bufnr() + if bnr == -1 + bnr = fname->bufadd() + endif + + var popupAttrs = { + title: $"{fname->fnamemodify(':t')} ({fname->fnamemodify(':h')})", + wrap: false, + fixed: true, + minheight: 10, + maxheight: 10, + minwidth: winwidth(0) - 38, + maxwidth: winwidth(0) - 38, + cursorline: true, + border: [], + line: 'cursor+1', + col: 1 + } + + lspserver.peekSymbolFilePopup = popup_create(bnr, popupAttrs) + var cmds =<< trim eval END + [{refs[n].range.start.line + 1}, 1]->cursor() + normal! z. + END + win_execute(lspserver.peekSymbolFilePopup, cmds) + + lspserver.peekSymbolFilePopup->clearmatches() + var start_col = util.GetLineByteFromPos(bnr, + refs[n].range.start) + 1 + var end_col = util.GetLineByteFromPos(bnr, refs[n].range.end) + var pos = [[refs[n].range.start.line + 1, + start_col, end_col - start_col + 1]] + matchaddpos('Search', pos, 10, -1, {window: lspserver.peekSymbolFilePopup}) +enddef + +def RefPopupFilter(lspserver: dict, refs: list>, + popup_id: number, key: string): bool + popup_filter_menu(popup_id, key) + if lspserver.peekSymbolPopup->winbufnr() == -1 + if lspserver.peekSymbolFilePopup->winbufnr() != -1 + lspserver.peekSymbolFilePopup->popup_close() + endif + lspserver.peekSymbolPopup = -1 + lspserver.peekSymbolFilePopup = -1 + else + UpdatePeekFilePopup(lspserver, refs) + endif + return true +enddef + +def RefPopupCallback(lspserver: dict, refs: list>, + popup_id: number, selIdx: number) + if lspserver.peekSymbolFilePopup->winbufnr() != -1 + lspserver.peekSymbolFilePopup->popup_close() + endif + lspserver.peekSymbolPopup = -1 + if selIdx != -1 + var fname: string = util.LspUriToFile(refs[selIdx - 1].uri) + util.PushCursorToTagStack() + util.JumpToLspLocation(refs[selIdx - 1], '') + endif +enddef + +# Display the references in a popup menu. Display the corresponding file in +# an another popup window. +def PeekReferences(lspserver: dict, refs: list>) + if lspserver.peekSymbolPopup->winbufnr() != -1 + # If the symbol popup window is already present, close it. + lspserver.peekSymbolPopup->popup_close() + endif + + var w: number = &columns + var fnamelen = float2nr(w * 0.4) + + var menuItems: list = [] + for loc in refs + var fname: string = util.LspUriToFile(loc.uri) + var bnr: number = fname->bufnr() + if bnr == -1 + bnr = fname->bufadd() + endif + if !bnr->bufloaded() + bnr->bufload() + endif + + var text: string = bnr->getbufline(loc.range.start.line + 1)[0] + var lnum = loc.range.start.line + 1 + menuItems->add($'{lnum}: {text}') + endfor + + var popupAttrs = { + title: 'References', + wrap: false, + pos: 'topleft', + line: 'cursor+1', + col: winwidth(0) - 34, + minheight: 10, + maxheight: 10, + minwidth: 30, + maxwidth: 30, + mapping: false, + fixed: true, + filter: function(RefPopupFilter, [lspserver, refs]), + callback: function(RefPopupCallback, [lspserver, refs]) + } + lspserver.peekSymbolPopup = popup_menu(menuItems, popupAttrs) + UpdatePeekFilePopup(lspserver, refs) +enddef + # Display or peek symbol references in a location list export def ShowReferences(lspserver: dict, refs: list>, peekSymbol: bool) + if peekSymbol + PeekReferences(lspserver, refs) + return + endif + # create a location list with the location of the references var qflist: list> = [] for loc in refs @@ -249,24 +373,26 @@ export def ShowReferences(lspserver: dict, refs: list>, peekSymbo endfor var save_winid = win_getid() - if peekSymbol - silent! pedit - wincmd P - endif setloclist(0, [], ' ', {title: 'Symbol Reference', items: qflist}) var mods: string = '' - if peekSymbol - # When peeking the references, open the location list in a vertically - # split window to the right and make the location list window 30% of the - # source window width - mods = $'belowright vert :{(winwidth(0) * 30) / 100}' - endif exe $'{mods} lopen' if !opt.lspOptions.keepFocusInReferences save_winid->win_gotoid() endif enddef +# Key filter callback function used for the symbol popup window. +# Vim doesn't close the popup window when the escape key is pressed. +# This is function supports that. +def SymbolFilterCB(lspserver: dict, id: number, key: string): bool + if key == "\" + lspserver.peekSymbolPopup->popup_close() + return true + endif + + return false +enddef + # Display the file specified by LSP 'location' in a popup window and highlight # the range in 'location'. def PeekSymbolLocation(lspserver: dict, location: dict) @@ -283,14 +409,17 @@ def PeekSymbolLocation(lspserver: dict, location: dict) lspserver.peekSymbolPopup->popup_close() endif var ptitle = $"{fnamemodify(fname, ':t')} ({fnamemodify(fname, ':h')})" - lspserver.peekSymbolPopup = popup_atcursor(bnum, {moved: 'any', + var CbFunc = function(SymbolFilterCB, [lspserver]) + lspserver.peekSymbolPopup = popup_atcursor(bnum, { + moved: 'any', title: ptitle, minwidth: 10, maxwidth: 60, minheight: 10, maxheight: 10, mapping: false, - wrap: false}) + wrap: false, + filter: CbFunc}) # Highlight the symbol name and center the line in the popup var pwid = lspserver.peekSymbolPopup diff --git a/autoload/lsp/typehierarchy.vim b/autoload/lsp/typehierarchy.vim index faedaae..510a446 100644 --- a/autoload/lsp/typehierarchy.vim +++ b/autoload/lsp/typehierarchy.vim @@ -71,7 +71,7 @@ def UpdateTypeHierFileInPopup(lspserver: dict, typeUriMap: list>) return endif - var popupAttr = { + var popupAttrs = { title: $"{fname->fnamemodify(':t')} ({fname->fnamemodify(':h')})", wrap: false, fixed: true, @@ -84,12 +84,13 @@ def UpdateTypeHierFileInPopup(lspserver: dict, typeUriMap: list>) line: 'cursor+1', col: 1 } - lspserver.typeHierFilePopup = popup_create(bnr, popupAttr) + lspserver.typeHierFilePopup = popup_create(bnr, popupAttrs) var cmds =<< trim eval END [{typeUriMap[n].range.start.line + 1}, 1]->cursor() normal! z. END win_execute(lspserver.typeHierFilePopup, cmds) + lspserver.typeHierFilePopup->clearmatches() var start_col = util.GetLineByteFromPos(bnr, typeUriMap[n].selectionRange.start) + 1 @@ -150,7 +151,7 @@ export def ShowTypeHierarchy(lspserver: dict, super: bool, types: dict # Display a popup window with the type hierarchy tree and a popup window for # the file. - var popupAttr = { + var popupAttrs = { title: $'{super ? "Super" : "Sub"}Type Hierarchy', wrap: 0, pos: 'topleft', @@ -161,11 +162,11 @@ export def ShowTypeHierarchy(lspserver: dict, super: bool, types: dict minwidth: 30, maxwidth: 30, mapping: false, - fixed: 1, + fixed: true, filter: function(TypeHierPopupFilter, [lspserver, typeUriMap]), callback: function(TypeHierPopupCallback, [lspserver, typeUriMap]) } - lspserver.typeHierPopup = popup_menu(typeTree, popupAttr) + lspserver.typeHierPopup = popup_menu(typeTree, popupAttrs) UpdateTypeHierFileInPopup(lspserver, typeUriMap) enddef diff --git a/doc/lsp.txt b/doc/lsp.txt index 635f485..36abd1e 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -105,9 +105,8 @@ The following commands are provided: popup window. :LspPeekImpl Open the implementation of the symbol under cursor in a popup window. -:LspPeekReferences Display the list of references to the keyword under - cursor in a location list associated with the preview - window. +:LspPeekReferences Display the list of references to the symbol under + cursor in a popup window. :LspPeekTypeDef Open the type definition of the symbol under cursor in a popup window. :LspRename Rename the current symbol @@ -446,9 +445,11 @@ To get a particular option value you can use the following: > opens the location window. *:LspPeekReferences* -:LspPeekReferences Opens the preview window and creates a new location - list with the list of locations where the symbol under - the cursor is referenced and opens the location window. +:LspPeekReferences Displays the list of references to the symbol under + cursor in a popup menu. The corresponding file for + the reference is displayed in another popup window. + As the selection in the reference popup menu changes, + the file in the popup is updated. *:LspHighlight* :LspHighlight Highlights all the matches for the symbol under diff --git a/test/unit_tests.vim b/test/unit_tests.vim index 069f6d0..9903b17 100644 --- a/test/unit_tests.vim +++ b/test/unit_tests.vim @@ -229,9 +229,14 @@ def Test_LspShowReferences() setlocal nomodified cursor(1, 5) :LspPeekReferences - assert_equal([3, 3], [winnr('$'), winnr()]) - assert_equal('preview', win_gettype(1)) - assert_equal('loclist', win_gettype(2)) + var ids = popup_list() + assert_equal(2, ids->len()) + var filePopupAttrs = ids[0]->popup_getoptions() + var refPopupAttrs = ids[1]->popup_getoptions() + assert_match('Xtest', filePopupAttrs.title) + assert_equal('References', refPopupAttrs.title) + assert_equal(1, line('.', ids[0])) + popup_clear() bw! -- 2.48.1