Skip to content

Commit

Permalink
Merge branch 'main' into feat/diag-annotations
Browse files Browse the repository at this point in the history
* main:
  Allow plugins to modify server response messages (#1992)
  Keep active group when using Goto commands (#1994)
  Optionally fallback to goto_definition in lsp_symbol_definition (#1986)
  Don't use actual linebreaks in log panel if payload is string literal (#1993)
  • Loading branch information
rchl committed Jul 14, 2022
2 parents dc38b46 + 9fa725a commit 2bbc951
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 29 deletions.
13 changes: 9 additions & 4 deletions Default.sublime-keymap
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@
// {
// "command": "lsp_symbol_definition",
// "args": {
// "side_by_side": false
// "side_by_side": false,
// "force_group": true,
// "fallback": false
// },
// "keys": [
// "f12"
Expand All @@ -193,7 +195,8 @@
// {
// "command": "lsp_symbol_type_definition",
// "args": {
// "side_by_side": false
// "side_by_side": false,
// "force_group": true
// },
// "keys": [
// "UNBOUND"
Expand All @@ -215,7 +218,8 @@
// {
// "command": "lsp_symbol_declaration",
// "args": {
// "side_by_side": false
// "side_by_side": false,
// "force_group": true
// },
// "keys": [
// "UNBOUND"
Expand All @@ -237,7 +241,8 @@
// {
// "command": "lsp_symbol_implementation",
// "args": {
// "side_by_side": false
// "side_by_side": false,
// "force_group": true
// },
// "keys": [
// "UNBOUND"
Expand Down
2 changes: 2 additions & 0 deletions Syntaxes/ServerLog.sublime-syntax
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ contexts:
- match: $
pop: true
- include: scope:source.python#constants # e.g. shutdown request
- include: scope:source.python#strings
- include: scope:source.python#numbers
- include: scope:source.python#lists
- include: scope:source.python#dictionaries-and-sets
- match: ''
Expand Down
3 changes: 3 additions & 0 deletions docs/src/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ In addition to the basic "Goto Definition", the protocol also provides further r
- Goto Declaration
- Goto Implementation

Additionally, the LSP's "Goto Definition" command can fall back to the built-in Sublime's "Goto Definition" if the `fallback` argument is set to `true`.
This way, when there are no results found the built-in "Goto Definition" command will be triggered.

## Find References

[Example GIF 1](https://user-images.githubusercontent.com/6579999/128551752-b37fe407-148c-41cf-b1e4-6fe96ed0f77c.gif)
Expand Down
7 changes: 4 additions & 3 deletions plugin/core/open.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ def open_file(
# to open as a separate view).
view = window.find_open_file(file)
if view:
opens_in_current_group = group == -1 or window.active_group() == group
opens_as_new_selection = (flags & (sublime.ADD_TO_SELECTION | sublime.REPLACE_MRU)) != 0
return_existing_view = opens_in_current_group and not opens_as_new_selection
opens_in_desired_group = not bool(flags & sublime.FORCE_GROUP) or \
window.get_view_index(view)[0] == window.active_group()
opens_in_side_by_side = bool(flags & (sublime.ADD_TO_SELECTION | sublime.REPLACE_MRU))
return_existing_view = opens_in_desired_group and not opens_in_side_by_side
if return_existing_view:
return Promise.resolve(view)

Expand Down
31 changes: 24 additions & 7 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,17 @@ def on_pre_send_notification_async(self, notification: Notification) -> None:
"""
pass

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

def on_open_uri_async(self, uri: DocumentUri, callback: Callable[[str, str, str], None]) -> bool:
"""
Called when a language server reports to open an URI. If you know how to handle this URI, then return True and
Expand Down Expand Up @@ -1898,10 +1909,12 @@ def deduce_payload(
return res
elif "id" in payload:
response_id = int(payload["id"])
handler, result, is_error = self.response_handler(response_id, payload)
response_tuple = (handler, result, None, None, None)
handler, method, result, is_error = self.response_handler(response_id, payload)
self._logger.incoming_response(response_id, result, is_error)
return response_tuple
response = Response(response_id, result)
if self._plugin and not is_error:
self._plugin.on_server_response_async(method, response) # type: ignore
return handler, response.result, None, None, None
else:
debug("Unknown payload type: ", payload)
return (None, None, None, None, None)
Expand All @@ -1925,21 +1938,25 @@ def on_payload(self, payload: Dict[str, Any]) -> None:
except Exception as err:
exception_log("Error handling {}".format(typestr), err)

def response_handler(self, response_id: int, response: Dict[str, Any]) -> Tuple[Optional[Callable], Any, bool]:
def response_handler(
self,
response_id: int,
response: Dict[str, Any]
) -> Tuple[Optional[Callable], Optional[str], Any, bool]:
request, handler, error_handler = self._response_handlers.pop(response_id, (None, None, None))
if not request:
error = {"code": ErrorCode.InvalidParams, "message": "unknown response ID {}".format(response_id)}
return (print_to_status_bar, error, True)
return (print_to_status_bar, None, error, True)
self._invoke_views(request, "on_request_finished_async", response_id)
if "result" in response and "error" not in response:
return (handler, response["result"], False)
return (handler, request.method, response["result"], False)
if not error_handler:
error_handler = print_to_status_bar
if "result" not in response and "error" in response:
error = response["error"]
else:
error = {"code": ErrorCode.InvalidParams, "message": "invalid response payload"}
return (error_handler, error, True)
return (error_handler, request.method, error, True)

def _get_handler(self, method: str) -> Optional[Callable]:
return getattr(self, method2attr(method), None)
2 changes: 1 addition & 1 deletion plugin/core/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ def log(self, message: str, params: Any) -> None:

def run_on_async_worker_thread() -> None:
nonlocal message
params_str = str(params)
params_str = repr(params)
if 0 < userprefs().log_max_size <= len(params_str):
params_str = '<params with {} characters>'.format(len(params_str))
message = "{}: {}".format(message, params_str)
Expand Down
47 changes: 35 additions & 12 deletions plugin/goto.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,76 @@
class LspGotoCommand(LspTextCommand):

method = ''
fallback_command = ''

def is_enabled(self, event: Optional[dict] = None, point: Optional[int] = None, side_by_side: bool = False) -> bool:
return super().is_enabled(event, point)
def is_enabled(
self,
event: Optional[dict] = None,
point: Optional[int] = None,
side_by_side: bool = False,
force_group: bool = True,
fallback: bool = False
) -> bool:
return fallback or super().is_enabled(event, point)

def run(
self,
_: sublime.Edit,
event: Optional[dict] = None,
point: Optional[int] = None,
side_by_side: bool = False
side_by_side: bool = False,
force_group: bool = True,
fallback: bool = False
) -> None:
session = self.best_session(self.capability)
position = get_position(self.view, event, point)
if session and position is not None:
params = text_document_position_params(self.view, position)
request = Request(self.method, params, self.view, progress=True)
session.send_request(request, functools.partial(self._handle_response_async, session, side_by_side))
session.send_request(
request, functools.partial(self._handle_response_async, session, side_by_side, force_group, fallback))
else:
self._handle_no_results(fallback, side_by_side)

def _handle_response_async(
self,
session: Session,
side_by_side: bool,
force_group: bool,
fallback: bool,
response: Union[None, Location, List[Location], List[LocationLink]]
) -> None:
if isinstance(response, dict):
self.view.run_command("add_jump_record", {"selection": [(r.a, r.b) for r in self.view.sel()]})
open_location_async(session, response, side_by_side)
open_location_async(session, response, side_by_side, force_group)
elif isinstance(response, list):
if len(response) == 0:
window = self.view.window()
if window:
window.status_message("No results found")
self._handle_no_results(fallback, side_by_side)
elif len(response) == 1:
self.view.run_command("add_jump_record", {"selection": [(r.a, r.b) for r in self.view.sel()]})
open_location_async(session, response[0], side_by_side)
open_location_async(session, response[0], side_by_side, force_group)
else:
self.view.run_command("add_jump_record", {"selection": [(r.a, r.b) for r in self.view.sel()]})
sublime.set_timeout(functools.partial(LocationPicker, self.view, session, response, side_by_side))
else:
window = self.view.window()
if window:
window.status_message("No results found")
self._handle_no_results(fallback, side_by_side)

def _handle_no_results(self, fallback: bool = False, side_by_side: bool = False) -> None:
window = self.view.window()

if not window:
return

if fallback and self.fallback_command:
window.run_command(self.fallback_command, {"side_by_side": side_by_side})
else:
window.status_message("No results found")


class LspSymbolDefinitionCommand(LspGotoCommand):
method = "textDocument/definition"
capability = method_to_capability(method)[0]
fallback_command = "goto_definition"


class LspSymbolTypeDefinitionCommand(LspGotoCommand):
Expand Down
12 changes: 10 additions & 2 deletions plugin/locationpicker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@
import weakref


def open_location_async(session: Session, location: Union[Location, LocationLink], side_by_side: bool) -> None:
def open_location_async(
session: Session,
location: Union[Location, LocationLink],
side_by_side: bool,
force_group: bool
) -> None:
flags = sublime.ENCODED_POSITION
if force_group:
flags |= sublime.FORCE_GROUP
if side_by_side:
flags |= sublime.ADD_TO_SELECTION | sublime.SEMI_TRANSIENT

Expand Down Expand Up @@ -80,7 +87,8 @@ def _select_entry(self, index: int) -> None:
if not self._side_by_side:
open_basic_file(session, uri, position, flags)
else:
sublime.set_timeout_async(functools.partial(open_location_async, session, location, self._side_by_side))
sublime.set_timeout_async(
functools.partial(open_location_async, session, location, self._side_by_side, True))
else:
self._window.focus_view(self._view)
# When in side-by-side mode close the current highlighted
Expand Down

0 comments on commit 2bbc951

Please sign in to comment.