]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Add support for the LspCalling and LspCalledBy commands
authorYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 3 Feb 2022 03:00:16 +0000 (19:00 -0800)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 3 Feb 2022 03:00:16 +0000 (19:00 -0800)
README.md
autoload/callhierarchy.vim [new file with mode: 0644]
autoload/handlers.vim
autoload/lsp.vim
autoload/lspoptions.vim
autoload/lspserver.vim
doc/lsp.txt

index 39edd9b182067d431070cc76fc98d8796dd7a4f5..29bbb170eec72be882df97e894d0fcfb80ace53f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -99,8 +99,8 @@ Command|Description
 :LspOutline|Show the list of symbols defined in the current file in a separate window.
 :LspFormat|Format the current file using the LSP server.
 :{range}LspFormat|Format the specified range of lines.
-:LspCalledBy|Display the list of symbols called by the current symbol. (NOT IMPLEMENTED YET).
-:LspCalling|Display the list of symbols calling the current symbol (NOT IMPLEMENTED YET).
+:LspCalledBy|Display the list of symbols called by the current symbol.
+:LspCalling|Display the list of symbols calling the current symbol.
 :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
diff --git a/autoload/callhierarchy.vim b/autoload/callhierarchy.vim
new file mode 100644 (file)
index 0000000..9d20dd7
--- /dev/null
@@ -0,0 +1,89 @@
+vim9script
+
+# Functions for dealing with call hierarchy (incoming/outgoing calls)
+
+var util = {}
+if has('patch-8.2.4019')
+  import './util.vim' as util_import
+
+  util.WarnMsg = util_import.WarnMsg
+  util.LspUriToFile = util_import.LspUriToFile
+  util.GetLineByteFromPos = util_import.GetLineByteFromPos
+else
+  import {WarnMsg,
+         LspUriToFile,
+         GetLineByteFromPos} from './util.vim'
+
+  util.WarnMsg = WarnMsg
+  util.LspUriToFile = LspUriToFile
+  util.GetLineByteFromPos = GetLineByteFromPos
+endif
+
+def s:createLoclistWithCalls(calls: list<dict<any>>, incoming: bool)
+  var qflist: list<dict<any>> = []
+
+  for item in calls
+    var fname: string
+    if incoming
+      fname = util.LspUriToFile(item.from.uri)
+    else
+      fname = util.LspUriToFile(item.to.uri)
+    endif
+    var bnr: number = fname->bufnr()
+    if bnr == -1
+      bnr = fname->bufadd()
+    endif
+    if !bnr->bufloaded()
+      bnr->bufload()
+    endif
+
+    var name: string
+    if incoming
+      name = item.from.name
+    else
+      name = item.to.name
+    endif
+
+    if incoming
+      for r in item.fromRanges
+        var text: string =
+                               bnr->getbufline(r.start.line + 1)[0]->trim("\t ", 1)
+        qflist->add({filename: fname,
+                       lnum: r.start.line + 1,
+                       col: util.GetLineByteFromPos(bnr, r.start) + 1,
+                       text: name .. ': ' .. text})
+      endfor
+    else
+      var pos: dict<any> = item.to.range.start
+      var text: string = bnr->getbufline(pos.line + 1)[0]->trim("\t ", 1)
+      qflist->add({filename: fname,
+                       lnum: item.to.range.start.line + 1,
+                       col: util.GetLineByteFromPos(bnr, pos) + 1,
+                       text: name .. ': ' .. text})
+    endif
+  endfor
+  var save_winid = win_getid()
+  setloclist(0, [], ' ', {title: 'Incoming Calls', items: qflist})
+  lopen
+  save_winid->win_gotoid()
+enddef
+
+export def IncomingCalls(calls: list<dict<any>>)
+  if calls->empty()
+    util.WarnMsg('No incoming calls')
+    return
+  endif
+
+  s:createLoclistWithCalls(calls, true)
+enddef
+
+export def OutgoingCalls(calls: list<dict<any>>)
+  if calls->empty()
+    util.WarnMsg('No outgoing calls')
+    return
+  endif
+
+  s:createLoclistWithCalls(calls, false)
+enddef
+
+# vim: shiftwidth=2 softtabstop=2
index aa8265917336c8db4d61ec5a93a75485c623bcc5..43cf2650ea04aa760d867b8d1df705670b027b20 100644 (file)
@@ -11,6 +11,7 @@ var outline = {}
 var textedit = {}
 var symbol = {}
 var codeaction = {}
