From 66b0fe0f81c7f92fa3f44a6fd7987778544b561c Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Thu, 18 Nov 2021 14:32:41 +0100 Subject: [PATCH 01/19] Send range with textDocument/hover when possible This relies on `experimental.rangeHoverProvider` which so far only Metals implement. But this workaround would be removed once https://github.com/microsoft/language-server-protocol/issues/377 is part of the LSP specification. --- plugin/core/views.py | 4 +++ plugin/hover.py | 75 +++++++++++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/plugin/core/views.py b/plugin/core/views.py index 9b1a471a8..a8c4ed66e 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -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)} @@ -404,6 +406,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..1150b79f3 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,7 +37,7 @@ SUBLIME_WORD_MASK = 515 SessionName = str ResolvedHover = Union[Hover, Error] - +PointOrRegion = Union[int, sublime.Region] _test_contents = [] # type: List[str] @@ -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] @@ -82,17 +86,28 @@ 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: + 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 + + if point_or_region is None: 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] @@ -106,21 +121,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) @@ -209,15 +232,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:"): From 38f51c9e1d38656fb02fd467f5acc05c23c3dba5 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Thu, 18 Nov 2021 15:01:04 +0100 Subject: [PATCH 02/19] Delay capability check --- plugin/hover.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index 1150b79f3..5556fa981 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -73,8 +73,6 @@ 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] @@ -86,20 +84,10 @@ def run( point: Optional[int] = None, event: Optional[dict] = None ) -> 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 and not range_hover_provider: - point_or_region = region.begin() - elif region is not None and range_hover_provider and not region.empty(): + if region is not None: point_or_region = region if point_or_region is None: @@ -137,14 +125,19 @@ def run_async() -> None: def request_symbol_hover_async(self, listener: AbstractViewListener, point_or_region: PointOrRegion) -> None: hover_promises = [] # type: List[Promise[ResolvedHover]] - 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() + point = point_or_region if isinstance(point_or_region, int) else point_or_region.begin() for session in listener.sessions_async('hoverProvider'): + capability = session.get_capability('experimental.rangeHoverProvider') + range_hover_provider = False + if isinstance(capability, bool): + range_hover_provider = capability + + if isinstance(point_or_region, sublime.Region) and range_hover_provider: + document_position = text_document_range_params(self.view, point_or_region) + else: + document_position = text_document_position_params(self.view, point_or_region) + hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From ffef3bc96121afb2022f4187daa70338e2aef0ba Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Thu, 18 Nov 2021 15:26:44 +0100 Subject: [PATCH 03/19] Address PR comments --- plugin/hover.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index 5556fa981..f61288639 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -111,9 +111,13 @@ def run_async() -> None: if not only_diagnostics: 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) + point_or_region, userprefs().show_diagnostics_severity_level) + else: + self._diagnostics_by_config, covering = listener.diagnostics_intersecting_region_async( + point_or_region) + if isinstance(point_or_region, int): + hover_point = point_or_region if self._diagnostics_by_config: self.show_hover(listener, hover_point, only_diagnostics) if not only_diagnostics and userprefs().show_code_actions_in_hover: @@ -137,7 +141,6 @@ def request_symbol_hover_async(self, listener: AbstractViewListener, point_or_re document_position = text_document_range_params(self.view, point_or_region) else: document_position = text_document_position_params(self.view, point_or_region) - hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From 21169c91e563023b401dde15457da8c76bd17bd4 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Thu, 18 Nov 2021 15:46:35 +0100 Subject: [PATCH 04/19] Send range when hovering selection as well --- plugin/hover.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index f61288639..34d58585a 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -89,10 +89,8 @@ def run( region = first_selection_region(self.view) if region is not None: point_or_region = region - if point_or_region is None: return - window = self.view.window() if not window: return @@ -130,17 +128,17 @@ def run_async() -> None: def request_symbol_hover_async(self, listener: AbstractViewListener, point_or_region: PointOrRegion) -> None: hover_promises = [] # type: List[Promise[ResolvedHover]] point = point_or_region if isinstance(point_or_region, int) else point_or_region.begin() - for session in listener.sessions_async('hoverProvider'): - capability = session.get_capability('experimental.rangeHoverProvider') - range_hover_provider = False - if isinstance(capability, bool): - range_hover_provider = capability - - if isinstance(point_or_region, sublime.Region) and range_hover_provider: - document_position = text_document_range_params(self.view, point_or_region) + range_hover_provider = session.get_capability('experimental.rangeHoverProvider') + if range_hover_provider and isinstance(point_or_region, int): + for region in self.view.sel(): + if region.contains(point): + # when hovering selection send it as range + document_position = text_document_range_params(self.view, region) + else: + document_position = text_document_position_params(self.view, point_or_region) else: - document_position = text_document_position_params(self.view, point_or_region) + document_position = text_document_range_params(self.view, PointOrRegion) hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From 8bdc9bfe7def98eb408e3a19e1af0ea1b02db1e2 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Thu, 18 Nov 2021 15:52:19 +0100 Subject: [PATCH 05/19] fix typo --- plugin/hover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/hover.py b/plugin/hover.py index 34d58585a..33a69771d 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -138,7 +138,7 @@ def request_symbol_hover_async(self, listener: AbstractViewListener, point_or_re else: document_position = text_document_position_params(self.view, point_or_region) else: - document_position = text_document_range_params(self.view, PointOrRegion) + document_position = text_document_range_params(self.view, point_or_region) hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From 6df11663e172816e08f218cd154e32ae0f5c8a56 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Thu, 18 Nov 2021 15:58:45 +0100 Subject: [PATCH 06/19] Fix condition --- plugin/hover.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index 33a69771d..87918ff23 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -130,15 +130,18 @@ def request_symbol_hover_async(self, listener: AbstractViewListener, point_or_re point = point_or_region if isinstance(point_or_region, int) else point_or_region.begin() for session in listener.sessions_async('hoverProvider'): range_hover_provider = session.get_capability('experimental.rangeHoverProvider') - if range_hover_provider and isinstance(point_or_region, int): - for region in self.view.sel(): - if region.contains(point): - # when hovering selection send it as range - document_position = text_document_range_params(self.view, region) - else: - document_position = text_document_position_params(self.view, point_or_region) + if range_hover_provider: + if isinstance(point_or_region, int): + for region in self.view.sel(): + if region.contains(point): + # when hovering selection send it as range + document_position = text_document_range_params(self.view, region) + else: + document_position = text_document_position_params(self.view, point_or_region) + else: + document_position = text_document_range_params(self.view, point_or_region) else: - document_position = text_document_range_params(self.view, point_or_region) + document_position = text_document_position_params(self.view, point) hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From 7fad7cf58b9beb415dfa887d421e50c28e205177 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Thu, 18 Nov 2021 17:40:55 +0100 Subject: [PATCH 07/19] Revert hide_lsp_popup --- plugin/core/views.py | 3 --- plugin/hover.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/plugin/core/views.py b/plugin/core/views.py index a8c4ed66e..d63370334 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -406,9 +406,6 @@ 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, on_navigate: Optional[Callable] = None, on_hide: Optional[Callable] = None) -> None: diff --git a/plugin/hover.py b/plugin/hover.py index 87918ff23..6bf23bdba 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -20,7 +20,6 @@ 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 @@ -231,13 +230,14 @@ def _show_hover(self, listener: AbstractViewListener, point: int, only_diagnosti if contents: # The previous popup could be in a different location from the next one if self.view.is_popup_visible(): - 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)) + 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)) def _on_navigate(self, href: str, point: int) -> None: if href.startswith("subl:"): From 8107120a4242a140ecf1d0af732de4d9f626636a Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 21:01:27 +0100 Subject: [PATCH 08/19] Fix test case --- plugin/hover.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index 6bf23bdba..9181ab4a5 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -107,20 +107,15 @@ def run_async() -> None: return if not only_diagnostics: self.request_symbol_hover_async(listener, point_or_region) - if isinstance(point_or_region, int): - self._diagnostics_by_config, covering = listener.diagnostics_touching_point_async( - point_or_region, userprefs().show_diagnostics_severity_level) - else: - self._diagnostics_by_config, covering = listener.diagnostics_intersecting_region_async( - point_or_region) - if isinstance(point_or_region, int): - hover_point = point_or_region - 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)) + point = point_or_region if isinstance(point_or_region, int) else point_or_region.begin() + self._diagnostics_by_config, covering = listener.diagnostics_touching_point_async( + point, userprefs().show_diagnostics_severity_level) + if self._diagnostics_by_config: + self.show_hover(listener, 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, point)) sublime.set_timeout_async(run_async) @@ -132,7 +127,7 @@ def request_symbol_hover_async(self, listener: AbstractViewListener, point_or_re if range_hover_provider: if isinstance(point_or_region, int): for region in self.view.sel(): - if region.contains(point): + if region.contains(point_or_region): # when hovering selection send it as range document_position = text_document_range_params(self.view, region) else: From eb669753e4380a0bd2cf6a948cbd5ed0b6dcabda Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 21:40:43 +0100 Subject: [PATCH 09/19] Do range check in request_symbol_hover_async --- plugin/core/views.py | 3 +++ plugin/hover.py | 42 +++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/plugin/core/views.py b/plugin/core/views.py index d63370334..c56a0dfda 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -270,9 +270,11 @@ 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)} @@ -406,6 +408,7 @@ def text_document_code_action_params( # Workaround for a limited margin-collapsing capabilities of the minihtml. LSP_POPUP_SPACER_HTML = '
' + 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, on_navigate: Optional[Callable] = None, on_hide: Optional[Callable] = None) -> None: diff --git a/plugin/hover.py b/plugin/hover.py index 9181ab4a5..e67750dbc 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -36,7 +36,6 @@ SUBLIME_WORD_MASK = 515 SessionName = str ResolvedHover = Union[Hover, Error] -PointOrRegion = Union[int, sublime.Region] _test_contents = [] # type: List[str] @@ -83,14 +82,18 @@ def run( point: Optional[int] = None, event: Optional[dict] = None ) -> None: - point_or_region = point # type: Optional[PointOrRegion] - if point_or_region is None: + temp_point = point + if temp_point is None: region = first_selection_region(self.view) if region is not None: - point_or_region = region - if point_or_region is None: + temp_point = region.begin() + if temp_point is None: return window = self.view.window() + if not window: + return + hover_point = temp_point + window = self.view.window() if not window: return wm = windows.lookup(window) @@ -106,34 +109,31 @@ def run_async() -> None: if not listener: return if not only_diagnostics: - self.request_symbol_hover_async(listener, point_or_region) - point = point_or_region if isinstance(point_or_region, int) else point_or_region.begin() + self.request_symbol_hover_async(listener, hover_point) self._diagnostics_by_config, covering = listener.diagnostics_touching_point_async( - point, userprefs().show_diagnostics_severity_level) + hover_point, userprefs().show_diagnostics_severity_level) if self._diagnostics_by_config: - self.show_hover(listener, point, only_diagnostics) + 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, point)) + functools.partial(self.handle_code_actions, listener, hover_point)) sublime.set_timeout_async(run_async) - def request_symbol_hover_async(self, listener: AbstractViewListener, point_or_region: PointOrRegion) -> None: + def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) -> None: hover_promises = [] # type: List[Promise[ResolvedHover]] - point = point_or_region if isinstance(point_or_region, int) else point_or_region.begin() for session in listener.sessions_async('hoverProvider'): range_hover_provider = session.get_capability('experimental.rangeHoverProvider') if range_hover_provider: - if isinstance(point_or_region, int): - for region in self.view.sel(): - if region.contains(point_or_region): - # when hovering selection send it as range - document_position = text_document_range_params(self.view, region) - else: - document_position = text_document_position_params(self.view, point_or_region) - else: - document_position = text_document_range_params(self.view, point_or_region) + region = first_selection_region(self.view) + if region is not None: + if region.contains(point): # when hovering selection send the selection as range + document_position = text_document_range_params(self.view, region) + else: # there is selection but ignored + document_position = text_document_range_params(self.view, region) + else: # nothing selected + document_position = text_document_position_params(self.view, point) else: document_position = text_document_position_params(self.view, point) hover_promises.append(session.send_request_task( From 7376b2c4e0d78481b32691c3baea3316edd47335 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 21:42:13 +0100 Subject: [PATCH 10/19] make flake8 happy --- plugin/hover.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index e67750dbc..f53565466 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -128,11 +128,11 @@ def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) if range_hover_provider: region = first_selection_region(self.view) if region is not None: - if region.contains(point): # when hovering selection send the selection as range + if region.contains(point): # when hovering selection send the selection as range document_position = text_document_range_params(self.view, region) - else: # there is selection but ignored + else: # there is selection but ignored document_position = text_document_range_params(self.view, region) - else: # nothing selected + else: # nothing selected document_position = text_document_position_params(self.view, point) else: document_position = text_document_position_params(self.view, point) From 265e422e27fc8ea2eb8d98011c4c36ff088573e7 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 21:43:37 +0100 Subject: [PATCH 11/19] remove duplicated code --- plugin/hover.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index f53565466..2de04538a 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -93,9 +93,6 @@ def run( if not window: return hover_point = temp_point - window = self.view.window() - if not window: - return wm = windows.lookup(window) self._base_dir = wm.get_project_path(self.view.file_name() or "") self._hover_responses = [] # type: List[Hover] From 7972cd1f1e7a90d9158fd5e74293719c85e00768 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 21:45:10 +0100 Subject: [PATCH 12/19] remove comment --- plugin/hover.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin/hover.py b/plugin/hover.py index 2de04538a..715cba0ce 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -220,7 +220,6 @@ 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: From 5b3182a921cf9f63db546db6628d9fe9b8c33c22 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 21:46:05 +0100 Subject: [PATCH 13/19] put space back --- plugin/hover.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/hover.py b/plugin/hover.py index 715cba0ce..d8aa7e124 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -37,6 +37,7 @@ SessionName = str ResolvedHover = Union[Hover, Error] + _test_contents = [] # type: List[str] From 5efa5396d44b8f93accb59ae8464a480fd69ba94 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 21:54:15 +0100 Subject: [PATCH 14/19] Simplify case --- plugin/hover.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index d8aa7e124..1f7cd4ee6 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -121,19 +121,15 @@ def run_async() -> None: def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) -> None: hover_promises = [] # type: List[Promise[ResolvedHover]] + document_position = text_document_position_params(self.view, point) for session in listener.sessions_async('hoverProvider'): range_hover_provider = session.get_capability('experimental.rangeHoverProvider') if range_hover_provider: region = first_selection_region(self.view) if region is not None: - if region.contains(point): # when hovering selection send the selection as range + if region.contains(point): + # hovering selection or only text was selected document_position = text_document_range_params(self.view, region) - else: # there is selection but ignored - document_position = text_document_range_params(self.view, region) - else: # nothing selected - document_position = text_document_position_params(self.view, point) - else: - document_position = text_document_position_params(self.view, point) hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From 6a21f058c237713a4d6a05e7e47841222c232acb Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 22:01:36 +0100 Subject: [PATCH 15/19] Set range field directly --- plugin/core/views.py | 4 ---- plugin/hover.py | 5 +++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/plugin/core/views.py b/plugin/core/views.py index c56a0dfda..9b1a471a8 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -271,10 +271,6 @@ def text_document_position_params(view: sublime.View, location: int) -> TextDocu 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)} diff --git a/plugin/hover.py b/plugin/hover.py index 1f7cd4ee6..eab2c5434 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -20,9 +20,9 @@ from .core.views import is_location_href from .core.views import make_command_link from .core.views import make_link +from .core.views import region_to_range 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 @@ -129,7 +129,8 @@ def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) if region is not None: if region.contains(point): # hovering selection or only text was selected - document_position = text_document_range_params(self.view, region) + document_position.pop('position', None) + document_position['range'] = region_to_range(self.view, region).to_lsp() hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From 7e5e0450e2aed35ca8d71297f58934e281af2ae5 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 22:05:35 +0100 Subject: [PATCH 16/19] create document position inside the loop --- plugin/hover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/hover.py b/plugin/hover.py index eab2c5434..6f765864d 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -121,8 +121,8 @@ def run_async() -> None: def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) -> None: hover_promises = [] # type: List[Promise[ResolvedHover]] - document_position = text_document_position_params(self.view, point) for session in listener.sessions_async('hoverProvider'): + document_position = text_document_position_params(self.view, point) range_hover_provider = session.get_capability('experimental.rangeHoverProvider') if range_hover_provider: region = first_selection_region(self.view) From d95f7e5cf0eb778bb68418526ab7d96e00f21eef Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 22:32:49 +0100 Subject: [PATCH 17/19] Extract range logic --- plugin/core/protocol.py | 5 +++++ plugin/core/views.py | 5 +++++ plugin/hover.py | 24 ++++++++++++++---------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/plugin/core/protocol.py b/plugin/core/protocol.py index 7df2d7e4b..af976fdff 100644 --- a/plugin/core/protocol.py +++ b/plugin/core/protocol.py @@ -72,6 +72,11 @@ class InsertTextMode: 'position': Position, }, total=True) +ExperimentalTextDocumentRangeParams = TypedDict('ExperimentalTextDocumentRangeParams', { + '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..359b4e116 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -5,6 +5,7 @@ from .protocol import DiagnosticRelatedInformation from .protocol import DiagnosticSeverity from .protocol import DocumentUri +from .protocol import ExperimentalTextDocumentRangeParams from .protocol import Location from .protocol import LocationLink from .protocol import MarkedString @@ -271,6 +272,10 @@ def text_document_position_params(view: sublime.View, location: int) -> TextDocu return {"textDocument": text_document_identifier(view), "position": position(view, location)} +def text_document_range_params(view: sublime.View, region: sublime.Region) -> ExperimentalTextDocumentRangeParams: + 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)} diff --git a/plugin/hover.py b/plugin/hover.py index 6f765864d..e72bdc934 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -4,12 +4,15 @@ from .core.promise import Promise from .core.protocol import Diagnostic from .core.protocol import Error +from .core.protocol import ExperimentalTextDocumentRangeParams from .core.protocol import Hover from .core.protocol import Position from .core.protocol import RangeLsp from .core.protocol import Request +from .core.protocol import TextDocumentPositionParams from .core.registry import LspTextCommand from .core.registry import windows +from .core.sessions import Session from .core.sessions import SessionBufferProtocol from .core.settings import userprefs from .core.typing import List, Optional, Dict, Tuple, Sequence, Union @@ -20,9 +23,9 @@ from .core.views import is_location_href from .core.views import make_command_link from .core.views import make_link -from .core.views import region_to_range 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 @@ -119,18 +122,19 @@ def run_async() -> None: sublime.set_timeout_async(run_async) + def _create_hover_request( + self, session: Session, point: int + ) -> Union[TextDocumentPositionParams, ExperimentalTextDocumentRangeParams]: + if session.get_capability('experimental.rangeHoverProvider'): + region = first_selection_region(self.view) + if region is not None and region.contains(point): + return text_document_range_params(self.view, region) + return text_document_position_params(self.view, point) + def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) -> None: hover_promises = [] # type: List[Promise[ResolvedHover]] for session in listener.sessions_async('hoverProvider'): - document_position = text_document_position_params(self.view, point) - range_hover_provider = session.get_capability('experimental.rangeHoverProvider') - if range_hover_provider: - region = first_selection_region(self.view) - if region is not None: - if region.contains(point): - # hovering selection or only text was selected - document_position.pop('position', None) - document_position['range'] = region_to_range(self.view, region).to_lsp() + document_position = self._create_hover_request(session, point) hover_promises.append(session.send_request_task( Request("textDocument/hover", document_position, self.view) )) From e47c1f4aab02302f918349cec185770e5e27c311 Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Fri, 19 Nov 2021 22:37:17 +0100 Subject: [PATCH 18/19] change functions order --- plugin/hover.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugin/hover.py b/plugin/hover.py index e72bdc934..e0996c3a9 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -122,15 +122,6 @@ def run_async() -> None: sublime.set_timeout_async(run_async) - def _create_hover_request( - self, session: Session, point: int - ) -> Union[TextDocumentPositionParams, ExperimentalTextDocumentRangeParams]: - if session.get_capability('experimental.rangeHoverProvider'): - region = first_selection_region(self.view) - if region is not None and region.contains(point): - return text_document_range_params(self.view, region) - return text_document_position_params(self.view, point) - def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) -> None: hover_promises = [] # type: List[Promise[ResolvedHover]] for session in listener.sessions_async('hoverProvider'): @@ -141,6 +132,15 @@ def request_symbol_hover_async(self, listener: AbstractViewListener, point: int) Promise.all(hover_promises).then(lambda responses: self._on_all_settled(responses, listener, point)) + def _create_hover_request( + self, session: Session, point: int + ) -> Union[TextDocumentPositionParams, ExperimentalTextDocumentRangeParams]: + if session.get_capability('experimental.rangeHoverProvider'): + region = first_selection_region(self.view) + if region is not None and region.contains(point): + return text_document_range_params(self.view, region) + return text_document_position_params(self.view, point) + def _on_all_settled(self, responses: List[ResolvedHover], listener: AbstractViewListener, point: int) -> None: hovers = [] # type: List[Hover] errors = [] # type: List[Error] From 2c66b1ab4863f3d93866407c4afe3db48161b96d Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Tue, 23 Nov 2021 20:12:22 +0100 Subject: [PATCH 19/19] Send position along range --- plugin/core/protocol.py | 1 + plugin/core/views.py | 9 +++++++-- plugin/hover.py | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/plugin/core/protocol.py b/plugin/core/protocol.py index af976fdff..7ef381fc9 100644 --- a/plugin/core/protocol.py +++ b/plugin/core/protocol.py @@ -74,6 +74,7 @@ class InsertTextMode: ExperimentalTextDocumentRangeParams = TypedDict('ExperimentalTextDocumentRangeParams', { 'textDocument': TextDocumentIdentifier, + 'position': Position, 'range': RangeLsp, }, total=True) diff --git a/plugin/core/views.py b/plugin/core/views.py index 359b4e116..8edcae6e1 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -272,8 +272,13 @@ def text_document_position_params(view: sublime.View, location: int) -> TextDocu return {"textDocument": text_document_identifier(view), "position": position(view, location)} -def text_document_range_params(view: sublime.View, region: sublime.Region) -> ExperimentalTextDocumentRangeParams: - return {"textDocument": text_document_identifier(view), "range": region_to_range(view, region).to_lsp()} +def text_document_range_params(view: sublime.View, location: int, + region: sublime.Region) -> ExperimentalTextDocumentRangeParams: + return { + "textDocument": text_document_identifier(view), + "position": position(view, location), + "range": region_to_range(view, region).to_lsp() + } def did_open_text_document_params(view: sublime.View, language_id: str) -> Dict[str, Any]: diff --git a/plugin/hover.py b/plugin/hover.py index e0996c3a9..40ae7337f 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -138,7 +138,7 @@ def _create_hover_request( if session.get_capability('experimental.rangeHoverProvider'): region = first_selection_region(self.view) if region is not None and region.contains(point): - return text_document_range_params(self.view, region) + return text_document_range_params(self.view, point, region) return text_document_position_params(self.view, point) def _on_all_settled(self, responses: List[ResolvedHover], listener: AbstractViewListener, point: int) -> None: