diff --git a/CHANGELOG.md b/CHANGELOG.md index aead8ef5..a8cd7b37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,10 @@ Status of the `main` branch. Changes prior to the next official version change w its global state under ``~/Library``; Serena now gives the child process an isolated home-directory view via ``solidity_state_dir`` without changing the parent process's ``HOME`` (#1817) - Add Fatou support as an alternative Julia language server (`julia_fatou`) + - Fix: C# properties/fields whose type contains a literal `(`, e.g. a tuple type like + `(int X, string Y)`, had their name corrupted to include a trailing `:` because the + parenthesis in the type was mistaken for a method's parameter list; `find_symbol` on + the real name then returned nothing - Fix: Nextflow's `_flush_deferred_workspace_scan` marked the workspace scan flushed even when both of its `completion` probes failed, permanently skipping the flush (and silencing retries) for the rest of the session (#1871) diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index bef929d3..ce34f2e6 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -257,7 +257,7 @@ class CSharpLanguageServer(SolidLanguageServer): return hover def _document_symbols_cache_fingerprint(self) -> Hashable | None: - normalize_symbol_name_version = 1 + normalize_symbol_name_version = 2 return normalize_symbol_name_version def _normalize_symbol_name(self, symbol: RawDocumentSymbol, relative_file_path: str) -> str: @@ -301,15 +301,19 @@ class CSharpLanguageServer(SolidLanguageServer): "Add(int, int) : int" -> ("Add", "(int, int) : int") "ToString()" -> ("ToString", "()") "SimpleMethod" -> ("SimpleMethod", "") + "Position : (int X, string Y)" -> ("Position", ": (int X, string Y)") Returns: Tuple of (base_name, type_info) """ - # Check for property pattern: "Name : Type" - if " : " in roslyn_name and "(" not in roslyn_name: + # Check for property pattern: "Name : Type". The '(' guard must look only at the + # name segment before the first " : ", not the whole string, since a tuple type + # ("(int X, string Y)") legitimately contains parentheses. + if " : " in roslyn_name: base_name, type_part = roslyn_name.split(" : ", 1) - return base_name.strip(), f": {type_part.strip()}" + if "(" not in base_name: + return base_name.strip(), f": {type_part.strip()}" # Check for method pattern: "MethodName(params) : ReturnType" if "(" in roslyn_name: diff --git a/test/solidlsp/csharp/test_csharp_basic.py b/test/solidlsp/csharp/test_csharp_basic.py index 5f0a0107..1babaf2e 100644 --- a/test/solidlsp/csharp/test_csharp_basic.py +++ b/test/solidlsp/csharp/test_csharp_basic.py @@ -7,6 +7,7 @@ import pytest from solidlsp import SolidLanguageServer from solidlsp.language_servers.csharp_language_server import ( + CSharpLanguageServer, breadth_first_file_scan, find_solution_or_project_file, ) @@ -200,6 +201,28 @@ class TestCSharpLanguageServer: ), f"Expected ConsoleGreeter.FormatGreeting symbol, got: {implementing_symbols}" +class TestCSharpExtractBaseNameAndType: + """Regression tests for _extract_base_name_and_type, no running language server needed.""" + + @pytest.mark.parametrize( + ("roslyn_name", "expected"), + [ + # Property whose type is a tuple: the literal '(' in the type must not be + # mistaken for a method's parameter list. + ("Position : (int X, string Y)", ("Position", ": (int X, string Y)")), + ("Name : string", ("Name", ": string")), + ("Add(int, int) : int", ("Add", "(int, int) : int")), + ("ToString()", ("ToString", "()")), + ("SimpleMethod", ("SimpleMethod", "")), + # Both still have a '(' before the first " : ", so they keep the method branch. + ("GetPair() : (int, int)", ("GetPair", "() : (int, int)")), + ("Merge((int, int) a, (int, int) b) : void", ("Merge", "((int, int) a, (int, int) b) : void")), + ], + ) + def test_extract_base_name_and_type(self, roslyn_name: str, expected: tuple[str, str]) -> None: + assert CSharpLanguageServer._extract_base_name_and_type(roslyn_name) == expected + + @pytest.mark.csharp class TestCSharpSolutionProjectOpening: """Test C# language server solution and project opening functionality."""