]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Add support for displaying type hiearchy in a popup window
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sun, 20 Nov 2022 07:09:03 +0000 (23:09 -0800)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Sun, 20 Nov 2022 07:09:03 +0000 (23:09 -0800)
autoload/lsp/lsp.vim
autoload/lsp/lspserver.vim
autoload/lsp/symbol.vim
autoload/lsp/typehierarchy.vim [new file with mode: 0644]
autoload/lsp/util.vim
doc/lsp.txt
plugin/lsp.vim

index 292b581764eaccc72310f3ec85778d88afffbfb2..fe86fbef75a9ae1b50e2f02337e62f2b5e11deee 100644 (file)
@@ -43,6 +43,7 @@ def LspInitOnce()
   prop_type_add('LspTextRef', {'highlight': 'Search'})
   prop_type_add('LspReadRef', {'highlight': 'DiffChange'})
   prop_type_add('LspWriteRef', {'highlight': 'DiffDelete'})
+
   set ballooneval balloonevalterm
   lspInitializedOnce = true
 enddef
@@ -648,6 +649,17 @@ export def OutgoingCalls()
   lspserver.outgoingCalls(@%)
 enddef
 
+# Display the type hierarchy for the current symbol.  Direction is 0 for
+# sub types and 1 for super types.
+export def TypeHierarchy(direction: number)
+  var lspserver: dict<any> = buf.CurbufGetServerChecked()
+  if lspserver->empty()
+    return
+  endif
+
+  lspserver.typeHierarchy(direction)
+enddef
+
 # Rename a symbol
 # Uses LSP "textDocument/rename" request
 export def Rename()
index 817ec1ca69fcb61b9a61d82963a593cbfd2eae51..67ceb599eb9ee2160b66534899cbc0da7d40e206 100644 (file)
@@ -20,6 +20,7 @@ import './hover.vim'
 import './signature.vim'
 import './codeaction.vim'
 import './callhierarchy.vim' as callhier
+import './typehierarchy.vim' as typehier
 
 # LSP server standard output handler
 def Output_cb(lspserver: dict<any>, chan: channel, msg: any): void
@@ -224,6 +225,13 @@ def ProcessServerCaps(lspserver: dict<any>, caps: dict<any>)
     lspserver.isCallHierarchyProvider = false
   endif
 
+  # typeHierarchyProvider
+  if lspserver.caps->has_key('typeHierarchyProvider')
+    lspserver.isTypeHierarchyProvider = true
+  else
+    lspserver.isTypeHierarchyProvider = false
+  endif
+
   # renameProvider
   if lspserver.caps->has_key('renameProvider')
     if lspserver.caps.renameProvider->type() == v:t_bool
@@ -1212,6 +1220,33 @@ def OutgoingCalls(lspserver: dict<any>, fname: string)
   callhier.OutgoingCalls(reply.result)
 enddef
 
+# Request: "textDocument/typehierarchy"
+# Support the clangd version of type hierarchy retrieval method.
+# The method described in the LSP 3.17.0 standard is not supported as clangd
+# doesn't support that method.
+def TypeHiearchy(lspserver: dict<any>, direction: number)
+  # Check whether LSP server supports type hierarchy
+  if !lspserver.isTypeHierarchyProvider
+    util.ErrMsg("Error: LSP server does not support type hierarchy")
+    return
+  endif
+
+  # interface TypeHierarchy
+  #   interface TextDocumentPositionParams
+  var param: dict<any>
+  param = GetLspTextDocPosition()
+  # 0: children, 1: parent, 2: both
+  param.direction = direction
+  param.resolve = 5
+  var reply = lspserver.rpc('textDocument/typeHierarchy', param)
+  if reply->empty() || reply.result->empty()
+    util.WarnMsg('No type hierarchy available')
+    return
+  endif
+
+  typehier.ShowTypeHierarchy(lspserver, direction == 1, reply.result)
+enddef
+
 # Request: "textDocument/rename"
 # Param: RenameParams
 def RenameSymbol(lspserver: dict<any>, newName: string)
