]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Add support for displaying signature help
authorYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 10 Dec 2020 18:09:11 +0000 (10:09 -0800)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 10 Dec 2020 18:09:11 +0000 (10:09 -0800)
autoload/lsp.vim
plugin/lsp.vim

index 897650bd2926765773b72093b5c2d1a2ad757785..aa784c46f407e4432c7e7683664b95f62d949843 100644 (file)
@@ -4,22 +4,77 @@ vim9script
 
 var lsp_servers: dict<dict<any>> = {}
 
-def lsp#process_reply(req: dict<any>, reply: dict<any>): void
-  if req.method == 'textDocument/definition' || req.method == 'textDocument/declaration'
-    if reply.result->len() == 0
-      echomsg "Error: definition is not found"
-    else
-      var result: dict<any> = reply.result[0]
-      var file = result.uri[7:]
-      var wid = bufwinid(file)
-      if wid != -1
-        win_gotoid(wid)
-      else
-        exe 'split ' .. file
-      endif
-      cursor(result.range.start.line + 1, result.range.start.character + 1)
-      redraw!
-    endif
+var lsp_log_dir: string = '/tmp/'
+
+# process the initialize reply from the LSP server
+def lsp#processInitializeReply(ftype: string, reply: dict<any>): void
+  if reply.result->len() <= 0
+    return
+  endif
+
+  var caps: dict<any> = reply.result.capabilities
+  lsp_servers[ftype].caps = caps
+  if caps->has_key('signatureHelpProvider')
+    var triggers = caps.signatureHelpProvider.triggerCharacters
+    for ch in triggers
+      exe 'inoremap <silent> ' .. ch .. ' ' .. ch .. "<C-R>=lsp#show_signature()<CR>"
+    endfor
+  endif
+enddef
+
+# process the 'textDocument/definition'/'textDocument/declaration' replies
+# from the LSP server
+def lsp#processDefDeclReply(reply: dict<any>): void
+  if reply.result->len() == 0
+    echomsg "Error: definition is not found"
+    return
+  endif
+
+  var result: dict<any> = reply.result[0]
+  var file = result.uri[7:]
+  var wid = bufwinid(file)
+  if wid != -1
+    win_gotoid(wid)
+  else
+    exe 'split ' .. file
+  endif
+  cursor(result.range.start.line + 1, result.range.start.character + 1)
+  redraw!
+enddef
+
+# process the signatureHelp reply from the LSP server
+def lsp#process_signatureHelp_reply(reply: dict<any>): void
+  var result: dict<any> = reply.result
+  if result.signatures->len() <= 0
+    return
+  endif
+
+  var sig: dict<any> = result.signatures[result.activeSignature]
+  var text = sig.label
+  var hllen = 0
+  var startcol = 0
+  var params_len = sig.parameters->len()
+  if params_len > 0 && result.activeParameter < params_len
+    var label = sig.parameters[result.activeParameter].label
+    hllen = label->len()
+    startcol = text->stridx(label)
+  endif
+  var popupID = popup_atcursor(text, {})
+  prop_type_add('signature', {'bufnr': popupID->winbufnr(),
+  'highlight': 'Title'})
+  if hllen > 0
+    prop_add(1, startcol + 1, {'bufnr': popupID->winbufnr(), 'length': hllen, 'type': 'signature'})
+  endif
+enddef
+
+# Process varous reply messages from the LSP server
+def lsp#process_reply(ftype: string, req: dict<any>, reply: dict<any>): void
+  if req.method == 'initialize'
+    lsp#processInitializeReply(ftype, reply)
+  elseif req.method == 'textDocument/definition' || req.method == 'textDocument/declaration'
+    lsp#processDefDeclReply(reply)
+  elseif req.method == 'textDocument/signatureHelp'
+    lsp#process_signatureHelp_reply(reply)
   endif
 enddef
 
