From: Yegappan Lakshmanan Date: Sun, 16 Jan 2022 21:57:35 +0000 (-0800) Subject: Move the functions to display symbol references and jump to a symbol to a separate... X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=e8c3f107780a0a8ef601a2d3b89803e8ac630118;p=vim-lsp.git Move the functions to display symbol references and jump to a symbol to a separate file --- diff --git a/autoload/handlers.vim b/autoload/handlers.vim index 168743e..67b7865 100644 --- a/autoload/handlers.vim +++ b/autoload/handlers.vim @@ -9,6 +9,7 @@ var util = {} var diag = {} var outline = {} var textedit = {} +var symbol = {} var codeaction = {} if has('patch-8.2.4019') @@ -17,6 +18,7 @@ if has('patch-8.2.4019') import './diag.vim' as diag_import import './outline.vim' as outline_import import './textedit.vim' as textedit_import + import './symbol.vim' as symbol_import import './codeaction.vim' as codeaction_import opt.lspOptions = opt_import.lspOptions @@ -29,6 +31,8 @@ if has('patch-8.2.4019') outline.UpdateOutlineWindow = outline_import.UpdateOutlineWindow textedit.ApplyTextEdits = textedit_import.ApplyTextEdits textedit.ApplyWorkspaceEdit = textedit_import.ApplyWorkspaceEdit + symbol.ShowReferences = symbol_import.ShowReferences + symbol.GotoSymbol = symbol_import.GotoSymbol codeaction.ApplyCodeAction = codeaction_import.ApplyCodeAction else import lspOptions from './lspoptions.vim' @@ -40,6 +44,7 @@ else import DiagNotification from './diag.vim' import UpdateOutlineWindow from './outline.vim' import {ApplyTextEdits, ApplyWorkspaceEdit} from './textedit.vim' + import {ShowReferences, GotoSymbol} from './symbol.vim' import ApplyCodeAction from './codeaction.vim' opt.lspOptions = lspOptions @@ -52,6 +57,8 @@ else outline.UpdateOutlineWindow = UpdateOutlineWindow textedit.ApplyTextEdits = ApplyTextEdits textedit.ApplyWorkspaceEdit = ApplyWorkspaceEdit + symbol.ShowReferences = ShowReferences + symbol.GotoSymbol = GotoSymbol codeaction.ApplyCodeAction = ApplyCodeAction endif @@ -103,75 +110,18 @@ enddef # the LSP server # Result: Location | Location[] | LocationLink[] | null def s:processDefDeclReply(lspserver: dict, req: dict, reply: dict): void - if reply.result->empty() - util.WarnMsg("Error: definition is not found") - if !lspserver.peekSymbol - # pop the tag stack - var tagstack: dict = gettagstack() - if tagstack.length > 0 - settagstack(winnr(), {curidx: tagstack.length}, 't') - endif - endif - lspserver.peekSymbol = false - return - endif - var location: dict if reply.result->type() == v:t_list - location = reply.result[0] - else - location = reply.result - endif - var fname = util.LspUriToFile(location.uri) - if lspserver.peekSymbol - # open the definition/declaration in the preview window and highlight the - # matching symbol - exe 'pedit ' .. fname - var cur_wid = win_getid() - wincmd P - var pvwbuf = bufnr() - setcursorcharpos(location.range.start.line + 1, - location.range.start.character + 1) - silent! matchdelete(101) - var pos: list = [] - var start_col: number - var end_col: number - start_col = util.GetLineByteFromPos(pvwbuf, location.range.start) + 1 - end_col = util.GetLineByteFromPos(pvwbuf, location.range.end) + 1 - pos->add(location.range.start.line + 1) - pos->extend([start_col, end_col - start_col]) - matchaddpos('Search', [pos], 10, 101) - win_gotoid(cur_wid) - else - # jump to the file and line containing the symbol - var wid = fname->bufwinid() - if wid != -1 - wid->win_gotoid() + if !reply.result->empty() + location = reply.result[0] else - var bnr: number = fname->bufnr() - if bnr != -1 - if &modified || &buftype != '' - exe 'sbuffer ' .. bnr - else - exe 'buf ' .. bnr - endif - else - if &modified || &buftype != '' - # if the current buffer has unsaved changes, then open the file in a - # new window - exe 'split ' .. fname - else - exe 'edit ' .. fname - endif - endif + location = {} endif - # Set the previous cursor location mark - setpos("'`", getcurpos()) - setcursorcharpos(location.range.start.line + 1, - location.range.start.character + 1) + else + location = reply.result endif - redraw! - lspserver.peekSymbol = false + + symbol.GotoSymbol(lspserver, location) enddef # process the 'textDocument/signatureHelp' reply from the LSP server @@ -420,50 +370,7 @@ enddef # process the 'textDocument/references' reply from the LSP server # Result: Location[] | null def s:processReferencesReply(lspserver: dict, req: dict, reply: dict): void - if reply.result->empty() - util.WarnMsg('Error: No references found') - lspserver.peekSymbol = false - return - endif - - # create a quickfix list with the location of the references - var locations: list> = reply.result - var qflist: list> = [] - for loc in locations - 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] - ->trim("\t ", 1) - qflist->add({filename: fname, - lnum: loc.range.start.line + 1, - col: util.GetLineByteFromPos(bnr, loc.range.start) + 1, - text: text}) - endfor - - var save_winid = win_getid() - if lspserver.peekSymbol - silent! pedit - wincmd P - endif - setloclist(0, [], ' ', {title: 'Symbol Reference', items: qflist}) - var mods: string = '' - if lspserver.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 - lspserver.peekSymbol = false + symbol.ShowReferences(lspserver, reply.result) enddef # process the 'textDocument/documentHighlight' reply from the LSP server diff --git a/autoload/lsp.vim b/autoload/lsp.vim index cdb6594..a30df2a 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -19,7 +19,7 @@ if has('patch-8.2.4019') import './lspserver.vim' as server_import import './util.vim' as util_import import './diag.vim' as diag_import - import './symbolsearch.vim' as symbolsearch_import + import './symbol.vim' as symbol_import import './outline.vim' as outline_import opt.LspOptionsSet = opt_import.LspOptionsSet @@ -37,7 +37,7 @@ if has('patch-8.2.4019') diag.ShowCurrentDiag = diag_import.ShowCurrentDiag diag.LspDiagsJump = diag_import.LspDiagsJump diag.DiagRemoveFile = diag_import.DiagRemoveFile - symbol.ShowSymbolMenu = symbolsearch_import.ShowSymbolMenu + symbol.ShowSymbolMenu = symbol_import.ShowSymbolMenu outline.OpenOutlineWindow = outline_import.OpenOutlineWindow outline.SkipOutlineRefresh = outline_import.SkipOutlineRefresh else @@ -55,7 +55,7 @@ else ShowAllDiags, ShowCurrentDiag, LspDiagsJump} from './diag.vim' - import ShowSymbolMenu from './symbolsearch.vim' + import ShowSymbolMenu from './symbol.vim' import {OpenOutlineWindow, SkipOutlineRefresh} from './outline.vim' opt.LspOptionsSet = LspOptionsSet diff --git a/autoload/symbol.vim b/autoload/symbol.vim new file mode 100644 index 0000000..4240f62 --- /dev/null +++ b/autoload/symbol.vim @@ -0,0 +1,280 @@ +vim9script + +# Functions for dealing with symbols. +# - LSP symbol menu and for searching symbols across the workspace. +# - show symbol references +# - jump to a symbol definition, declaration, type definition or +# implementation + +var opt = {} +var util = {} + +if has('patch-8.2.4019') + import './lspoptions.vim' as opt_import + import './util.vim' as util_import + + opt.lspOptions = opt_import.lspOptions + util.PushCursorToTagStack = util_import.PushCursorToTagStack + util.WarnMsg = util_import.WarnMsg + util.LspUriToFile = util_import.LspUriToFile + util.GetLineByteFromPos = util_import.GetLineByteFromPos +else + import lspOptions from './lspoptions.vim' + import {WarnMsg, + LspUriToFile, + GetLineByteFromPos, + PushCursorToTagStack} from './util.vim' + + opt.lspOptions = lspOptions + util.WarnMsg = WarnMsg + util.LspUriToFile = LspUriToFile + util.GetLineByteFromPos = GetLineByteFromPos + util.PushCursorToTagStack = PushCursorToTagStack +endif + +# Handle keys pressed when the workspace symbol popup menu is displayed +def s:filterSymbols(lspserver: dict, popupID: number, key: string): bool + var key_handled: bool = false + var update_popup: bool = false + var query: string = lspserver.workspaceSymbolQuery + + if key == "\" || key == "\" + # Erase one character from the filter text + if query->len() >= 1 + query = query[: -2] + update_popup = true + endif + key_handled = true + elseif key == "\" + # clear the filter text + query = '' + update_popup = true + key_handled = true + elseif key == "\" + || key == "\" + || key == "\" + || key == "\" + || key == "\" + || key == "\" + || key == "\" + || key == "\" + # scroll the popup window + var cmd: string = 'normal! ' .. (key == "\" ? 'j' : key == "\" ? 'k' : key) + win_execute(popupID, cmd) + key_handled = true + elseif key == "\" || key == "\" + # Use native Vim handling for these keys + key_handled = false + elseif key =~ '^\f$' || key == "\" + # Filter the names based on the typed key and keys typed before + query ..= key + update_popup = true + key_handled = true + endif + + if update_popup + # Update the popup with the new list of symbol names + popupID->popup_settext('') + if query != '' + lspserver.workspaceQuery(query) + else + []->setwinvar(popupID, 'LspSymbolTable') + endif + echo 'Symbol: ' .. query + endif + + # Update the workspace symbol query string + lspserver.workspaceSymbolQuery = query + + if key_handled + return true + endif + + return popupID->popup_filter_menu(key) +enddef + +# Jump to the location of a symbol selected in the popup menu +def s:jumpToWorkspaceSymbol(popupID: number, result: number): void + # clear the message displayed at the command-line + echo '' + + if result <= 0 + # popup is canceled + return + endif + + var symTbl: list> = popupID->getwinvar('LspSymbolTable', []) + if symTbl->empty() + return + endif + try + # Save the current location in the tag stack + util.PushCursorToTagStack() + + # if the selected file is already present in a window, then jump to it + var fname: string = symTbl[result - 1].file + var winList: list = fname->bufnr()->win_findbuf() + if winList->len() == 0 + # Not present in any window + if &modified || &buftype != '' + # the current buffer is modified or is not a normal buffer, then open + # the file in a new window + exe "split " .. symTbl[result - 1].file + else + exe "confirm edit " .. symTbl[result - 1].file + endif + else + winList[0]->win_gotoid() + endif + setcursorcharpos(symTbl[result - 1].pos.line + 1, + symTbl[result - 1].pos.character + 1) + catch + # ignore exceptions + endtry +enddef + +# display a list of symbols from the workspace +export def ShowSymbolMenu(lspserver: dict, query: string) + # Create the popup menu + var lnum = &lines - &cmdheight - 2 - 10 + var popupAttr = { + title: 'Workspace Symbol Search', + wrap: 0, + pos: 'topleft', + line: lnum, + col: 2, + minwidth: 60, + minheight: 10, + maxheight: 10, + maxwidth: 60, + mapping: false, + fixed: 1, + close: "button", + filter: function('s:filterSymbols', [lspserver]), + callback: function('s:jumpToWorkspaceSymbol') + } + lspserver.workspaceSymbolPopup = popup_menu([], popupAttr) + lspserver.workspaceSymbolQuery = query + prop_type_add('lspworkspacesymbol', + {bufnr: lspserver.workspaceSymbolPopup->winbufnr(), + highlight: 'Title'}) + echo 'Symbol: ' .. query +enddef + +# Display or peek symbol references in a location list +export def ShowReferences(lspserver: dict, refs: list>) + if refs->empty() + util.WarnMsg('Error: No references found') + lspserver.peekSymbol = false + return + endif + + # create a location list with the location of the references + var qflist: 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] + ->trim("\t ", 1) + qflist->add({filename: fname, + lnum: loc.range.start.line + 1, + col: util.GetLineByteFromPos(bnr, loc.range.start) + 1, + text: text}) + endfor + + var save_winid = win_getid() + if lspserver.peekSymbol + silent! pedit + wincmd P + endif + setloclist(0, [], ' ', {title: 'Symbol Reference', items: qflist}) + var mods: string = '' + if lspserver.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 + lspserver.peekSymbol = false +enddef + +# Jump to the definition, declaration or implementation of a symbol. +# Also, used to peek at the definition, declaration or implementation of a +# symbol. +export def GotoSymbol(lspserver: dict, location: dict) + if location->empty() + util.WarnMsg("Error: definition is not found") + if !lspserver.peekSymbol + # pop the tag stack + var tagstack: dict = gettagstack() + if tagstack.length > 0 + settagstack(winnr(), {curidx: tagstack.length}, 't') + endif + endif + lspserver.peekSymbol = false + return + endif + + var fname = util.LspUriToFile(location.uri) + if lspserver.peekSymbol + # open the definition/declaration in the preview window and highlight the + # matching symbol + exe 'pedit ' .. fname + var cur_wid = win_getid() + wincmd P + var pvwbuf = bufnr() + setcursorcharpos(location.range.start.line + 1, + location.range.start.character + 1) + silent! matchdelete(101) + var pos: list = [] + var start_col: number + var end_col: number + start_col = util.GetLineByteFromPos(pvwbuf, location.range.start) + 1 + end_col = util.GetLineByteFromPos(pvwbuf, location.range.end) + 1 + pos->add(location.range.start.line + 1) + pos->extend([start_col, end_col - start_col]) + matchaddpos('Search', [pos], 10, 101) + win_gotoid(cur_wid) + else + # jump to the file and line containing the symbol + var wid = fname->bufwinid() + if wid != -1 + wid->win_gotoid() + else + var bnr: number = fname->bufnr() + if bnr != -1 + if &modified || &buftype != '' + exe 'sbuffer ' .. bnr + else + exe 'buf ' .. bnr + endif + else + if &modified || &buftype != '' + # if the current buffer has unsaved changes, then open the file in a + # new window + exe 'split ' .. fname + else + exe 'edit ' .. fname + endif + endif + endif + # Set the previous cursor location mark + setpos("'`", getcurpos()) + setcursorcharpos(location.range.start.line + 1, + location.range.start.character + 1) + endif + redraw! + lspserver.peekSymbol = false +enddef + +# vim: shiftwidth=2 softtabstop=2 diff --git a/autoload/symbolsearch.vim b/autoload/symbolsearch.vim deleted file mode 100644 index cee7266..0000000 --- a/autoload/symbolsearch.vim +++ /dev/null @@ -1,145 +0,0 @@ -vim9script - -# Functions for the LSP symbol menu and for searching symbols across the -# workspace. - -var util = {} - -if has('patch-8.2.4019') - import './util.vim' as util_import - util.PushCursorToTagStack = util_import.PushCursorToTagStack -else - import {PushCursorToTagStack} from './util.vim' - util.PushCursorToTagStack = PushCursorToTagStack -endif - -# Handle keys pressed when the workspace symbol popup menu is displayed -def s:filterSymbols(lspserver: dict, popupID: number, key: string): bool - var key_handled: bool = false - var update_popup: bool = false - var query: string = lspserver.workspaceSymbolQuery - - if key == "\" || key == "\" - # Erase one character from the filter text - if query->len() >= 1 - query = query[: -2] - update_popup = true - endif - key_handled = true - elseif key == "\" - # clear the filter text - query = '' - update_popup = true - key_handled = true - elseif key == "\" - || key == "\" - || key == "\" - || key == "\" - || key == "\" - || key == "\" - || key == "\" - || key == "\" - # scroll the popup window - var cmd: string = 'normal! ' .. (key == "\" ? 'j' : key == "\" ? 'k' : key) - win_execute(popupID, cmd) - key_handled = true - elseif key == "\" || key == "\" - # Use native Vim handling for these keys - key_handled = false - elseif key =~ '^\f$' || key == "\" - # Filter the names based on the typed key and keys typed before - query ..= key - update_popup = true - key_handled = true - endif - - if update_popup - # Update the popup with the new list of symbol names - popupID->popup_settext('') - if query != '' - lspserver.workspaceQuery(query) - else - []->setwinvar(popupID, 'LspSymbolTable') - endif - echo 'Symbol: ' .. query - endif - - # Update the workspace symbol query string - lspserver.workspaceSymbolQuery = query - - if key_handled - return true - endif - - return popupID->popup_filter_menu(key) -enddef - -# Jump to the location of a symbol selected in the popup menu -def s:jumpToWorkspaceSymbol(popupID: number, result: number): void - # clear the message displayed at the command-line - echo '' - - if result <= 0 - # popup is canceled - return - endif - - var symTbl: list> = popupID->getwinvar('LspSymbolTable', []) - if symTbl->empty() - return - endif - try - # Save the current location in the tag stack - util.PushCursorToTagStack() - - # if the selected file is already present in a window, then jump to it - var fname: string = symTbl[result - 1].file - var winList: list = fname->bufnr()->win_findbuf() - if winList->len() == 0 - # Not present in any window - if &modified || &buftype != '' - # the current buffer is modified or is not a normal buffer, then open - # the file in a new window - exe "split " .. symTbl[result - 1].file - else - exe "confirm edit " .. symTbl[result - 1].file - endif - else - winList[0]->win_gotoid() - endif - setcursorcharpos(symTbl[result - 1].pos.line + 1, - symTbl[result - 1].pos.character + 1) - catch - # ignore exceptions - endtry -enddef - -# display a list of symbols from the workspace -export def ShowSymbolMenu(lspserver: dict, query: string) - # Create the popup menu - var lnum = &lines - &cmdheight - 2 - 10 - var popupAttr = { - title: 'Workspace Symbol Search', - wrap: 0, - pos: 'topleft', - line: lnum, - col: 2, - minwidth: 60, - minheight: 10, - maxheight: 10, - maxwidth: 60, - mapping: false, - fixed: 1, - close: "button", - filter: function('s:filterSymbols', [lspserver]), - callback: function('s:jumpToWorkspaceSymbol') - } - lspserver.workspaceSymbolPopup = popup_menu([], popupAttr) - lspserver.workspaceSymbolQuery = query - prop_type_add('lspworkspacesymbol', - {bufnr: lspserver.workspaceSymbolPopup->winbufnr(), - highlight: 'Title'}) - echo 'Symbol: ' .. query -enddef - -# vim: shiftwidth=2 softtabstop=2