diff --git a/plugin/core/protocol.py b/plugin/core/protocol.py index 7df2d7e4b..56d711ba0 100644 --- a/plugin/core/protocol.py +++ b/plugin/core/protocol.py @@ -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) diff --git a/plugin/core/views.py b/plugin/core/views.py index 9b1a471a8..99120e70c 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -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 @@ -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)} @@ -404,6 +408,8 @@ def text_document_code_action_params( # Workaround for a limited margin-collapsing capabilities of the minihtml. LSP_POPUP_SPACER_HTML = '
' +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, diff --git a/plugin/hover.py b/plugin/hover.py index aecbd2557..bf39e95dd 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -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 @@ -35,6 +37,7 @@ SUBLIME_WORD_MASK = 515 SessionName = str ResolvedHover = Union[Hover, Error] +RegionOrPoint = Union[sublime.Region, int] _test_contents = [] # type: List[str] @@ -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() @@ -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] @@ -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) @@ -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:"):