+var callhier = {}
 
 if has('patch-8.2.4019')
   import './lspoptions.vim' as opt_import
@@ -20,6 +21,7 @@ if has('patch-8.2.4019')
   import './textedit.vim' as textedit_import
   import './symbol.vim' as symbol_import
   import './codeaction.vim' as codeaction_import
+  import './callhierarchy.vim' as callhierarchy_import
 
   opt.lspOptions = opt_import.lspOptions
   util.WarnMsg = util_import.WarnMsg
@@ -34,6 +36,8 @@ if has('patch-8.2.4019')
   symbol.ShowReferences = symbol_import.ShowReferences
   symbol.GotoSymbol = symbol_import.GotoSymbol
   codeaction.ApplyCodeAction = codeaction_import.ApplyCodeAction
+  callhier.IncomingCalls = callhierarchy_import.IncomingCalls
+  callhier.OutgoingCalls = callhierarchy_import.OutgoingCalls
 else
   import lspOptions from './lspoptions.vim'
   import {WarnMsg,
@@ -46,6 +50,7 @@ else
   import {ApplyTextEdits, ApplyWorkspaceEdit} from './textedit.vim'
   import {ShowReferences, GotoSymbol} from './symbol.vim'
   import ApplyCodeAction from './codeaction.vim'
+  import {IncomingCalls, OutgoingCalls} from './callhierarchy.vim'
 
   opt.lspOptions = lspOptions
   util.WarnMsg = WarnMsg
@@ -60,6 +65,8 @@ else
   symbol.ShowReferences = ShowReferences
   symbol.GotoSymbol = GotoSymbol
   codeaction.ApplyCodeAction = ApplyCodeAction
+  callhier.IncomingCalls = IncomingCalls
+  callhier.OutgoingCalls = OutgoingCalls
 endif
 
 # process the 'initialize' method reply from the LSP server
@@ -677,19 +684,43 @@ enddef
 # Result: CallHierarchyItem[] | null
 def s:processPrepareCallHierarchy(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
   if reply.result->empty()
+    if lspserver.callHierarchyType == 'incoming'
+      util.WarnMsg('No incoming calls')
+    else
+      util.WarnMsg('No outgoing calls')
+    endif
     return
   endif
 
-  var items: list<string> = ['Select a Call Hierarchy Item:']
-  for i in range(reply.result->len())
-    items->add(printf("%d. %s", i + 1, reply.result[i].name))
-  endfor
-  var choice = inputlist(items)
-  if choice < 1 || choice > items->len()
-    return
+  var choice: number = 1
+  if reply.result->len() > 1
+    var items: list<string> = ['Select a Call Hierarchy Item:']
+    for i in range(reply.result->len())
+      items->add(printf("%d. %s", i + 1, reply.result[i].name))
+    endfor
+    choice = inputlist(items)
+    if choice < 1 || choice > items->len()
+      return
+    endif
   endif
 
-  echomsg reply.result[choice - 1]
+  if lspserver.callHierarchyType == 'incoming'
+    LspGetIncomingCalls(reply.result[choice - 1])
+  else
+    LspGetOutgoingCalls(reply.result[choice - 1])
+  endif
+enddef
+
+# process the 'callHierarchy/incomingCalls' reply from the LSP server
+# Result: CallHierarchyIncomingCall[] | null
+def s:processIncomingCalls(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
+  callhier.IncomingCalls(reply.result)
+enddef
+
+# process the 'callHierarchy/outgoingCalls' reply from the LSP server
+# Result: CallHierarchyOutgoingCall[] | null
+def s:processOutgoingCalls(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
+  callhier.OutgoingCalls(reply.result)
 enddef
 
 # Process various reply messages from the LSP server
@@ -715,7 +746,9 @@ export def ProcessReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>):
       'textDocument/foldingRange': function('s:processFoldingRangeReply'),
       'workspace/executeCommand': function('s:processWorkspaceExecuteReply'),
       'workspace/symbol': function('s:processWorkspaceSymbolReply'),
-      'textDocument/prepareCallHierarchy': function('s:processPrepareCallHierarchy')
+      'textDocument/prepareCallHierarchy': function('s:processPrepareCallHierarchy'),
+      'callHierarchy/incomingCalls': function('s:processIncomingCalls'),
+      'callHierarchy/outgoingCalls': function('s:processOutgoingCalls')
     }
 
   if lsp_reply_handlers->has_key(req.method)
index f51996e9e63e5310ceef5a4b81605dc46e6abb65..822c465da6f487e9aa411164018db5edd35aab0c 100644 (file)
@@ -734,14 +734,41 @@ export def IncomingCalls()
     return
   endif
 
+  lspserver.callHierarchyType = 'incoming'
   var fname: string = @%
-  lspserver.incomingCalls(fname)
+  lspserver.prepareCallHierarchy(fname)
 enddef
 
+def g:LspGetIncomingCalls(item: dict<any>)
+  var lspserver: dict<any> = s:curbufGetServerChecked()
+  if lspserver->empty()
+    return
+  endif
+
+  lspserver.incomingCalls(item)
+enddef
+
+def g:LspGetOutgoingCalls(item: dict<any>)
+  var lspserver: dict<any> = s:curbufGetServerChecked()
+  if lspserver->empty()
+    return
+  endif
+
+  lspserver.outgoingCalls(item)
+enddef
+
+
 # Display all the symbols used by the current symbol.
 # Uses LSP "callHierarchy/outgoingCalls" request
 export def OutgoingCalls()
-  :echomsg 'Error: Not implemented yet'
+  var lspserver: dict<any> = s:curbufGetServerChecked()
+  if lspserver->empty()
+    return
+  endif
+
+  lspserver.callHierarchyType = 'outgoing'
+  var fname: string = @%
+  lspserver.prepareCallHierarchy(fname)
 enddef
 
 # Rename a symbol
index 4b99d3781a475baba66abc586cfc6491beaeb4b3..b8ac3f24b1470e8e6a6d557cd341424fb2972ecc 100644 (file)
@@ -25,7 +25,8 @@ export var lspOptions: dict<any> = {
   outlineWinSize: 20,
   # Open outline window on right side
   outlineOnRight: false,
-  # Suppress diagnostic hover from appearing when the mouse is over the line instead of the signature
+  # Suppress diagnostic hover from appearing when the mouse is over the line
+  # instead of the signature
   noDiagHoverOnLine: true,
   # Show a diagnostic message on a status line
   showDiagOnStatusLine: false,
index 3b943730bed867365998144811044d87057fc293..c0ee844b179a1e8be9b8ba0285cfdb606a8131c4 100644 (file)
@@ -678,10 +678,10 @@ def s:textDocFormat(lspserver: dict<any>, fname: string, rangeFormat: bool,
   lspserver.sendMessage(req)
 enddef
 
-# Request: "callHierarchy/incomingCalls"
-# Param: CallHierarchyIncomingCallsParams
-def s:incomingCalls(lspserver: dict<any>, fname: string)
-  # Check whether LSP server supports incoming calls
+# Request: "textDocument/prepareCallHierarchy"
+# Param: CallHierarchyPrepareParams
+def s:prepareCallHierarchy(lspserver: dict<any>, fname: string)
+  # Check whether LSP server supports call hierarchy
   if !lspserver.caps->has_key('callHierarchyProvider')
                        || !lspserver.caps.callHierarchyProvider
     util.ErrMsg("Error: LSP server does not support call hierarchy")
@@ -696,6 +696,42 @@ def s:incomingCalls(lspserver: dict<any>, fname: string)
   lspserver.sendMessage(req)
 enddef
 
+# Request: "callHierarchy/incomingCalls"
+# Param: CallHierarchyItem
+def s:incomingCalls(lspserver: dict<any>, hierItem: dict<any>)
+  # Check whether LSP server supports call hierarchy
+  if !lspserver.caps->has_key('callHierarchyProvider')
+                       || !lspserver.caps.callHierarchyProvider
+    util.ErrMsg("Error: LSP server does not support call hierarchy")
+    return
+  endif
+
+  var req = lspserver.createRequest('callHierarchy/incomingCalls')
+
+  # interface CallHierarchyIncomingCallsParams
+  #   interface CallHierarchyItem
+  req.params->extend({item: hierItem})
+  lspserver.sendMessage(req)
+enddef
+
+# Request: "callHierarchy/outgoingCalls"
+# Param: CallHierarchyItem
+def s:outgoingCalls(lspserver: dict<any>, hierItem: dict<any>)
+  # Check whether LSP server supports call hierarchy
+  if !lspserver.caps->has_key('callHierarchyProvider')
+                       || !lspserver.caps.callHierarchyProvider
+    util.ErrMsg("Error: LSP server does not support call hierarchy")
+    return
+  endif
+
+  var req = lspserver.createRequest('callHierarchy/outgoingCalls')
+
+  # interface CallHierarchyOutgoingCallsParams
+  #   interface CallHierarchyItem
+  req.params->extend({item: hierItem})
+  lspserver.sendMessage(req)
+enddef
+
 # Request: "textDocument/rename"
 # Param: RenameParams
 def s:renameSymbol(lspserver: dict<any>, newName: string)
@@ -885,7 +921,8 @@ export def NewLspServer(path: string, args: list<string>): dict<any>
     diagsMap: {},
     workspaceSymbolPopup: 0,
     workspaceSymbolQuery: '',
-    peekSymbol: false
+    peekSymbol: false,
+    callHierarchyType: ''
   }
   # Add the LSP server functions
   lspserver->extend({
@@ -922,7 +959,9 @@ export def NewLspServer(path: string, args: list<string>): dict<any>
     docHighlight: function('s:docHighlight', [lspserver]),
     getDocSymbols: function('s:getDocSymbols', [lspserver]),
     textDocFormat: function('s:textDocFormat', [lspserver]),
+    prepareCallHierarchy: function('s:prepareCallHierarchy', [lspserver]),
     incomingCalls: function('s:incomingCalls', [lspserver]),
+    outgoingCalls: function('s:outgoingCalls', [lspserver]),
     renameSymbol: function('s:renameSymbol', [lspserver]),
     codeAction: function('s:codeAction', [lspserver]),
     workspaceQuery: function('s:workspaceQuerySymbols', [lspserver]),
index e4e284361eb6d3a70f74367db8e5d2b1397ff471..8bbce823147b58bfd6c0e85ff6c56292b7c60fe3 100644 (file)
@@ -2,7 +2,7 @@
 
 Author: Yegappan Lakshmanan  (yegappan AT yahoo DOT com)
 For Vim version 8.2.2342 and above
-Last change: Jan 21, 2022
+Last change: Feb 2, 2022
 
 ==============================================================================
                                                *lsp-license*
@@ -108,9 +108,9 @@ The following commands are provided:
 :LspFormat             Format the current file using the LSP server.
 :{range}LspFormat      Format the specified range of lines.
 :LspCalledBy           Display the list of symbols called by the current
-                       symbol. (NOT IMPLEMENTED YET).
+                       symbol in a new location list.
 :LspCalling            Display the list of symbols calling the current symbol
-                       (NOT IMPLEMENTED YET).
+                       in a new location list.
 :LspRename             Rename the current symbol
 :LspCodeAction         Apply the code action supplied by the LSP server to
                        the diagnostic in the current line.
@@ -384,12 +384,12 @@ diagnostic messages, you can add the following line to your .vimrc file:
                        file using the LSP server.
 
                                                *:LspCalledBy*
-:LspCalledBy           Display the list of symbols called by the current
-                       symbol. (NOT IMPLEMENTED YET).
+:LspCalledBy           Creates a new location list with the location of
+                       the list of symbols called by the current symbol.
 
                                                *:LspCalling*
-:LspCalling            Display the list of symbols calling the current symbol
-                       (NOT IMPLEMENTED YET).
+:LspCalling            Creates a new location list with the location of the
+                       list of symbols calling the current symbol.
 
                                                *:LspRename*
 :LspRename             Rename the current symbol. You will be prompted to