Skip to content

Commit

Permalink
Send range with textDocument/hover when possible
Browse files Browse the repository at this point in the history
This relies on `experimental.rangeHoverProvider` which so far only Metals implement. But this workaround would be removed once microsoft/language-server-protocol#377 is part of the LSP specification.
  • Loading branch information
ayoub-benali committed Nov 18, 2021
1 parent c1e25f0 commit 1453c23
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 27 deletions.
4 changes: 4 additions & 0 deletions plugin/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ 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) -> Dict[str, Any]:
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 +406,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
76 changes: 49 additions & 27 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,7 +37,7 @@
SUBLIME_WORD_MASK = 515
SessionName = str
ResolvedHover = Union[Hover, Error]

PointOrRegion = Union[int, sublime.Region]

_test_contents = [] # type: List[str]

Expand Down Expand Up @@ -71,6 +73,8 @@ def link(self, point: int, view: sublime.View) -> str:

class LspHoverCommand(LspTextCommand):

range_hover_capability = 'experimental.rangeHoverProvider'

def __init__(self, view: sublime.View) -> None:
super().__init__(view)
self._base_dir = None # type: Optional[str]
Expand All @@ -82,17 +86,27 @@ def run(
point: Optional[int] = None,
event: Optional[dict] = None
) -> None:
temp_point = point
if temp_point is None:
range_hover_provider = False
session = self.best_session(self.range_hover_capability)
if session:
capability = session.get_capability(self.range_hover_capability)
if isinstance(capability, bool):
range_hover_provider = capability

# ignore selection if there a point is hovered
point_or_region = point # type: Optional[PointOrRegion]
if point_or_region is None:
region = first_selection_region(self.view)
if region is not None:
temp_point = region.begin()
if temp_point is None:
return
if region is not None and not range_hover_provider:
point_or_region = region.begin()
elif region is not None and range_hover_provider and not region.empty():
point_or_region = region
else:
return

window = self.view.window()
if not window:
return
hover_point = temp_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 +120,29 @@ 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, point_or_region)
if isinstance(point_or_region, int):
hover_point = point_or_region
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, point_or_region: PointOrRegion) -> None:
hover_promises = [] # type: List[Promise[ResolvedHover]]
document_position = text_document_position_params(self.view, point)
if isinstance(point_or_region, int):
document_position = text_document_position_params(self.view, point_or_region)
point = point_or_region
else:
document_position = text_document_range_params(self.view, point_or_region)
point = point_or_region.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 @@ -209,15 +231,15 @@ def _show_hover(self, listener: AbstractViewListener, point: int, only_diagnosti
_test_contents.append(contents) # for testing only

if contents:
# The previous popup could be in a different location from the next one
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 1453c23

Please sign in to comment.