Skip to content

Commit 2ab807a

Browse files
authored
Merge pull request #1075 from oraios/fix/batch_hover_optimization
Fix: the request_info for a batch of symbols was actually never optimized
2 parents 66a3ca8 + 229ce04 commit 2ab807a

File tree

5 files changed

+47
-43
lines changed

5 files changed

+47
-43
lines changed

src/serena/symbol.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import serena.jetbrains.jetbrains_types as jb
1313
from solidlsp import SolidLanguageServer
14+
from solidlsp.ls import LSPFileBuffer
1415
from solidlsp.ls import ReferenceInSymbol as LSPReferenceInSymbol
1516
from solidlsp.ls_types import Position, SymbolKind, UnifiedSymbolInformation
1617

@@ -549,14 +550,14 @@ def __init__(self, ls: SolidLanguageServer | LanguageServerManager, agent: Union
549550
self._ls_manager: LanguageServerManager = ls_manager
550551
self.agent = agent
551552

552-
def _request_info(self, relative_file_path: str, line: int, column: int) -> str | None:
553+
def _request_info(self, relative_file_path: str, line: int, column: int, file_buffer: LSPFileBuffer | None = None) -> str | None:
553554
"""Retrieves information (in a sanitized format) about the symbol at the desired location,
554555
typically containing the docstring and signature.
555556
556557
Returns None if no information is available.
557558
"""
558559
lang_server = self.get_language_server(relative_file_path)
559-
hover_info = lang_server.request_hover(relative_file_path=relative_file_path, line=line, column=column)
560+
hover_info = lang_server.request_hover(relative_file_path=relative_file_path, line=line, column=column, file_buffer=file_buffer)
560561
if hover_info is None:
561562
return None
562563

@@ -651,26 +652,28 @@ def request_info_for_symbol_batch(
651652
t0_file = perf_counter() if debug_enabled else 0.0
652653
file_hover_lookups = 0
653654

654-
for sym in file_symbols:
655-
# Check budget before starting a new hover request
656-
# symbol_info_budget_seconds=0 disables the budget mechanism (the first inequality)
657-
if 0 < symbol_info_budget_seconds <= hover_spent_seconds:
658-
skipped_due_to_budget += 1
659-
info = None
660-
# log once when budget exceeded
661-
if skipped_due_to_budget == 1:
662-
log.debug("Skipping further hover operations due to budget exceeded")
663-
else:
664-
line = sym.line
665-
column = sym.column
666-
assert line is not None and column is not None # for mypy, we filtered invalid symbols above
667-
t0_hover = perf_counter()
668-
info = self._request_info(file_path, line, column)
669-
hover_spent_seconds += perf_counter() - t0_hover
670-
file_hover_lookups += 1
671-
total_hover_lookups += 1
672-
673-
info_by_symbol[sym] = info
655+
ls = self.get_language_server(file_path)
656+
with ls.open_file(file_path) as file_buffer:
657+
for sym in file_symbols:
658+
# Check budget before starting a new hover request
659+
# symbol_info_budget_seconds=0 disables the budget mechanism (the first inequality)
660+
if 0 < symbol_info_budget_seconds <= hover_spent_seconds:
661+
skipped_due_to_budget += 1
662+
info = None
663+
# log once when budget exceeded
664+
if skipped_due_to_budget == 1:
665+
log.debug("Skipping further hover operations due to budget exceeded")
666+
else:
667+
line = sym.line
668+
column = sym.column
669+
assert line is not None and column is not None # for mypy, we filtered invalid symbols above
670+
t0_hover = perf_counter()
671+
info = self._request_info(file_path, line, column, file_buffer=file_buffer)
672+
hover_spent_seconds += perf_counter() - t0_hover
673+
file_hover_lookups += 1
674+
total_hover_lookups += 1
675+
676+
info_by_symbol[sym] = info
674677

675678
if debug_enabled:
676679
file_elapsed_ms = (perf_counter() - t0_file) * 1000

src/solidlsp/language_servers/al_language_server.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,9 @@ def normalize_name(symbol: UnifiedSymbolInformation) -> None:
10551055
return document_symbols
10561056

10571057
@override
1058-
def request_hover(self, relative_file_path: str, line: int, column: int) -> ls_types.Hover | None:
1058+
def request_hover(
1059+
self, relative_file_path: str, line: int, column: int, file_buffer: LSPFileBuffer | None = None
1060+
) -> ls_types.Hover | None:
10591061
"""
10601062
Override to inject original AL object name (with type and ID) into hover responses.
10611063
@@ -1065,7 +1067,7 @@ def request_hover(self, relative_file_path: str, line: int, column: int) -> ls_t
10651067
# Normalize path separators for cross-platform compatibility (backslash → forward slash)
10661068
relative_file_path = self._normalize_path(relative_file_path)
10671069

1068-
hover = super().request_hover(relative_file_path, line, column)
1070+
hover = super().request_hover(relative_file_path, line, column, file_buffer=file_buffer)
10691071

10701072
if hover is None:
10711073
return None

src/solidlsp/language_servers/csharp_language_server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from overrides import override
1717

18-
from solidlsp.ls import DocumentSymbols, LanguageServerDependencyProvider, SolidLanguageServer
18+
from solidlsp.ls import DocumentSymbols, LanguageServerDependencyProvider, LSPFileBuffer, SolidLanguageServer
1919
from solidlsp.ls_config import LanguageServerConfig
2020
from solidlsp.ls_exceptions import SolidLSPException
2121
from solidlsp.ls_types import Hover, UnifiedSymbolInformation
@@ -206,14 +206,14 @@ def request_document_symbols(self, relative_file_path: str, file_buffer: Any = N
206206
return symbols
207207

208208
@override
209-
def request_hover(self, relative_file_path: str, line: int, column: int) -> Hover | None:
209+
def request_hover(self, relative_file_path: str, line: int, column: int, file_buffer: LSPFileBuffer | None = None) -> Hover | None:
210210
"""
211211
Override to inject original Roslyn symbol names (with type annotations) into hover responses.
212212
213213
When hovering over a symbol whose name was normalized, we prepend the original
214214
full name (e.g., 'Add(int, int) : int') to the hover content.
215215
"""
216-
hover = super().request_hover(relative_file_path, line, column)
216+
hover = super().request_hover(relative_file_path, line, column, file_buffer=file_buffer)
217217

218218
if hover is None:
219219
return None

src/solidlsp/language_servers/eclipse_jdtls.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ def do_nothing(params: dict) -> None:
849849
log.info("Startup complete")
850850

851851
@override
852-
def _request_hover(self, uri: str, line: int, column: int) -> ls_types.Hover | None:
852+
def _request_hover(self, file_buffer: LSPFileBuffer, line: int, column: int) -> ls_types.Hover | None:
853853
# Eclipse JDTLS lazily loads javadocs on first hover request, then caches them.
854854
# This means the first request often returns incomplete info (just the signature),
855855
# while subsequent requests return the full javadoc.
@@ -879,12 +879,12 @@ def content_score(result: ls_types.Hover | None) -> tuple[int, int]:
879879
return (1, len(contents))
880880

881881
max_retries = 5
882-
best_result = super()._request_hover(uri, line, column)
882+
best_result = super()._request_hover(file_buffer, line, column)
883883
best_score = content_score(best_result)
884884

885885
for _ in range(max_retries):
886886
sleep(0.05)
887-
new_result = super()._request_hover(uri, line, column)
887+
new_result = super()._request_hover(file_buffer, line, column)
888888
new_score = content_score(new_result)
889889
if new_score > best_score:
890890
best_result = new_result

src/solidlsp/ls.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,8 @@ def _open_file_context(
756756
be opened in the LS later by calling the `ensure_open_in_ls` method on the returned LSPFileBuffer.
757757
"""
758758
if file_buffer is not None:
759+
expected_uri = pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri()
760+
assert file_buffer.uri == expected_uri, f"Inconsistency between provided {file_buffer.uri=} and {expected_uri=}"
759761
if open_in_ls:
760762
file_buffer.ensure_open_in_ls()
761763
yield file_buffer
@@ -1555,32 +1557,29 @@ def request_overview(self, within_relative_path: str) -> dict[str, list[UnifiedS
15551557
else:
15561558
return self.request_dir_overview(within_relative_path)
15571559

1558-
def request_hover(self, relative_file_path: str, line: int, column: int) -> ls_types.Hover | None:
1560+
def request_hover(
1561+
self, relative_file_path: str, line: int, column: int, file_buffer: LSPFileBuffer | None = None
1562+
) -> ls_types.Hover | None:
15591563
"""
15601564
Raise a [textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover) request to the Language Server
15611565
to find the hover information at the given line and column in the given file. Wait for the response and return the result.
15621566
15631567
:param relative_file_path: The relative path of the file that has the hover information
15641568
:param line: The line number of the symbol
15651569
:param column: The column number of the symbol
1570+
:param file_buffer: The file buffer to use for the request. If not provided, the file will be read from disk.
1571+
Can be used for optimizing number of file reads in downstream code
15661572
"""
1567-
with self.open_file(relative_file_path):
1568-
uri = pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri()
1569-
return self._request_hover(uri, line, column)
1573+
with self._open_file_context(relative_file_path, file_buffer=file_buffer) as fb:
1574+
return self._request_hover(fb, line, column)
15701575

1571-
def _request_hover(self, uri: str, line: int, column: int) -> ls_types.Hover | None:
1576+
def _request_hover(self, file_buffer: LSPFileBuffer, line: int, column: int) -> ls_types.Hover | None:
15721577
"""
1573-
Internal method that performs the actual hover request.
1574-
The file must already be open when calling this method.
1575-
Subclasses can override this to customize hover behavior (e.g., retries).
1576-
1577-
:param uri: The URI of the file
1578-
:param line: The line number of the symbol
1579-
:param column: The column number of the symbol
1578+
Performs the actual hover request.
15801579
"""
15811580
response = self.server.send.hover(
15821581
{
1583-
"textDocument": {"uri": uri},
1582+
"textDocument": {"uri": file_buffer.uri},
15841583
"position": {
15851584
"line": line,
15861585
"character": column,

0 commit comments

Comments
 (0)