Skip to content

Commit

Permalink
Allow user to send textDocument/hover with range if needed
Browse files Browse the repository at this point in the history
This a better alternative to scalameta/metals-sublime#45 and this implementation is just a workaround until microsoft/language-server-protocol#377 is part of the LSP spec
  • Loading branch information
ayoub-benali committed Nov 15, 2021
1 parent c1e25f0 commit 9b55f4a
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 21 deletions.
5 changes: 5 additions & 0 deletions plugin/core/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ class InsertTextMode:
'position': Position,
}, total=True)

TextDocumentRangeParams = TypedDict('TextDocumentRangeParams', {
'textDocument': TextDocumentIdentifier,
'range': RangeLsp,
}, total=True)

CodeDescription = TypedDict('CodeDescription', {
'href': str
}, total=True)
Expand Down
6 changes: 6 additions & 0 deletions plugin/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .protocol import Request
from .protocol import TextDocumentIdentifier
from .protocol import TextDocumentPositionParams
from .protocol import TextDocumentRangeParams
from .settings import userprefs
from .types import ClientConfig
from .typing import Callable, Optional, Dict, Any, Iterable, List, Union, Tuple, Sequence, cast
Expand Down Expand Up @@ -270,6 +271,9 @@ def versioned_text_document_identifier(view: sublime.View, version: int) -> Dict
def text_document_position_params(view: sublime.View, location: int) -> TextDocumentPositionParams:
return {"textDocument": text_document_identifier(view), "position": position(view, location)}

def text_document_range_params(view: sublime.View, region: sublime.Region) -> TextDocumentRangeParams:
return {"textDocument": text_document_identifier(view), "range": region_to_range(view, region).to_lsp()}


def did_open_text_document_params(view: sublime.View, language_id: str) -> Dict[str, Any]:
return {"textDocument": text_document_item(view, language_id)}
Expand Down Expand Up @@ -404,6 +408,8 @@ def text_document_code_action_params(
# Workaround for a limited margin-collapsing capabilities of the minihtml.
LSP_POPUP_SPACER_HTML = '<div class="lsp_popup--spacer"></div>'

def hide_lsp_popup(view: sublime.View) -> None:
mdpopups.hide_popup(view)

def show_lsp_popup(view: sublime.View, contents: str, location: int = -1, md: bool = False, flags: int = 0,
css: Optional[str] = None, wrapper_class: Optional[str] = None,
Expand Down
61 changes: 40 additions & 21 deletions plugin/hover.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
from .core.views import is_location_href
from .core.views import make_command_link
from .core.views import make_link
from .core.views import hide_lsp_popup
from .core.views import show_lsp_popup
from .core.views import text_document_position_params
from .core.views import text_document_range_params
from .core.views import unpack_href_location
from .core.views import update_lsp_popup
from .core.windows import AbstractViewListener
Expand All @@ -35,6 +37,7 @@
SUBLIME_WORD_MASK = 515
SessionName = str
ResolvedHover = Union[Hover, Error]
RegionOrPoint = Union[sublime.Region, int]


_test_contents = [] # type: List[str]
Expand Down Expand Up @@ -80,10 +83,18 @@ def run(
edit: sublime.Edit,
only_diagnostics: bool = False,
point: Optional[int] = None,
event: Optional[dict] = None
use_selection: bool = False
) -> None:
selection = None
if use_selection:
region = first_selection_region(self.view)
if region is not None and not region.empty():
selection = region
if use_selection and not selection:
return

temp_point = point
if temp_point is None:
if temp_point is None and not use_selection:
region = first_selection_region(self.view)
if region is not None:
temp_point = region.begin()
Expand All @@ -93,6 +104,8 @@ def run(
if not window:
return
hover_point = temp_point
selection_or_hover_point = selection if selection is not None else hover_point

wm = windows.lookup(window)
self._base_dir = wm.get_project_path(self.view.file_name() or "")
self._hover_responses = [] # type: List[Hover]
Expand All @@ -106,21 +119,27 @@ def run_async() -> None:
if not listener:
return
if not only_diagnostics:
self.request_symbol_hover_async(listener, hover_point)
self._diagnostics_by_config, covering = listener.diagnostics_touching_point_async(
hover_point, userprefs().show_diagnostics_severity_level)
if self._diagnostics_by_config:
self.show_hover(listener, hover_point, only_diagnostics)
if not only_diagnostics and userprefs().show_code_actions_in_hover:
actions_manager.request_for_region_async(
self.view, covering, self._diagnostics_by_config,
functools.partial(self.handle_code_actions, listener, hover_point))
self.request_symbol_hover_async(listener, selection_or_hover_point)
if not use_selection:
self._diagnostics_by_config, covering = listener.diagnostics_touching_point_async(
hover_point, userprefs().show_diagnostics_severity_level)
if self._diagnostics_by_config:
self.show_hover(listener, hover_point, only_diagnostics)
if not only_diagnostics and userprefs().show_code_actions_in_hover:
actions_manager.request_for_region_async(
self.view, covering, self._diagnostics_by_config,
functools.partial(self.handle_code_actions, listener, hover_point))

sublime.set_timeout_async(run_async)

def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) -> None:
def request_symbol_hover_async(self, listener: AbstractViewListener, region_or_point: RegionOrPoint) -> None:
hover_promises = [] # type: List[Promise[ResolvedHover]]
document_position = text_document_position_params(self.view, point)
if isinstance(region_or_point, int):
document_position = text_document_position_params(self.view, region_or_point)
point = region_or_point
else:
document_position = text_document_range_params(self.view, region_or_point)
point = region_or_point.begin()
for session in listener.sessions_async('hoverProvider'):
hover_promises.append(session.send_request_task(
Request("textDocument/hover", document_position, self.view)
Expand Down Expand Up @@ -210,14 +229,14 @@ def _show_hover(self, listener: AbstractViewListener, point: int, only_diagnosti

if contents:
if self.view.is_popup_visible():
update_lsp_popup(self.view, contents)
else:
show_lsp_popup(
self.view,
contents,
flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY,
location=point,
on_navigate=lambda href: self._on_navigate(href, point))
hide_lsp_popup(self.view)

show_lsp_popup(
self.view,
contents,
flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY,
location=point,
on_navigate=lambda href: self._on_navigate(href, point))

def _on_navigate(self, href: str, point: int) -> None:
if href.startswith("subl:"):
Expand Down

0 comments on commit 9b55f4a

Please sign in to comment.