README.md | 1 + autoload/handlers.vim | 8 +++++++- autoload/lsp.vim | 38 +++++++++++++++++++++++++++++++++++--- autoload/lspserver.vim | 39 ++++++++++++++++++++++++++++----------- doc/lsp.txt | 15 ++++++++++----- plugin/lsp.vim | 6 ++++++ diff --git a/README.md b/README.md index 0f1614ade02293ed698756d56644cde38809d63f..380bfddeed123982110731dfc0ee0bae8dc5a2a1 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ :LspFold|Fold the current file :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 +:LspServerRestart|Restart the LSP server for the current buffer ## Similar Vim LSP Plugins diff --git a/autoload/handlers.vim b/autoload/handlers.vim index ce0b20c62a6099e4e50574c183482e370901d7de..2995634e1e2924100d131a8cfccce1134341503b 100644 --- a/autoload/handlers.vim +++ b/autoload/handlers.vim @@ -122,6 +122,11 @@ lspserver.getDocSymbols(@%) endif enddef +# Process a 'shutdown' reply from the LSP server. +def ProcessShutdownReply(lspserver: dict, req: dict, reply: dict): void + return +enddef + # process the 'textDocument/definition' / 'textDocument/declaration' / # 'textDocument/typeDefinition' and 'textDocument/implementation' replies from # the LSP server @@ -674,6 +679,7 @@ export def ProcessReply(lspserver: dict, req: dict, reply: dict): void var lsp_reply_handlers: dict = { 'initialize': function('ProcessInitializeReply'), + 'shutdown': function('ProcessShutdownReply'), 'textDocument/definition': function('ProcessDefDeclReply'), 'textDocument/declaration': function('ProcessDefDeclReply'), 'textDocument/typeDefinition': function('ProcessDefDeclReply'), @@ -700,7 +706,7 @@ if lsp_reply_handlers->has_key(req.method) lsp_reply_handlers[req.method](lspserver, req, reply) else - util.ErrMsg("Error: Unsupported reply received from LSP server: " .. reply->string()) + util.ErrMsg("Error: Unsupported reply received from LSP server: " .. reply->string() .. " for request: " .. req->string()) endif enddef diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 6005ec0c9c743f6feb156217f29b6367e728710e..85fe67b62603af557a5ce238692eb9eb7129e7fd 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -401,10 +401,12 @@ # Notify LSP server to remove a file export def RemoveFile(bnr: number): void var lspserver: dict = buf.BufLspServerGet(bnr) - if lspserver->empty() || !lspserver.running + if lspserver->empty() return endif - lspserver.textdocDidClose(bnr) + if lspserver.running + lspserver.textdocDidClose(bnr) + endif diag.DiagRemoveFile(lspserver, bnr) buf.BufLspServerRemove(bnr) enddef @@ -414,6 +416,35 @@ export def StopAllServers() for lspserver in lspServers if lspserver.running lspserver.stopServer() + endif + endfor +enddef + +# Restart the LSP server for the current buffer +export def RestartServer() + var lspserver: dict = CurbufGetServerChecked() + if lspserver->empty() + return + endif + + # Stop the server + lspserver.stopServer() + + # Remove all the buffers with the same file type as the current buffer + var ftype: string = &filetype + for binfo in getbufinfo() + if getbufvar(binfo.bufnr, '&filetype') == ftype + RemoveFile(binfo.bufnr) + endif + endfor + + # Start the server again + lspserver.startServer(true) + + # Add all the buffers with the same file type as the current buffer + for binfo in getbufinfo({bufloaded: 1}) + if getbufvar(binfo.bufnr, '&filetype') == ftype + AddFile(binfo.bufnr) endif endfor enddef @@ -779,7 +810,8 @@ if lspserver->empty() return endif - var newName: string = input("Rename symbol: ", expand('')) + var sym: string = expand('') + var newName: string = input("Rename symbol '" .. sym .. "' to: ", sym) if newName == '' return endif diff --git a/autoload/lspserver.vim b/autoload/lspserver.vim index 6d9f1e895be5d6ec04fd15c808ab5470eab14c00..2077722dfac816d732908e26f2235c97d2b84639 100644 --- a/autoload/lspserver.vim +++ b/autoload/lspserver.vim @@ -70,14 +70,16 @@ # LSP server exit callback def Exit_cb(lspserver: dict, job: job, status: number): void util.WarnMsg("LSP server exited with status " .. status) - lspserver.job = v:null lspserver.running = false lspserver.ready = false lspserver.requests = {} enddef # Start a LSP server -def StartServer(lspserver: dict): number +# +# If 'isSync' is true, then waits for the server to send the initialize +# reponse message. +def StartServer(lspserver: dict, isSync: bool = false): number if lspserver.running util.WarnMsg("LSP server for is already running") return 0 @@ -109,20 +111,23 @@ util.ErrMsg("Error: Failed to start LSP server " .. lspserver.path) return 1 endif - # wait for the LSP server to start + # wait a little for the LSP server to start sleep 10m lspserver.job = job lspserver.running = true - lspserver.initServer() + lspserver.initServer(isSync) return 0 enddef # Request: 'initialize' # Param: InitializeParams -def InitServer(lspserver: dict) +# +# If 'isSync' is true, then waits for the server to send the initialize +# reponse message. +def InitServer(lspserver: dict, isSync: bool = false) var req = lspserver.createRequest('initialize') # client capabilities (ClientCapabilities) @@ -171,6 +176,9 @@ initparams.capabilities = clientCaps req.params->extend(initparams) lspserver.sendMessage(req) + if isSync + lspserver.waitForReponse(req) + endif enddef # Send a "initialized" LSP notification @@ -185,6 +193,7 @@ # Param: void def ShutdownServer(lspserver: dict): void var req = lspserver.createRequest('shutdown') lspserver.sendMessage(req) + lspserver.waitForReponse(req) enddef # Send a 'exit' notification to the LSP server @@ -201,15 +210,23 @@ util.WarnMsg("LSP server is not running") return 0 endif + # Send the shutdown request to the server lspserver.shutdownServer() - # Wait for the server to process the shutodwn request - sleep 1 - + # Notify the server to exit lspserver.exitServer() - lspserver.job->job_stop() - lspserver.job = v:null + # Wait for the server to process the exit notification and exit for a + # maximum of 2 seconds. + var maxCount: number = 1000 + while lspserver.job->job_status() == 'run' && maxCount > 0 + sleep 2m + maxCount -= 1 + endwhile + + if lspserver.job->job_status() == 'run' + lspserver.job->job_stop() + endif lspserver.running = false lspserver.ready = false lspserver.requests = {} @@ -921,7 +938,7 @@ var req = lspserver.createRequest('textDocument/selectionRange') # interface SelectionRangeParams # interface TextDocumentIdentifier - req.params->extend({textDocument: {uri: util.LspFileToUri(fname)}, positions: [s:GetLspPosition()]}) + req.params->extend({textDocument: {uri: util.LspFileToUri(fname)}, positions: [GetLspPosition()]}) lspserver.sendMessage(req) lspserver.waitForReponse(req) diff --git a/doc/lsp.txt b/doc/lsp.txt index 411ee85a8f7a0be18a4d2ba08cb5aa75d4b93eff..f463686d4d96370396b7814360f5d847dfae87b2 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -2,7 +2,7 @@ *lsp.txt* Language Server Protocol (LSP) Plugin for Vim9 Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) For Vim version 8.2.2342 and above -Last change: Feb 4, 2022 +Last change: Feb 17, 2022 ============================================================================== *lsp-license* @@ -126,6 +126,7 @@ :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. ============================================================================== 4. Configuration *lsp-configuration* @@ -485,16 +486,20 @@ *:LspWorkspaceRemoveFolder* :LspWorkspaceRemoveFolder {folder} Remove a folder from the workspace - *:LspWorkspaceListFolders* -:LspWorkspaceListFolders +:LspWorkspaceListFolders *:LspWorkspaceListFolders* Show the list of folders in the workspace. - *:LspShowServerCapabilities* -:LspShowServerCapabilities +:LspShowServerCapabilities *:LspShowServerCapabilities* Display the list of capabilities of a LSP server. The server capabilities are described in the LSP protocol specification under the "ServerCapabilities" interface. + + *:LspServerRestart* +:LspServerRestart Restart (stop and then start) the LSP server for the + current buffer. All the loaded buffers with the same + filetype as the current buffer are added back to the + server. ============================================================================== 6. Insert mode completion diff --git a/plugin/lsp.vim b/plugin/lsp.vim index 83b98d7a7d92b2318bb38b88f61ab808993c1bb9..7d241ea513b0fa9a9cb1aac982e9d08bd9018a0f 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -16,6 +16,7 @@ opt.LspOptionsSet = lspoptions.OptionsSet opt.lspOptions = lspoptions.lspOptions lspf.enableServerTrace = lsp.EnableServerTrace lspf.addServer = lsp.AddServer + lspf.restartServer = lsp.RestartServer lspf.LspServerReady = lsp.ServerReady lspf.addFile = lsp.AddFile lspf.removeFile = lsp.RemoveFile @@ -55,6 +56,7 @@ opt.LspOptionsSet = opt_import.OptionsSet opt.lspOptions = opt_import.lspOptions lspf.enableServerTrace = lsp_import.EnableServerTrace lspf.addServer = lsp_import.AddServer + lspf.restartServer = lsp_import.RestartServer lspf.LspServerReady = lsp_import.ServerReady lspf.addFile = lsp_import.AddFile lspf.removeFile = lsp_import.RemoveFile @@ -90,6 +92,7 @@ else import {lspOptions, OptionsSet} from '../autoload/lspoptions.vim' import {EnableServerTrace, AddServer, + RestartServer, ServerReady, AddFile, RemoveFile, @@ -126,6 +129,7 @@ opt.LspOptionsSet = OptionsSet opt.lspOptions = lspOptions lspf.enableServerTrace = EnableServerTrace lspf.addServer = AddServer + lspf.restartServer = RestartServer lspf.LspServerReady = ServerReady lspf.addFile = AddFile lspf.removeFile = RemoveFile @@ -182,6 +186,7 @@ enddef var TshowServers = lspf.showServers var TshowServerCapabilities = lspf.showServerCapabilities +var TrestartServer = lspf.restartServer var TsetTraceServer = lspf.setTraceServer var TaddFile = lspf.addFile var TremoveFile = lspf.removeFile @@ -229,6 +234,7 @@ # LSP commands command! -nargs=0 -bar LspShowServers call TshowServers() command! -nargs=0 -bar LspShowServerCapabilities call TshowServerCapabilities() +command! -nargs=0 -bar LspServerRestart call TrestartServer() command! -nargs=1 -bar LspSetTrace call TsetTraceServer() command! -nargs=0 -bar LspGotoDefinition call TgotoDefinition(v:false) command! -nargs=0 -bar LspGotoDeclaration call TgotoDeclaration(v:false)