diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 9a19bf3a..a0d5408d 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -13,7 +13,6 @@ import threading import urllib.request import zipfile from pathlib import Path -from time import sleep from typing import cast from overrides import override @@ -22,7 +21,6 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_exceptions import SolidLSPException from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.ls_types import Location from solidlsp.ls_utils import PathUtils from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -739,8 +737,5 @@ class CSharpLanguageServer(SolidLanguageServer): self.logger.log(f"Opened project files: {project_files}", logging.DEBUG) @override - def request_references(self, relative_file_path: str, line: int, column: int) -> list[Location]: - # Like in the typescript LS, we need to wait here for the language server to - # get correct results that include cross-file references. - sleep(2) - return super().request_references(relative_file_path, line, column) + def _get_wait_time_for_cross_file_referencing(self) -> float: + return 1 diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index ccb5e8c4..361d7284 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -7,7 +7,6 @@ import os import pathlib import shutil import threading -from time import sleep from overrides import override from sensai.util.logging import LogTime @@ -250,12 +249,5 @@ class TypeScriptLanguageServer(SolidLanguageServer): self.completions_available.set() @override - # For some reason, the LS may need longer to process this, so we just retry - def _send_references_request(self, relative_file_path: str, line: int, column: int): - # TODO: The LS doesn't return references contained in other files if it doesn't sleep. This is - # despite the LS having processed requests already. I don't know what causes this, but sleeping - # one second helps. It may be that sleeping only once is enough but that's hard to reliably test. - # It may be that even this 1sec is not enough in larger TS projects, at some point we should find what - # causes this and solve it. - sleep(1) - return super()._send_references_request(relative_file_path, line, column) + def _get_wait_time_for_cross_file_referencing(self) -> float: + return 1 diff --git a/src/solidlsp/language_servers/vts_language_server.py b/src/solidlsp/language_servers/vts_language_server.py index c8596b89..b07e4c29 100644 --- a/src/solidlsp/language_servers/vts_language_server.py +++ b/src/solidlsp/language_servers/vts_language_server.py @@ -9,7 +9,6 @@ import os import pathlib import shutil import threading -from time import sleep from overrides import override @@ -231,8 +230,6 @@ class VtsLanguageServer(SolidLanguageServer): self.server_ready.set() self.completions_available.set() - # VTS may need longer to process references, so we include a sleep similar to TypeScript LS @override - def _send_references_request(self, relative_file_path: str, line: int, column: int): - sleep(1) - return super()._send_references_request(relative_file_path, line, column) + def _get_wait_time_for_cross_file_referencing(self) -> float: + return 1 diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index f4ff544f..9b888029 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -14,6 +14,7 @@ from collections.abc import Iterator from contextlib import contextmanager from copy import copy from pathlib import Path, PurePath +from time import sleep from typing import Self, Union, cast import pathspec @@ -323,6 +324,16 @@ class SolidLanguageServer(ABC): self._server_context = None self._request_timeout: float | None = None + self._has_waited_for_cross_file_references = False + + def _get_wait_time_for_cross_file_referencing(self) -> float: + """Meant to be overridden by subclasses for LS that don't have a reliable "finished initializing" signal. + + LS may return incomplete results on calls to `request_references` (only references found in the same file), + if the LS is not fully initialized yet. + """ + return 0 + def set_request_timeout(self, timeout: float | None) -> None: """ :param timeout: the timeout, in seconds, for requests to the language server. @@ -699,6 +710,12 @@ class SolidLanguageServer(ABC): ) raise SolidLSPException("Language Server not started") + if not self._has_waited_for_cross_file_references: + # Some LS require waiting for a while before they can return cross-file references. + # This is a workaround for such LS that don't have a reliable "finished initializing" signal. + sleep(self._get_wait_time_for_cross_file_referencing()) + self._has_waited_for_cross_file_references = True + with self.open_file(relative_file_path): try: response = self._send_references_request(relative_file_path, line=line, column=column) diff --git a/test/solidlsp/csharp/test_csharp_basic.py b/test/solidlsp/csharp/test_csharp_basic.py index a0c83a13..d4bbd8f1 100644 --- a/test/solidlsp/csharp/test_csharp_basic.py +++ b/test/solidlsp/csharp/test_csharp_basic.py @@ -1,6 +1,7 @@ import os import tempfile from pathlib import Path +from typing import cast from unittest.mock import Mock, patch import pytest @@ -110,16 +111,20 @@ class TestCSharpLanguageServer: refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1) # Should find references in both Program.cs and Models/Person.cs - ref_files = [ref.get("relativePath", "") for ref in refs] + ref_files = cast(list[str], [ref.get("relativePath", "") for ref in refs]) print(f"Found references: {refs}") print(f"Reference files: {ref_files}") # Check that we have references from both files assert any("Program.cs" in ref_file for ref_file in ref_files), "Should find reference in Program.cs" assert any( - "Models/Person.cs" in ref_file for ref_file in ref_files + os.path.join("Models", "Person.cs") in ref_file for ref_file in ref_files ), "Should find reference in Models/Person.cs where Calculator.Subtract is called" + # check for a second time, since the first call may trigger initialization and change the state of the LS + refs_second_call = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1) + assert refs_second_call == refs, "Second call to request_references should return the same results" + @pytest.mark.csharp class TestCSharpSolutionProjectOpening: