From: Yegappan Lakshmanan Date: Thu, 3 Nov 2022 05:18:09 +0000 (-0700) Subject: Support command modifiers for LspGotoDefinition, LspGotoDeclaration, etc. commands X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=5585387ef255690b8e9c4ac66462d35a2a121910;p=vim-lsp.git Support command modifiers for LspGotoDefinition, LspGotoDeclaration, etc. commands --- diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index 97ef976..80a42bf 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -134,54 +134,54 @@ export def ServerRunning(ftype: string): bool enddef # Go to a definition using "textDocument/definition" LSP request -export def GotoDefinition(peek: bool) +export def GotoDefinition(peek: bool, cmdmods: string) var lspserver: dict = CurbufGetServerChecked() if lspserver->empty() return endif - lspserver.gotoDefinition(peek) + lspserver.gotoDefinition(peek, cmdmods) enddef -# Switch source header using "textDocument/switchSourceHeader" LSP request -# (Clangd specifc extension) -export def SwitchSourceHeader() +# Go to a declaration using "textDocument/declaration" LSP request +export def GotoDeclaration(peek: bool, cmdmods: string) var lspserver: dict = CurbufGetServerChecked() if lspserver->empty() return endif - lspserver.switchSourceHeader() + lspserver.gotoDeclaration(peek, cmdmods) enddef -# Go to a declaration using "textDocument/declaration" LSP request -export def GotoDeclaration(peek: bool) +# Go to a type definition using "textDocument/typeDefinition" LSP request +export def GotoTypedef(peek: bool, cmdmods: string) var lspserver: dict = CurbufGetServerChecked() if lspserver->empty() return endif - lspserver.gotoDeclaration(peek) + lspserver.gotoTypeDef(peek, cmdmods) enddef -# Go to a type definition using "textDocument/typeDefinition" LSP request -export def GotoTypedef(peek: bool) +# Go to a implementation using "textDocument/implementation" LSP request +export def GotoImplementation(peek: bool, cmdmods: string) var lspserver: dict = CurbufGetServerChecked() if lspserver->empty() return endif - lspserver.gotoTypeDef(peek) + lspserver.gotoImplementation(peek, cmdmods) enddef -# Go to a implementation using "textDocument/implementation" LSP request -export def GotoImplementation(peek: bool) +# Switch source header using "textDocument/switchSourceHeader" LSP request +# (Clangd specifc extension) +export def SwitchSourceHeader() var lspserver: dict = CurbufGetServerChecked() if lspserver->empty() return endif - lspserver.gotoImplementation(peek) + lspserver.switchSourceHeader() enddef # Show the signature using "textDocument/signatureHelp" LSP method diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index 14fb706..b73d58e 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -801,7 +801,8 @@ enddef # window but don't jump to the symbol location. # # Result: Location | Location[] | LocationLink[] | null -def GotoSymbolLoc(lspserver: dict, msg: string, peekSymbol: bool) +def GotoSymbolLoc(lspserver: dict, msg: string, peekSymbol: bool, + cmdmods: string) var reply = lspserver.rpc(msg, GetLspTextDocPosition()) if reply->empty() || reply.result->empty() var emsg: string @@ -819,10 +820,6 @@ def GotoSymbolLoc(lspserver: dict, msg: string, peekSymbol: bool) return endif - if !peekSymbol - util.PushCursorToTagStack() - endif - var location: dict if reply.result->type() == v:t_list location = reply.result[0] @@ -830,12 +827,12 @@ def GotoSymbolLoc(lspserver: dict, msg: string, peekSymbol: bool) location = reply.result endif - symbol.GotoSymbol(lspserver, location, peekSymbol) + symbol.GotoSymbol(lspserver, location, peekSymbol, cmdmods) enddef # Request: "textDocument/definition" # Param: DefinitionParams -def GotoDefinition(lspserver: dict, peek: bool) +def GotoDefinition(lspserver: dict, peek: bool, cmdmods: string) # Check whether LSP server supports jumping to a definition if !lspserver.caps->get('definitionProvider', false) util.ErrMsg("Error: Jumping to a symbol definition is not supported") @@ -844,12 +841,12 @@ def GotoDefinition(lspserver: dict, peek: bool) # interface DefinitionParams # interface TextDocumentPositionParams - GotoSymbolLoc(lspserver, 'textDocument/definition', peek) + GotoSymbolLoc(lspserver, 'textDocument/definition', peek, cmdmods) enddef # Request: "textDocument/declaration" # Param: DeclarationParams -def GotoDeclaration(lspserver: dict, peek: bool): void +def GotoDeclaration(lspserver: dict, peek: bool, cmdmods: string) # Check whether LSP server supports jumping to a declaration if !lspserver.caps->get('declarationProvider', false) util.ErrMsg("Error: Jumping to a symbol declaration is not supported") @@ -858,12 +855,12 @@ def GotoDeclaration(lspserver: dict, peek: bool): void # interface DeclarationParams # interface TextDocumentPositionParams - GotoSymbolLoc(lspserver, 'textDocument/declaration', peek) + GotoSymbolLoc(lspserver, 'textDocument/declaration', peek, cmdmods) enddef # Request: "textDocument/typeDefinition" # Param: TypeDefinitionParams -def GotoTypeDef(lspserver: dict, peek: bool): void +def GotoTypeDef(lspserver: dict, peek: bool, cmdmods: string) # Check whether LSP server supports jumping to a type definition if !lspserver.caps->get('typeDefinitionProvider', false) util.ErrMsg("Error: Jumping to a symbol type definition is not supported") @@ -872,12 +869,12 @@ def GotoTypeDef(lspserver: dict, peek: bool): void # interface TypeDefinitionParams # interface TextDocumentPositionParams - GotoSymbolLoc(lspserver, 'textDocument/typeDefinition', peek) + GotoSymbolLoc(lspserver, 'textDocument/typeDefinition', peek, cmdmods) enddef # Request: "textDocument/implementation" # Param: ImplementationParams -def GotoImplementation(lspserver: dict, peek: bool): void +def GotoImplementation(lspserver: dict, peek: bool, cmdmods: string) # Check whether LSP server supports jumping to a implementation if !lspserver.caps->get('implementationProvider', false) util.ErrMsg("Error: Jumping to a symbol implementation is not supported") @@ -886,7 +883,7 @@ def GotoImplementation(lspserver: dict, peek: bool): void # interface ImplementationParams # interface TextDocumentPositionParams - GotoSymbolLoc(lspserver, 'textDocument/implementation', peek) + GotoSymbolLoc(lspserver, 'textDocument/implementation', peek, cmdmods) enddef # Request: "textDocument/switchSourceHeader" diff --git a/autoload/lsp/symbol.vim b/autoload/lsp/symbol.vim index d1282a6..110170a 100644 --- a/autoload/lsp/symbol.vim +++ b/autoload/lsp/symbol.vim @@ -272,85 +272,103 @@ export def ShowReferences(lspserver: dict, refs: list>, peekSymbo endif 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, peekSymbol: bool) - var fname = util.LspUriToFile(location.uri) - if peekSymbol - # open the definition/declaration in the preview window and highlight the - # matching symbol +# Display the symbol in file 'fname' at 'location' in a popup window. +def PeekSymbolLocation(lspserver: dict, fname: string, + location: dict) + var bnum = fname->bufadd() + if bnum == 0 + # Failed to create or find a buffer + return + endif + silent! bnum->bufload() - var bnum = bufadd(fname) - if bnum == 0 - # Failed to create or find a buffer - return - endif + if lspserver.peekSymbolPopup->winbufnr() != -1 + # If the symbol popup window is already present, close it. + lspserver.peekSymbolPopup->popup_close() + endif + var ptitle = $"{fnamemodify(fname, ':t')} ({fnamemodify(fname, ':h')})" + lspserver.peekSymbolPopup = popup_atcursor(bnum, {moved: 'any', + title: ptitle, + minwidth: 10, + maxwidth: 60, + minheight: 10, + maxheight: 10, + mapping: false, + wrap: false}) + + # Highlight the symbol name and center the line in the popup + var pwid = lspserver.peekSymbolPopup + var pwbuf = winbufnr(pwid) + var pos: list = [] + var start_col: number + var end_col: number + start_col = util.GetLineByteFromPos(pwbuf, location.range.start) + 1 + end_col = util.GetLineByteFromPos(pwbuf, location.range.end) + 1 + pos->add(location.range.start.line + 1) + pos->extend([start_col, end_col - start_col]) + matchaddpos('Search', [pos], 10, 101, {window: pwid}) + var cmds =<< trim eval END + cursor({location.range.start.line + 1}, 1) + normal! z. + END + win_execute(pwid, cmds, 'silent!') +enddef - if lspserver.peekSymbolPopup->winbufnr() != -1 - # If the symbol popup window is already present, close it. - lspserver.peekSymbolPopup->popup_close() - endif - var ptitle = $"{fnamemodify(fname, ':t')} ({fnamemodify(fname, ':h')})" - lspserver.peekSymbolPopup = popup_atcursor(bnum, {moved: 'any', - title: ptitle, - minwidth: 10, - maxwidth: 60, - minheight: 10, - maxheight: 10, - mapping: false, - wrap: false}) - - # Highlight the symbol name and center the line in the popup - var pwid = lspserver.peekSymbolPopup - var pwbuf = winbufnr(pwid) - var pos: list = [] - var start_col: number - var end_col: number - start_col = util.GetLineByteFromPos(pwbuf, location.range.start) + 1 - end_col = util.GetLineByteFromPos(pwbuf, location.range.end) + 1 - pos->add(location.range.start.line + 1) - pos->extend([start_col, end_col - start_col]) - matchaddpos('Search', [pos], 10, 101, {window: pwid}) - var cmds =<< trim eval END - cursor({location.range.start.line + 1}, 1) - normal! z. - END - win_execute(pwid, cmds, 'silent!') - else - # jump to the file and line containing the symbol +# Jump to the symbol in file 'fname' at 'location'. The user specified +# window command modifiers (e.g. topleft) are in 'cmdmods'. +def JumpToSymbolLocation(lspserver: dict, fname: string, + location: dict, cmdmods: string) + # jump to the file and line containing the symbol + if cmdmods == '' var bnr: number = fname->bufnr() if bnr != bufnr() var wid = fname->bufwinid() if wid != -1 - wid->win_gotoid() + wid->win_gotoid() else - if bnr != -1 - # Reuse an existing buffer. If the current buffer has unsaved changes - # and 'hidden' is not set or if the current buffer is a special - # buffer, then open the buffer in a new window. - if (&modified && !&hidden) || &buftype != '' - exe $'sbuffer {bnr}' - else - exe $'buf {bnr}' - endif - else - if (&modified && !&hidden) || &buftype != '' - # if the current buffer has unsaved changes and 'hidden' is not set, - # or if the current buffer is a special buffer, then open the file - # in a new window - exe $'split {fname}' - else - exe $'edit {fname}' - endif - endif + if bnr != -1 + # Reuse an existing buffer. If the current buffer has unsaved changes + # and 'hidden' is not set or if the current buffer is a special + # buffer, then open the buffer in a new window. + if (&modified && !&hidden) || &buftype != '' + exe $'sbuffer {bnr}' + else + exe $'buf {bnr}' + endif + else + if (&modified && !&hidden) || &buftype != '' + # if the current buffer has unsaved changes and 'hidden' is not set, + # or if the current buffer is a special buffer, then open the file + # in a new window + exe $'split {fname}' + else + exe $'edit {fname}' + endif + endif endif endif - # Set the previous cursor location mark. Instead of using setpos(), m' is - # used so that the current location is added to the jump list. - normal m' - setcursorcharpos(location.range.start.line + 1, + else + exe $'{cmdmods} split {fname}' + endif + # Set the previous cursor location mark. Instead of using setpos(), m' is + # used so that the current location is added to the jump list. + normal m' + setcursorcharpos(location.range.start.line + 1, location.range.start.character + 1) +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, + peekSymbol: bool, cmdmods: string) + var fname = util.LspUriToFile(location.uri) + if peekSymbol + PeekSymbolLocation(lspserver, fname, location) + else + # Save the current cursor location in the tag stack. + util.PushCursorToTagStack() + JumpToSymbolLocation(lspserver, fname, location, cmdmods) endif enddef diff --git a/doc/lsp.txt b/doc/lsp.txt index b987c35..57c1e11 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -304,9 +304,21 @@ diagnostic messages, you can add the following line to your .vimrc file: can be used to go back up the tag stack. Also the |``| mark is set to the position before the jump. + This command supports |:command-modifiers|. You can + use the modifiers to specify whether a new window or + a new tab page is used and where the window is opened. + Example(s): > + # Open a horizontally split window + :topleft LspGotoDefinition + # Open a vertically split window + :vert LspGotoDefinition + # Open a new tab page + :tab LspGotoDefinition +< You may want to map a key to invoke this command: > nnoremap gd LspGotoDefinition + nnoremap gd topleft LspGotoDefinition < *:LspGotoDeclaration* :LspGotoDeclaration Jumps to the declaration of the symbol under the diff --git a/plugin/lsp.vim b/plugin/lsp.vim index d1d54b3..27b6250 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -70,14 +70,14 @@ command! -nargs=0 -bar LspShowServerCapabilities lsp.ShowServerCapabilities() command! -nargs=0 -bar LspServerRestart lsp.RestartServer() command! -nargs=1 -complete=customlist,LspServerTraceComplete -bar LspServerTrace lsp.ServerTraceSet() command! -nargs=1 -complete=customlist,LspServerDebugComplete -bar LspServerDebug lsp.ServerDebug() -command! -nargs=0 -bar LspGotoDefinition lsp.GotoDefinition(v:false) -command! -nargs=0 -bar LspGotoDeclaration lsp.GotoDeclaration(v:false) -command! -nargs=0 -bar LspGotoTypeDef lsp.GotoTypedef(v:false) -command! -nargs=0 -bar LspGotoImpl lsp.GotoImplementation(v:false) -command! -nargs=0 -bar LspPeekDefinition lsp.GotoDefinition(v:true) -command! -nargs=0 -bar LspPeekDeclaration lsp.GotoDeclaration(v:true) -command! -nargs=0 -bar LspPeekTypeDef lsp.GotoTypedef(v:true) -command! -nargs=0 -bar LspPeekImpl lsp.GotoImplementation(v:true) +command! -nargs=0 -bar LspGotoDefinition lsp.GotoDefinition(v:false, ) +command! -nargs=0 -bar LspGotoDeclaration lsp.GotoDeclaration(v:false, ) +command! -nargs=0 -bar LspGotoTypeDef lsp.GotoTypedef(v:false, ) +command! -nargs=0 -bar LspGotoImpl lsp.GotoImplementation(v:false, ) +command! -nargs=0 -bar LspPeekDefinition lsp.GotoDefinition(v:true, ) +command! -nargs=0 -bar LspPeekDeclaration lsp.GotoDeclaration(v:true, ) +command! -nargs=0 -bar LspPeekTypeDef lsp.GotoTypedef(v:true, ) +command! -nargs=0 -bar LspPeekImpl lsp.GotoImplementation(v:true, ) command! -nargs=0 -bar LspShowSignature call LspShowSignature() command! -nargs=0 -bar LspDiagShow lsp.ShowDiagnostics() command! -nargs=0 -bar LspDiagCurrent lsp.LspShowCurrentDiag() diff --git a/test/unit_tests.vim b/test/unit_tests.vim index 85ee435..3e8817a 100644 --- a/test/unit_tests.vim +++ b/test/unit_tests.vim @@ -547,20 +547,37 @@ def Test_LspGotoSymbol() END setline(1, lines) :sleep 1 + cursor(24, 6) :LspGotoDeclaration assert_equal([6, 19], [line('.'), col('.')]) exe "normal! \" assert_equal([24, 6], [line('.'), col('.')]) + assert_equal(1, winnr('$')) + :LspGotoDefinition assert_equal([9, 12], [line('.'), col('.')]) exe "normal! \" assert_equal([24, 6], [line('.'), col('.')]) - # FIXME: The following test is failing in Github CI - # :LspGotoImpl - # assert_equal([15, 11], [line('.'), col('.')]) - # exe "normal! \" - # assert_equal([24, 6], [line('.'), col('.')]) + assert_equal(1, winnr('$')) + + # Command modifiers + :topleft LspGotoDefinition + assert_equal([9, 12], [line('.'), col('.')]) + assert_equal([1, 2], [winnr(), winnr('$')]) + close + exe "normal! \" + assert_equal([24, 6], [line('.'), col('.')]) + + :tab LspGotoDefinition + assert_equal([9, 12], [line('.'), col('.')]) + assert_equal([2, 2, 1], [tabpagenr(), tabpagenr('$'), winnr('$')]) + tabclose + exe "normal! \" + assert_equal([24, 6], [line('.'), col('.')]) + + # FIXME: :LspGotoTypeDef and :LspGotoImpl are supported only with clang-14. + # This clangd version is not available in Github CI. # Error cases # FIXME: The following tests are failing in Github CI. Comment out for now. @@ -602,6 +619,9 @@ def Test_LspGotoSymbol() # tag stack should not be changed assert_fails("normal! \", 'E555:') + # FIXME: :LspPeekTypeDef and :LspPeekImpl are supported only with clang-14. + # This clangd version is not available in Github CI. + :%bw! # empty file