@@ -61,7 +116,7 @@ def lsp#process_server_msg(ftype: string): void
       if reply->has_key('error')
         echomsg "Error: request " .. req.method .. " failed (" .. reply.error.message .. ")"
       else
-        lsp#process_reply(req, reply)
+        lsp#process_reply(ftype, req, reply)
       endif
     endif
 
@@ -70,13 +125,13 @@ def lsp#process_server_msg(ftype: string): void
 enddef
 
 def lsp#output_cb(ftype: string, chan: channel, msg: string): void
-  #writefile(split(msg, "\n"), 'lsp_server.out', 'a')
+  writefile(split(msg, "\n"), lsp_log_dir .. 'lsp_server.out', 'a')
   lsp_servers[ftype].data = lsp_servers[ftype].data .. msg
   lsp#process_server_msg(ftype)
 enddef
 
 def lsp#error_cb(ftype: string, chan: channel, emsg: string,): void
-  #writefile(split(emsg, "\n"), 'lsp_server.err', 'a')
+  writefile(split(emsg, "\n"), lsp_log_dir .. 'lsp_server.err', 'a')
 enddef
 
 def lsp#exit_cb(ftype: string, job: job, status: number): void
@@ -96,7 +151,9 @@ def lsp#sendto_server(ftype: string, content: dict<any>): void
   var msg = "Content-Length: " .. req_js->len() .. "\r\n\r\n"
   var ch = lsp_servers[ftype].job->job_getchannel()
   ch->ch_sendraw(msg)
+  call writefile(["req_js length = " .. req_js->len()], "lsp_trace.txt", 'a')
   ch->ch_sendraw(req_js)
+  call writefile(["After sending data"], "lsp_trace.txt", 'a')
 enddef
 
 def lsp#create_reqmsg(ftype: string, method: string): dict<any>
@@ -145,9 +202,10 @@ def lsp#start_server(ftype: string): number
               'err_cb': function('lsp#error_cb', [ftype]),
               'exit_cb': function('lsp#exit_cb', [ftype])}
 
-  #writefile([], 'lsp_server.out')
-  #writefile([], 'lsp_server.err')
+  writefile([], lsp_log_dir .. 'lsp_server.out')
+  writefile([], lsp_log_dir .. 'lsp_server.err')
   lsp_servers[ftype].data = ''
+  lsp_servers[ftype].caps = {}
   lsp_servers[ftype].nextID = 1
   lsp_servers[ftype].requests = {}
 
@@ -173,7 +231,7 @@ enddef
 
 # Send a 'exit' notification to the LSP server
 def lsp#exit_server(ftype: string): void
-  var req = lsp#create_notifmsg(ftype, 'exit')
+  var req: dict<any> = lsp#create_notifmsg(ftype, 'exit')
   lsp#sendto_server(ftype, req)
 enddef
 
@@ -200,7 +258,7 @@ enddef
 
 # Send a LSP "textDocument/didOpen" notification
 def lsp#textdoc_didopen(fname: string, ftype: string): void
-  var notif = lsp#create_notifmsg(ftype, 'textDocument/didOpen')
+  var notif: dict<any> = lsp#create_notifmsg(ftype, 'textDocument/didOpen')
 
   # interface DidOpenTextDocumentParams
   # interface TextDocumentItem
@@ -216,7 +274,7 @@ enddef
 
 # Send a LSP "textDocument/didClose" notification
 def lsp#textdoc_didclose(fname: string, ftype: string): void
-  var notif = lsp#create_notifmsg(ftype, 'textDocument/didClose')
+  var notif: dict<any> = lsp#create_notifmsg(ftype, 'textDocument/didClose')
 
   # interface DidCloseTextDocumentParams
   #   interface TextDocumentIdentifier
@@ -279,6 +337,65 @@ def lsp#goto_declaration(fname: string, ftype: string, lnum: number, col: number
   lsp#sendto_server(ftype, req)
 enddef
 
