Repository: sublimelsp/LSP

Issue:
Provide an optional response handler in LspExecuteCommand
**Is your feature request related to a problem? Please describe.**
I would like to extends `LspExecuteCommand` in order to send a `workspace/executeCommand` but `LspExecuteCommand` simply [logs](https://github.com/sublimelsp/LSP/blob/acfd6406ba4680a0e537dc87a72aa5b410a154e7/plugin/execute_command.py#L47) the response. In my case I have to open a file URI that is in the reponse.

**Describe the solution you'd like**
`LspExecuteCommand` should provide an optional response handler. When missing simply log the response as the case now. Otherwise delegate the response handling. 

**Describe alternatives you've considered**
The current alternative is to copy `LspExecuteCommand` logic.



Discussion:
Sounds like an easy PR. Move https://github.com/sublimelsp/LSP/blob/acfd6406ba4680a0e537dc87a72aa5b410a154e7/plugin/execute_command.py#L42-L47 to a new method `on_result_async(self, response)` that the plugin will be able to override.
There was this PR that added `on_server_response_async` https://github.com/sublimelsp/LSP/pull/1992
```
    def on_server_response_async(self, method: str, response: Response) -> None:
        """
        Notifies about a response message that has been received from the language server.
        Only successful responses are passed to this method.
        :param    method:    The method of the request.
        :param    response:  The response object to the request. The response.result field can be modified by the
                             plugin, before it gets further handled by the LSP package.
        """
        pass
```
Will it suite your usecase?
Would be kinda weird and hard to follow a code that executes a command in one place but handles the result in a completely different place. So I think this simple change would still be better.

Resolving patch:
diff --git a/boot.py b/boot.py
--- a/boot.py
+++ b/boot.py
@@ -20,6 +20,7 @@
 from .plugin.core.panels import LspUpdatePanelCommand
 from .plugin.core.panels import LspUpdateServerPanelCommand
 from .plugin.core.panels import WindowPanelListener
+from .plugin.core.protocol import Error
 from .plugin.core.protocol import Location
 from .plugin.core.registry import LspRecheckSessionsCommand
 from .plugin.core.registry import LspRestartServerCommand
diff --git a/plugin/execute_command.py b/plugin/execute_command.py
--- a/plugin/execute_command.py
+++ b/plugin/execute_command.py
@@ -9,6 +9,9 @@
 
 
 class LspExecuteCommand(LspTextCommand):
+    """
+    Helper command for triggering workspace/executeCommand requests.
+    """
 
     def run(self,
             edit: sublime.Edit,
@@ -37,17 +40,33 @@ def run_async() -> None:
             def handle_response(response: Any) -> None:
                 assert command_name
                 if isinstance(response, Error):
-                    sublime.message_dialog("command {} failed. Reason: {}".format(command_name, str(response)))
+                    self.handle_error_async(response, command_name)
                     return
-                msg = "command {} completed".format(command_name)
-                if response:
-                    msg += "with response: {}".format(response)
-                window = self.view.window()
-                if window:
-                    window.status_message(msg)
+                self.handle_success_async(response, command_name)
 
             session.execute_command(params, progress=True).then(handle_response)
 
+    def handle_success_async(self, result: Any, command_name: str) -> None:
+        """
+        Override this method to handle successful response to workspace/executeCommand.
+
+        :param result: The result returned from the server.
+        :param command_name: The name of the command that was executed.
+        """
+        msg = "command {} completed".format(command_name)
+        window = self.view.window()
+        if window:
+            window.status_message(msg)
+
+    def handle_error_async(self, error: Error, command_name: str) -> None:
+        """
+        Override this method to handle failed response to workspace/executeCommand.
+
+        :param error: The Error object.
+        :param command_name: The name of the command that was executed.
+        """
+        sublime.message_dialog("command {} failed. Reason: {}".format(command_name, str(error)))
+
     def _expand_variables(self, command_args: List[Any]) -> List[Any]:
         view = self.view  # type: sublime.View
         region = first_selection_region(view)


Test patch: