From ae4ae5740f17210fed8a001a29a9a215901f90ba Mon Sep 17 00:00:00 2001 From: David Bernazal Date: Tue, 1 Jul 2025 08:55:40 -0500 Subject: [PATCH] Fix ready signal and ignore Elixir tests using skipif --- .../elixir_tools/elixir_tools.py | 31 ++++++++++++++++++- test/solidlsp/elixir/__init__.py | 31 ++++++++++++++++++- test/solidlsp/elixir/test_elixir_basic.py | 9 +++++- .../elixir/test_elixir_ignored_dirs.py | 9 ++++-- .../elixir/test_elixir_integration.py | 9 +++++- .../elixir/test_elixir_symbol_retrieval.py | 8 ++++- 6 files changed, 90 insertions(+), 7 deletions(-) diff --git a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py index 2e8a3ce..4491b59 100644 --- a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py +++ b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py @@ -162,6 +162,9 @@ class ElixirTools(SolidLanguageServer): ) self.server_ready = threading.Event() self.request_id = 0 + + # Set generous timeout for Next LS which can be slow to initialize and respond + self.set_request_timeout(60.0) # 60 seconds for all environments def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ @@ -201,12 +204,29 @@ class ElixirTools(SolidLanguageServer): def check_server_ready(params): # Next LS sends progress notifications when it's ready + # Check for various completion signals from Next LS + value = params.get("value", {}) + token = params.get("token", "") + + # Next LS sends different kinds of progress notifications + if (value.get("kind") == "end" or + "ready" in str(value).lower() or + "complete" in str(value).lower() or + "indexing" in str(token).lower() and value.get("kind") == "end"): + self.logger.log(f"Next LS ready signal detected", logging.INFO) + self.server_ready.set() + + def work_done_progress(params): + # Handle work done progress notifications that might indicate readiness if params.get("value", {}).get("kind") == "end": + self.logger.log("Next LS work done progress completed", logging.INFO) self.server_ready.set() self.server.on_request("client/registerCapability", register_capability_handler) self.server.on_notification("window/logMessage", window_log_message) self.server.on_notification("$/progress", check_server_ready) + self.server.on_notification("window/workDoneProgress/create", do_nothing) + self.server.on_notification("$/workDoneProgress", work_done_progress) self.server.on_notification("textDocument/publishDiagnostics", do_nothing) self.logger.log("Starting Next LS server process", logging.INFO) @@ -235,4 +255,13 @@ class ElixirTools(SolidLanguageServer): self.completions_available.set() # Next LS may take some time to be ready, so we wait for the progress notification - self.server_ready.wait() \ No newline at end of file + # Use a timeout to avoid hanging indefinitely + ready_timeout = 30.0 + self.logger.log(f"Waiting up to {ready_timeout} seconds for Next LS to be ready...", logging.INFO) + + if self.server_ready.wait(timeout=ready_timeout): + self.logger.log("Next LS is ready", logging.INFO) + else: + error_msg = f"Next LS failed to initialize within {ready_timeout} seconds. This may indicate a problem with the Elixir installation, project compilation, or Next LS itself." + self.logger.log(error_msg, logging.ERROR) + raise RuntimeError(error_msg) \ No newline at end of file diff --git a/test/solidlsp/elixir/__init__.py b/test/solidlsp/elixir/__init__.py index 0519ecb..184fc8e 100644 --- a/test/solidlsp/elixir/__init__.py +++ b/test/solidlsp/elixir/__init__.py @@ -1 +1,30 @@ - \ No newline at end of file +import platform +from pathlib import Path + + +def _test_nextls_available() -> str: + """Test if Next LS is available and return error reason if not.""" + + # Check if we're on Windows (Next LS doesn't support Windows) + if platform.system() == "Windows": + return "Next LS does not support Windows" + + # Try to import and check Elixir availability + try: + from solidlsp.language_servers.elixir_tools.elixir_tools import ElixirTools + + # Check if Elixir is installed + elixir_version = ElixirTools._get_elixir_version() + if not elixir_version: + return "Elixir is not installed or not in PATH" + + return "" # No error, Next LS should be available + + except ImportError as e: + return f"Failed to import ElixirTools: {e}" + except Exception as e: + return f"Error checking Next LS availability: {e}" + + +NEXTLS_UNAVAILABLE_REASON = _test_nextls_available() +NEXTLS_UNAVAILABLE = bool(NEXTLS_UNAVAILABLE_REASON) \ No newline at end of file diff --git a/test/solidlsp/elixir/test_elixir_basic.py b/test/solidlsp/elixir/test_elixir_basic.py index fbcaff6..a95b41e 100644 --- a/test/solidlsp/elixir/test_elixir_basic.py +++ b/test/solidlsp/elixir/test_elixir_basic.py @@ -12,8 +12,15 @@ from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from solidlsp.ls_utils import SymbolUtils +from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON + +# These marks will be applied to all tests in this module +pytestmark = [ + pytest.mark.elixir, + pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") +] + -@pytest.mark.elixir class TestElixirBasic: """Basic Elixir language server functionality tests.""" diff --git a/test/solidlsp/elixir/test_elixir_ignored_dirs.py b/test/solidlsp/elixir/test_elixir_ignored_dirs.py index 2e51ec2..4e11f65 100644 --- a/test/solidlsp/elixir/test_elixir_ignored_dirs.py +++ b/test/solidlsp/elixir/test_elixir_ignored_dirs.py @@ -7,8 +7,13 @@ from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from test.conftest import create_ls -# This mark will be applied to all tests in this module -pytestmark = pytest.mark.elixir +from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON + +# These marks will be applied to all tests in this module +pytestmark = [ + pytest.mark.elixir, + pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") +] @pytest.fixture(scope="module") diff --git a/test/solidlsp/elixir/test_elixir_integration.py b/test/solidlsp/elixir/test_elixir_integration.py index 14a888c..2377f0e 100644 --- a/test/solidlsp/elixir/test_elixir_integration.py +++ b/test/solidlsp/elixir/test_elixir_integration.py @@ -12,8 +12,15 @@ from pathlib import Path from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language +from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON + +# These marks will be applied to all tests in this module +pytestmark = [ + pytest.mark.elixir, + pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") +] + -@pytest.mark.elixir class TestElixirIntegration: """Integration tests for Elixir language server with test repository.""" diff --git a/test/solidlsp/elixir/test_elixir_symbol_retrieval.py b/test/solidlsp/elixir/test_elixir_symbol_retrieval.py index 14d9eee..ed36b9e 100644 --- a/test/solidlsp/elixir/test_elixir_symbol_retrieval.py +++ b/test/solidlsp/elixir/test_elixir_symbol_retrieval.py @@ -14,7 +14,13 @@ from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from solidlsp.ls_types import SymbolKind -pytestmark = pytest.mark.elixir +from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON + +# These marks will be applied to all tests in this module +pytestmark = [ + pytest.mark.elixir, + pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") +] class TestElixirLanguageServerSymbols: