autoload/lsp/codeaction.vim | 19 +++++++++++++++++-- autoload/lsp/lsp.vim | 5 +++++ autoload/lsp/textedit.vim | 7 +++---- autoload/lsp/util.vim | 7 +++++++ doc/lsp.txt | 26 ++++++++++++++++++++++++++ plugin/lsp.vim | 4 ++++ diff --git a/autoload/lsp/codeaction.vim b/autoload/lsp/codeaction.vim index 02bf7cd8ce7e6433b73f4f39fa9fd9f030ccbe3d..862c4ac23005832a3149d550cf32281a0c4884b1 100644 --- a/autoload/lsp/codeaction.vim +++ b/autoload/lsp/codeaction.vim @@ -6,6 +6,21 @@ import './util.vim' import './textedit.vim' import './options.vim' as opt +var CommandHandlers: dict + +export def RegisterCmdHandler(cmd: string, Handler: func) + CommandHandlers[cmd] = Handler +enddef + +def DoCommand(lspserver: dict, cmd: dict) + if CommandHandlers->has_key(cmd.command) + var CmdHandler: func = CommandHandlers[cmd.command] + call CmdHandler(cmd) + else + lspserver.executeCommand(cmd) + endif +enddef + export def HandleCodeAction(lspserver: dict, selAction: dict) # textDocument/codeAction can return either Command[] or CodeAction[]. # If it is a CodeAction, it can have either an edit, a command or both. @@ -20,11 +35,11 @@ # apply edit first textedit.ApplyWorkspaceEdit(selAction.edit) endif if selAction->has_key('command') - lspserver.executeCommand(selAction.command) + DoCommand(lspserver, selAction.command) endif else # selAction is a Command instance, apply it directly - lspserver.executeCommand(selAction) + DoCommand(lspserver, selAction) endif enddef diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index 80a42bf017465022220ad046ec45cabc61af2057..4f62aafd48cc66c20f94953a8d47c023bf332970 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -16,6 +16,7 @@ import './diag.vim' import './symbol.vim' import './outline.vim' import './signature.vim' +import './codeaction.vim' # LSP server information var lspServers: list> = [] @@ -997,6 +998,10 @@ return v:null endif return lspserver.tagFunc(pat, flags, info) +enddef + +export def RegisterCmdHandler(cmd: string, Handler: func) + codeaction.RegisterCmdHandler(cmd, Handler) enddef # vim: tabstop=8 shiftwidth=2 softtabstop=2 diff --git a/autoload/lsp/textedit.vim b/autoload/lsp/textedit.vim index 22f54856e9a366b66a7470134adeb4790779cff3..31bc3685af669746823a3c208783353199d7dd7d 100644 --- a/autoload/lsp/textedit.vim +++ b/autoload/lsp/textedit.vim @@ -210,10 +210,9 @@ endif var save_cursor: list = getcurpos() for [uri, changes] in workspaceEdit.changes->items() - var fname: string = util.LspUriToFile(uri) - var bnr: number = fname->bufnr() - if bnr == -1 - # file is already removed + var bnr: number = util.LspUriToBufnr(uri) + if bnr == 0 + # file is not present continue endif diff --git a/autoload/lsp/util.vim b/autoload/lsp/util.vim index 25701e38bcb7c3a254dd3e31ac1dc702d39dde69..d98253617d115e01c5c6ab3d3f16fdcb4ffef2be 100644 --- a/autoload/lsp/util.vim +++ b/autoload/lsp/util.vim @@ -69,6 +69,13 @@ return uri_decoded enddef +# Convert a LSP file URI (file://) to a Vim buffer number. +# If the file is not in a Vim buffer, then adds the buffer. +# Returns 0 on error. +export def LspUriToBufnr(uri: string): number + return LspUriToFile(uri)->bufadd() +enddef + # Returns if the URI refers to a remote file (e.g. ssh://) # Credit: vim-lsp plugin export def LspUriRemote(uri: string): bool diff --git a/doc/lsp.txt b/doc/lsp.txt index 57c1e1155f06f7e6e68615c09e0f4b0b772f4276..e060637c8a20b0318035bbac124c559354bf177d 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -761,4 +761,30 @@ use the :LspServerTrace command to set the trace value: > :LspServerTrace { off | messages | verbose } < +============================================================================== +11. Custom Command Handlers *lsp-custom-commands* + +When applying a code action, the language server may issue a non-standard +command. For example, the Java language server uses non-standard commands +(e.g. java.apply.workspaceEdit). To handle these commands, you can register a +callback function for each command using the LspRegisterCmdHandler() function. +For example: > + + vim9script + import "../../lsp/autoload/lsp/textedit.vim" + + def WorkspaceEdit(cmd: dict) + for editAct in cmd.arguments + textedit.ApplyWorkspaceEdit(editAct) + endfor + enddef + g:LspRegisterCmdHandler('java.apply.worksspaceEdit', WorkspaceEdit) +< +Place the above code in a file named lsp_java/plugin/lsp_java.vim and load +this plugin. + +The callback function should accept a Dict argument. The Dict argument +contains the LSP Command interface fields. Refer to the LSP specification for +more information about the "Command" interface. + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/plugin/lsp.vim b/plugin/lsp.vim index 27b6250530d810c5e06333f5796d31b4d37abe01..936fdc3bc22c5e618304f2c18d6caad12a489a98 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -18,6 +18,10 @@ def g:LspAddServer(serverList: list>) lsp.AddServer(serverList) enddef +def g:LspRegisterCmdHandler(cmd: string, Handler: func) + lsp.RegisterCmdHandler(cmd, Handler) +enddef + # Returns true if the language server for the current buffer is initialized # and ready to accept requests. def g:LspServerReady(): bool