+# Get the signature using "textDocument/signatureHelp" LSP request
+def lsp#show_signature(): string
+
+  # first send all the changes to the current buffer to the LSP server
+  listener_flush()
+
+  var fname: string = expand('%:p')
+  var ftype: string = &filetype
+  var lnum: number = line('.') - 1
+  var col: number = col('.') - 1
+
+  if fname == '' || ftype == ''
+    return ''
+  endif
+  if !lsp_servers->has_key(ftype)
+    echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not found'
+    return ''
+  endif
+  if !lsp_servers[ftype].running
+    echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not running'
+    return ''
+  endif
+
+  var req = lsp#create_reqmsg(ftype, 'textDocument/signatureHelp')
+  # interface SignatureHelpParams
+  #   interface TextDocumentPositionParams
+  #     interface TextDocumentIdentifier
+  req.params->extend({'textDocument': {'uri': 'file://' .. fname}})
+  #     interface Position
+  req.params->extend({'position': {'line': lnum, 'character': col}})
+
+  lsp#sendto_server(ftype, req)
+  return ''
+enddef
+
+# buffer change notification listener
+def lsp#bufchange_listener(bnum: number, start: number, end: number, added: number, changes: list<dict<number>>)
+  var ftype = getbufvar(bnum, '&filetype')
+  var notif: dict<any> = lsp#create_notifmsg(ftype, 'textDocument/didChange')
+
+  # interface DidChangeTextDocumentParams
+  #   interface VersionedTextDocumentIdentifier
+  var vtdid: dict<any> = {}
+  vtdid.uri = 'file://' .. fnamemodify(bufname(bnum), ':p')
+  # Use Vim 'changedtick' as the LSP document version number
+  vtdid.version = getbufvar(bnum, 'changedtick')
+  notif.params->extend({'textDocument': vtdid})
+  #   interface TextDocumentContentChangeEvent
+  var changeset: list<dict<any>> = []
+  #     Range
+  var range: dict<dict<number>> = {}
+  range.start = {'line': start - 1, 'character': 0}
+  range.end = {'line': end - 2, 'character': 0}
+  changeset->add({'range': range, 'text': getbufline(bnum, start, end - 1)->join("\n")})
+  notif.params->extend({'contentChanges': changeset})
+
+  lsp#sendto_server(ftype, notif)
+enddef
+
 def lsp#add_file(fname: string, ftype: string): void
   if fname == '' || ftype == '' || !lsp_servers->has_key(ftype)
     return
@@ -287,6 +404,12 @@ def lsp#add_file(fname: string, ftype: string): void
     lsp#start_server(ftype)
   endif
   lsp#textdoc_didopen(fname, ftype)
+
+  # add a listener to track changes to this buffer
+  var bnum = bufnr(fname)
+  if bnum != -1
+    listener_add(function('lsp#bufchange_listener'), bnum)
+  endif
 enddef
 
 def lsp#remove_file(fname: string, ftype: string): void
@@ -315,6 +438,7 @@ def lsp#add_server(ftype: string, serverpath: string, args: list<string>)
     'job': v:none,
     'data': '',
     'nextID': 1,
+    'caps': {},
     'requests': {}    # outstanding LSP requests
   }
   lsp_servers->extend({[ftype]: sinfo})
index 442a7248c9d68a7157659bbbc6091ca46920c55d..811efdda4275bbb6c9da54167641e055683c55a5 100644 (file)
@@ -11,4 +11,5 @@ autocmd BufWipeOut * call lsp#remove_file(expand('<afile>:p'), &filetype)
 
 command! -nargs=0 LspGotoDefinition call lsp#goto_definition(expand('%:p'), &filetype, line('.') - 1, col('.') - 1)
 command! -nargs=0 LspGotoDeclaration call lsp#goto_declaration(expand('%:p'), &filetype, line('.') - 1, col('.') - 1)
+command! -nargs=0 LspGetSignature call lsp#show_signature('')