]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Support command modifiers for LspGotoDefinition, LspGotoDeclaration, etc. commands
authorYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 3 Nov 2022 05:18:09 +0000 (22:18 -0700)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 3 Nov 2022 05:18:09 +0000 (22:18 -0700)
autoload/lsp/lsp.vim
autoload/lsp/lspserver.vim
autoload/lsp/symbol.vim
doc/lsp.txt
plugin/lsp.vim
test/unit_tests.vim

index 97ef97663c3006111c7249be1f9c654c646089de..80a42bf017465022220ad046ec45cabc61af2057 100644 (file)
@@ -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<any> = 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<any> = 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<any> = 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<any> = 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<any> = CurbufGetServerChecked()
   if lspserver->empty()
     return
   endif
 
-  lspserver.gotoImplementation(peek)
+  lspserver.switchSourceHeader()
 enddef
 
 # Show the signature using "textDocument/signatureHelp" LSP method
index 14fb706a0e100764198e0964d71ec4df8503b006..b73d58eb8bcd522155a4307492228fa72d5441e9 100644 (file)
@@ -801,7 +801,8 @@ enddef
 # window but don't jump to the symbol location.
 #
 # Result: Location | Location[] | LocationLink[] | null
-def GotoSymbolLoc(lspserver: dict<any>, msg: string, peekSymbol: bool)
+def GotoSymbolLoc(lspserver: dict<any>, 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<any>, msg: string, peekSymbol: bool)
     return
   endif
 
-  if !peekSymbol
-    util.PushCursorToTagStack()
-  endif
-
   var location: dict<any>
   if reply.result->type() == v:t_list
     location = reply.result[0]
@@ -830,12 +827,12 @@ def GotoSymbolLoc(lspserver: dict<any>, 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<any>, peek: bool)
+def GotoDefinition(lspserver: dict<any>, 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<any>, 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<any>, peek: bool): void
+def GotoDeclaration(lspserver: dict<any>, 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<any>, 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<any>, peek: bool): void
+def GotoTypeDef(lspserver: dict<any>, 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<any>, 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<any>, peek: bool): void
+def GotoImplementation(lspserver: dict<any>, 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<any>, peek: bool): void
 
   # interface ImplementationParams
   #   interface TextDocumentPositionParams
-  GotoSymbolLoc(lspserver, 'textDocument/implementation', peek)
+  GotoSymbolLoc(lspserver, 'textDocument/implementation', peek, cmdmods)
 enddef
 
 # Request: "textDocument/switchSourceHeader"
index d1282a66fd468474fbfdd4c64b8d46e1d1dbdce6..110170a04545bb4e40acac3433409d9584cff2a0 100644 (file)
@@ -272,85 +272,103 @@ export def ShowReferences(lspserver: dict<any>, refs: list<dict<any>>, 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<any>, location: dict<any>, 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<any>, fname: string,
+                      location: dict<any>)
+  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<number> = []
+  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<number> = []
-    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<any>, fname: string,
+                        location: dict<any>, 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<any>, location: dict<any>,
+                     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
 
index b987c3575d8667d891ccbf58070e672b30f7dc96..57c1e1155f06f7e6e68615c09e0f4b0b772f4276 100644 (file)
@@ -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 <buffer> gd <Cmd>LspGotoDefinition<CR>
+                           nnoremap <buffer> <C-W>gd <Cmd>topleft LspGotoDefinition<CR>
 <
                                                *:LspGotoDeclaration*
 :LspGotoDeclaration    Jumps to the declaration of the symbol under the
index d1d54b3ead1b8c7d09013e651fe9067205c94162..27b6250530d810c5e06333f5796d31b4d37abe01 100644 (file)
@@ -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(<q-args>)
 command! -nargs=1 -complete=customlist,LspServerDebugComplete -bar LspServerDebug lsp.ServerDebug(<q-args>)
-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, <q-mods>)
+command! -nargs=0 -bar LspGotoDeclaration lsp.GotoDeclaration(v:false, <q-mods>)
+command! -nargs=0 -bar LspGotoTypeDef lsp.GotoTypedef(v:false, <q-mods>)
+command! -nargs=0 -bar LspGotoImpl lsp.GotoImplementation(v:false, <q-mods>)
+command! -nargs=0 -bar LspPeekDefinition lsp.GotoDefinition(v:true, <q-mods>)
+command! -nargs=0 -bar LspPeekDeclaration lsp.GotoDeclaration(v:true, <q-mods>)
+command! -nargs=0 -bar LspPeekTypeDef lsp.GotoTypedef(v:true, <q-mods>)
+command! -nargs=0 -bar LspPeekImpl lsp.GotoImplementation(v:true, <q-mods>)
 command! -nargs=0 -bar LspShowSignature call LspShowSignature()
 command! -nargs=0 -bar LspDiagShow lsp.ShowDiagnostics()
 command! -nargs=0 -bar LspDiagCurrent lsp.LspShowCurrentDiag()
index 85ee435d5d7374f62e809fcb1606b3b84f805a02..3e8817a63508e22a4153f46b7e3717d31d75d00e 100644 (file)
@@ -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! \<C-t>"
   assert_equal([24, 6], [line('.'), col('.')])
+  assert_equal(1, winnr('$'))
+
   :LspGotoDefinition
   assert_equal([9, 12], [line('.'), col('.')])
   exe "normal! \<C-t>"
   assert_equal([24, 6], [line('.'), col('.')])
-  # FIXME: The following test is failing in Github CI
-  # :LspGotoImpl
-  # assert_equal([15, 11], [line('.'), col('.')])
-  # exe "normal! \<C-t>"
-  # 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! \<C-t>"
+  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! \<C-t>"
+  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! \<C-t>", 'E555:')
 
+  # FIXME: :LspPeekTypeDef and :LspPeekImpl are supported only with clang-14.
+  # This clangd version is not available in Github CI.
+
   :%bw!
 
   # empty file