@@ -1502,6 +1537,8 @@ export def NewLspServer(path: string, args: list<string>, isSync: bool, initiali
     omniCompletePending: false,
     completionTriggerChars: [],
     signaturePopup: -1,
+    typeHierPopup: -1,
+    typeHierFilePopup: -1,
     diagsMap: {},
     workspaceSymbolPopup: -1,
     workspaceSymbolQuery: '',
@@ -1552,6 +1589,7 @@ export def NewLspServer(path: string, args: list<string>, isSync: bool, initiali
     textDocFormat: function(TextDocFormat, [lspserver]),
     incomingCalls: function(IncomingCalls, [lspserver]),
     outgoingCalls: function(OutgoingCalls, [lspserver]),
+    typeHierarchy: function(TypeHiearchy, [lspserver]),
     renameSymbol: function(RenameSymbol, [lspserver]),
     codeAction: function(CodeAction, [lspserver]),
     workspaceQuery: function(WorkspaceQuerySymbols, [lspserver]),
index 82a087f1255234373b4c62012a26b00f2ff60e9b..17db79404afe1d57d883e9fd3f30e3d7634ab969 100644 (file)
@@ -229,11 +229,6 @@ enddef
 
 # Display or peek symbol references in a location list
 export def ShowReferences(lspserver: dict<any>, refs: list<dict<any>>, peekSymbol: bool)
-  if refs->empty()
-    util.WarnMsg('Error: No references found')
-    return
-  endif
-
   # create a location list with the location of the references
   var qflist: list<dict<any>> = []
   for loc in refs
@@ -314,49 +309,6 @@ def PeekSymbolLocation(lspserver: dict<any>, fname: string,
   win_execute(pwid, cmds, 'silent!')
 enddef
 
-# 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()
-      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
-      endif
-    endif
-  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.
@@ -368,7 +320,7 @@ export def GotoSymbol(lspserver: dict<any>, location: dict<any>,
   else
     # Save the current cursor location in the tag stack.
     util.PushCursorToTagStack()
-    JumpToSymbolLocation(lspserver, fname, location, cmdmods)
+    util.JumpToLspLocation(fname, location, cmdmods)
   endif
 enddef
 
diff --git a/autoload/lsp/typehierarchy.vim b/autoload/lsp/typehierarchy.vim
new file mode 100644 (file)
index 0000000..ee5d72e
--- /dev/null
@@ -0,0 +1,174 @@
+vim9script
+
+# Functions for dealing with type hierarchy (super types/sub types)
+
+import './util.vim'
+import './symbol.vim'
+
+# Parse the type hierarchy in 'typeHier' and displays a tree of type names
+# in the current buffer.  This function is called recursively to display the
+# super/sub type hierarchy.
+#
+# Returns the line number where the next type name should be added.
+def TypeTreeGenerate(super: bool, typeHier: dict<any>, pfx_arg: string,
+                       typeTree: list<string>, typeUriMap: list<dict<any>>)
+
+  var itemHasChildren = false
+  if super
+    if typeHier->has_key('parents') && !typeHier.parents->empty()
+      itemHasChildren = true
+    endif
+  else
+    if typeHier->has_key('children') && !typeHier.children->empty()
+      itemHasChildren = true
+    endif
+  endif
+
+  var itemBranchPfx: string
+  if itemHasChildren
+    itemBranchPfx = '▾ '
+  else
+    itemBranchPfx = pfx_arg->empty() ? '' : '  '
+  endif
+
+  var typestr: string
+  var kindstr = symbol.SymbolKindToName(typeHier.kind)
+  if kindstr != ''
+    typestr = $'{pfx_arg}{itemBranchPfx}{typeHier.name} ({kindstr[0]})'
+  else
+    typestr = $'{pfx_arg}{itemBranchPfx}{typeHier.name}'
+  endif
+  typeTree->add(typestr)
+  typeUriMap->add(typeHier)
+
+  # last item to process
+  if !itemHasChildren
+    return
+  endif
+
+  var items: list<dict<any>>
+  items = super ? typeHier.parents : typeHier.children
+
+  for item in items
+    TypeTreeGenerate(super, item, pfx_arg .. '| ', typeTree, typeUriMap)
+  endfor
+enddef
+
+# Display a popup with the file containing a type and highlight the line and
+# the type name.
+def UpdateTypeHierFileInPopup(lspserver: dict<any>, typeUriMap: list<dict<any>>)
+  if lspserver.typeHierPopup->winbufnr() == -1
+    return
+  endif
+
+  lspserver.typeHierFilePopup->popup_close()
+
+  var n = line('.', lspserver.typeHierPopup) - 1
+  var fname: string = util.LspUriToFile(typeUriMap[n].uri)
+
+  var bnr = fname->bufadd()
+  if bnr == 0
+    return
+  endif
+
+  var popupAttr = {
+    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.typeHierFilePopup = popup_create(bnr, popupAttr)
+  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
+  var end_col = util.GetLineByteFromPos(bnr, typeUriMap[n].selectionRange.end)
+  var pos = [[typeUriMap[n].selectionRange.start.line + 1,
+            start_col, end_col - start_col + 1]]
+  matchaddpos('Search', pos, 10, -1, {window: lspserver.typeHierFilePopup})
+enddef
+
+def TypeHierPopupFilter(lspserver: dict<any>, typeUriMap: list<dict<any>>,
+                       popupID: number, key: string): bool
+  popupID->popup_filter_menu(key)
+  if lspserver.typeHierPopup->winbufnr() == -1
+    # popup is closed
+    if lspserver.typeHierFilePopup->winbufnr() != -1
+      lspserver.typeHierFilePopup->popup_close()
+    endif
+    lspserver.typeHierFilePopup = -1
+    lspserver.typeHierPopup = -1
+  else
+    UpdateTypeHierFileInPopup(lspserver, typeUriMap)
+  endif
+
+  return true
+enddef
+
+def TypeHierPopupCallback(lspserver: dict<any>, typeUriMap: list<dict<any>>,
+                         popupID: number, selIdx: number)
+  if lspserver.typeHierFilePopup->winbufnr() != -1
+    lspserver.typeHierFilePopup->popup_close()
+  endif
+  lspserver.typeHierFilePopup = -1
+  lspserver.typeHierPopup = -1
+
+  if selIdx <= 0
+    # popup is canceled
+    return
+  endif
+
+  var item = typeUriMap[selIdx - 1]
+  var fname = util.LspUriToFile(item.uri)
+  # Save the current cursor location in the tag stack.
+  util.PushCursorToTagStack()
+  util.JumpToLspLocation(fname, item, '')
+enddef
+
+# Show the super or sub type hierarchy items 'types' as a tree in a popup window
+export def ShowTypeHierarchy(lspserver: dict<any>, super: bool, types: dict<any>)
+
+  if lspserver.typeHierPopup->winbufnr() != -1
+    # If the type hierarchy popup window is already present, close it.
+    lspserver.typeHierPopup->popup_close()
+  endif
+
+  var typeTree: list<string>
+  var typeUriMap: list<dict<any>>
+
+  # Generate a tree of the type hierarchy items
+  TypeTreeGenerate(super, types, '', typeTree, typeUriMap)
+
+  # Display a popup window with the type hierarchy tree and a popup window for
+  # the file.
+  var popupAttr = {
+      title: $'{super ? "Super" : "Sub"}Type Hierarchy',
+      wrap: 0,
+      pos: 'topleft',
+      line: 'cursor+1',
+      col: winwidth(0) - 34,
+      minheight: 10,
+      maxheight: 10,
+      minwidth: 30,
+      maxwidth: 30,
+      mapping: false,
+      fixed: 1,
+      filter: function(TypeHierPopupFilter, [lspserver, typeUriMap]),
+      callback: function(TypeHierPopupCallback, [lspserver, typeUriMap])
+    }
+  lspserver.typeHierPopup = popup_menu(typeTree, popupAttr)
+  UpdateTypeHierFileInPopup(lspserver, typeUriMap)
+enddef
+
+# vim: tabstop=8 shiftwidth=2 softtabstop=2
index 7cd4c40cd82b913611b82b74fef5ef23513e0789..1af97b9bae03329b5c81653c80bcca1db20797f9 100644 (file)
@@ -150,4 +150,47 @@ export def PushCursorToTagStack()
                         }]}, 't')
 enddef
 
+# Jump to the LSP 'location' in file 'fname'.  The user specified
+# window command modifiers (e.g. topleft) are in 'cmdmods'.
+export def JumpToLspLocation(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()
+      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
+      endif
+    endif
+  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
+
 # vim: tabstop=8 shiftwidth=2 softtabstop=2
index 724464e3358bc55a5cf34f28a3a95444d76b4466..c5678369d58a74f67923c22b0673c349c22de6ee 100644 (file)
@@ -2,7 +2,7 @@
 
 Author: Yegappan Lakshmanan  (yegappan AT yahoo DOT com)
 For Vim version 9.0 and above
-Last change: Oct 30, 2022
+Last change: Nov 19, 2022
 
 ==============================================================================
                                                *lsp-license*
@@ -68,66 +68,68 @@ To use this plugin, add the following line to your .vimrc file:
 
 The following commands are provided:
 
-:LspShowServers                Display the list of registered LSP servers
-:LspGotoDefinition     Go to the definition of the symbol under cursor
-:LspGotoDeclaration    Go to the declaration of the symbol under cursor
-:LspGotoTypeDef                Go to the type definition of the symbol under cursor
-:LspGotoImpl           Go to the implementation of the symbol under cursor
-:LspPeekDefinition     Open the definition of the symbol under cursor in a
-                       popup window.
-:LspPeekDeclaration    Open the declaration of the symbol under cursor in a
-                       popup window.
-:LspPeekTypeDef                Open the type definition of the symbol under cursor in
-                       a popup window.
-:LspPeekImpl           Open the implementation of the symbol under cursor in
-                       a popup window.
-:LspShowSignature      Display the signature of the symbol under cursor.
-:LspDiagShow           Display the diagnostics messages from the LSP server
-                       for the current buffer in a location list.
+:LspCodeAction         Apply the code action supplied by the LSP server to
+                       the diagnostic in the current line.
+:LspDiagCurrent                Display the diagnostic message for the current line.
 :LspDiagFirst          Jump to the first diagnostic message for the current
                        buffer.
+:LspDiagHighlightDisable
+                       Disable highlighting lines with a diagnostic message
+                       for the current Vim session.
+:LspDiagHighlightEnable        Enable highlighting lines with a diagnostic message
+                       for the current Vim session.
 :LspDiagNext           Jump to the next diagnostic message for the current
                        buffer after the current line.
 :LspDiagPrev           Jump to the previous diagnostic message for the current
                        buffer before the current line.
-:LspDiagCurrent                Display the diagnostic message for the current line.
-:LspDiagHighlightEnable        Enable highlighting lines with a diagnostic message
-                       for the current Vim session.
-:LspDiagHighlightDisable
-                       Disable highlighting lines with a diagnostic message
-                       for the current Vim session.
-:LspShowReferences     Display the list of references to the keyword under
-                       cursor in a new location list.
-:LspPeekReferences     Display the list of references to the keyword under
-                       cursor in a location list associated with the preview
-                       window.
-:LspSwitchSourceHeader Switch between source and header files.
-:LspHighlight          Highlight all the matches for the keyword under cursor
-:LspHighlightClear     Clear all the matches highlighted by :LspHighlight
-:LspOutline            Show the list of symbols defined in the current file
-                       in a separate window.
+:LspDiagShow           Display the diagnostics messages from the LSP server
+                       for the current buffer in a location list.
+:LspFold               Fold the current file
 :LspFormat             Format the current file using the LSP server.
 :{range}LspFormat      Format the specified range of lines.
+:LspGotoDeclaration    Go to the declaration of the symbol under cursor
+:LspGotoDefinition     Go to the definition of the symbol under cursor
+:LspGotoImpl           Go to the implementation of the symbol under cursor
+:LspGotoTypeDef                Go to the type definition of the symbol under cursor
+:LspHighlight          Highlight all the matches for the keyword under cursor
+:LspHighlightClear     Clear all the matches highlighted by :LspHighlight
 :LspIncomingCalls      Display the list of symbols calling the current symbol
                        in a new location list.
 :LspOutgoingCalls      Display the list of symbols called by the current
                        symbol in a new location list.
+:LspOutline            Show the list of symbols defined in the current file
+                       in a separate window.
+:LspPeekDeclaration    Open the declaration of the symbol under cursor in a
+                       popup window.
+:LspPeekDefinition     Open the definition of the symbol under cursor in a
+                       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.
+:LspPeekTypeDef                Open the type definition of the symbol under cursor in
+                       a popup window.
 :LspRename             Rename the current symbol
-:LspCodeAction         Apply the code action supplied by the LSP server to
-                       the diagnostic in the current line.
-:LspSymbolSearch       Perform a workspace wide search for a symbol
 :LspSelectionExpand    Expand the current symbol range visual selection
 :LspSelectionShrink    Shrink the current symbol range visual selection
-:LspFold               Fold the current file
+:LspServerRestart      Restart the LSP server for the current buffer.
+:LspShowReferences     Display the list of references to the keyword under
+                       cursor in a new location list.
+:LspShowServers                Display the list of registered LSP servers
+:LspShowServerCapabilities
+                       Display the list of capabilities of a LSP server.
+:LspShowSignature      Display the signature of the symbol under cursor.
+:LspSubTypeHierarchy   Display the sub type hierarchy in a popup window.
+:LspSuperTypeHierarchy Display the super type hierarchy in a popup window.
+:LspSwitchSourceHeader Switch between source and header files.
+:LspSymbolSearch       Perform a workspace wide search for a symbol
 :LspWorkspaceAddFolder {folder}
                        Add a folder to the workspace
-:LspWorkspaceRemoveFolder {folder}
-                       Remove a folder from the workspace
 :LspWorkspaceListFolders
                        Show the list of folders in the workspace
-:LspShowServerCapabilities
-                       Display the list of capabilities of a LSP server.
-:LspServerRestart      Restart the LSP server for the current buffer.
+:LspWorkspaceRemoveFolder {folder}
+                       Remove a folder from the workspace
 
 ==============================================================================
 4. Configuration                               *lsp-configuration*
@@ -540,6 +542,20 @@ To get a particular option value you can use the following: >
                        You can enter a new search pattern to do a workspace
                        wide symbol search.
 
+                                               *:LspSuperTypeHierarchy*
+:LspSuperTypeHierarchy Show the super type hierarchy for the symbol under the
+                       cursor in a popup window.  The file containing the
+                       type is shown in another popup window.  You can jump
+                       to the location where a type is defined by browsing the
+                       popup menu and selecting an entry.
+
+                                               *:LspSubTypeHierarchy*
+:LspSubTypeHierarchy   Show the sub type hierarchy for the symbol under the
+                       cursor in a popup window.  The file containing the
+                       type is shown in another popup window.  You can jump
+                       to the location where a type is defined by browsing the
+                       popup menu and selecting an entry.
+
                                                *:LspHover*
 :LspHover              Show the documentation for the symbol under the cursor
                        in a popup window. If you want to show the symbol
index 342e45959e7e779d4faabfc0a8c751ec0ad2c588..31b5eb5f951fc99c8c125ac8bb9818e70bab4c2b 100644 (file)
@@ -109,6 +109,8 @@ command! -nargs=0 -bar LspOutline lsp.Outline()
 command! -nargs=0 -bar -range=% LspFormat lsp.TextDocFormat(<range>, <line1>, <line2>)
 command! -nargs=0 -bar LspOutgoingCalls lsp.OutgoingCalls()
 command! -nargs=0 -bar LspIncomingCalls lsp.IncomingCalls()
+command! -nargs=0 -bar LspSuperTypeHierarchy lsp.TypeHierarchy(1)
+command! -nargs=0 -bar LspSubTypeHierarchy lsp.TypeHierarchy(0)
 command! -nargs=0 -bar LspRename lsp.Rename()
 command! -nargs=0 -bar -range LspCodeAction lsp.CodeAction(<line1>, <line2>)
 command! -nargs=? -bar LspSymbolSearch lsp.SymbolSearch(<q-args>)