diff --git a/src/solidlsp/language_servers/al_language_server.py b/src/solidlsp/language_servers/al_language_server.py index c2bad1e2..30ccbcaa 100644 --- a/src/solidlsp/language_servers/al_language_server.py +++ b/src/solidlsp/language_servers/al_language_server.py @@ -24,45 +24,6 @@ from solidlsp.settings import SolidLSPSettings log = logging.getLogger(__name__) -# Regex pattern to match AL object names like: -# - 'Table 50000 "TEST Customer"' -> captures 'TEST Customer' -# - 'Codeunit 50000 CustomerMgt' -> captures 'CustomerMgt' -# - 'Interface IPaymentProcessor' -> captures 'IPaymentProcessor' -# - 'Enum 50000 CustomerType' -> captures 'CustomerType' -# Pattern: [] (|) -_AL_OBJECT_NAME_PATTERN = re.compile( - r"^(?:Table|Page|Codeunit|Enum|Interface|Report|Query|XMLPort|PermissionSet|" - r"PermissionSetExtension|Profile|PageExtension|TableExtension|EnumExtension|" - r"PageCustomization|ReportExtension|ControlAddin|DotNetPackage)" # Object type - r"(?:\s+\d+)?" # Optional object ID - r"\s+" # Required space before name - r'(?:"([^"]+)"|(\S+))$' # Quoted name (group 1) or unquoted identifier (group 2) -) - - -def extract_al_display_name(full_name: str) -> str: - """ - Extract the display name from an AL symbol's full name. - - AL Language Server returns symbol names in format: - - 'Table 50000 "TEST Customer"' -> 'TEST Customer' - - 'Codeunit 50000 CustomerMgt' -> 'CustomerMgt' - - 'Interface IPaymentProcessor' -> 'IPaymentProcessor' - - 'fields' -> 'fields' (non-AL-object symbols pass through unchanged) - - Args: - full_name: The full symbol name as returned by AL Language Server - - Returns: - The extracted display name for matching, or the original name if not an AL object - - """ - match = _AL_OBJECT_NAME_PATTERN.match(full_name) - if match: - # Return quoted name (group 1) or unquoted name (group 2) - return match.group(1) or match.group(2) or full_name - return full_name - class ALLanguageServer(SolidLanguageServer): """ @@ -79,6 +40,45 @@ class ALLanguageServer(SolidLanguageServer): - File opening requirement before symbol retrieval """ + # Regex pattern to match AL object names like: + # - 'Table 50000 "TEST Customer"' -> captures 'TEST Customer' + # - 'Codeunit 50000 CustomerMgt' -> captures 'CustomerMgt' + # - 'Interface IPaymentProcessor' -> captures 'IPaymentProcessor' + # - 'Enum 50000 CustomerType' -> captures 'CustomerType' + # Pattern: [] (|) + _AL_OBJECT_NAME_PATTERN = re.compile( + r"^(?:Table|Page|Codeunit|Enum|Interface|Report|Query|XMLPort|PermissionSet|" + r"PermissionSetExtension|Profile|PageExtension|TableExtension|EnumExtension|" + r"PageCustomization|ReportExtension|ControlAddin|DotNetPackage)" # Object type + r"(?:\s+\d+)?" # Optional object ID + r"\s+" # Required space before name + r'(?:"([^"]+)"|(\S+))$' # Quoted name (group 1) or unquoted identifier (group 2) + ) + + @staticmethod + def _extract_al_display_name(full_name: str) -> str: + """ + Extract the display name from an AL symbol's full name. + + AL Language Server returns symbol names in format: + - 'Table 50000 "TEST Customer"' -> 'TEST Customer' + - 'Codeunit 50000 CustomerMgt' -> 'CustomerMgt' + - 'Interface IPaymentProcessor' -> 'IPaymentProcessor' + - 'fields' -> 'fields' (non-AL-object symbols pass through unchanged) + + Args: + full_name: The full symbol name as returned by AL Language Server + + Returns: + The extracted display name for matching, or the original name if not an AL object + + """ + match = ALLanguageServer._AL_OBJECT_NAME_PATTERN.match(full_name) + if match: + # Return quoted name (group 1) or unquoted name (group 2) + return match.group(1) or match.group(2) or full_name + return full_name + def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings): """ Initialize the AL Language Server. @@ -1025,7 +1025,7 @@ class ALLanguageServer(SolidLanguageServer): # Normalize names by stripping AL object metadata, storing originals for hover def normalize_name(symbol: UnifiedSymbolInformation) -> None: original_name = symbol["name"] - normalized_name = extract_al_display_name(original_name) + normalized_name = self._extract_al_display_name(original_name) # Store original name if it was normalized (for hover injection) # Only store if we have valid position data to avoid false matches at (0, 0) diff --git a/test/solidlsp/al/test_al_basic.py b/test/solidlsp/al/test_al_basic.py index d103aba2..51bae0b7 100644 --- a/test/solidlsp/al/test_al_basic.py +++ b/test/solidlsp/al/test_al_basic.py @@ -4,7 +4,7 @@ import pytest from serena.symbol import LanguageServerSymbol from solidlsp import SolidLanguageServer -from solidlsp.language_servers.al_language_server import extract_al_display_name +from solidlsp.language_servers.al_language_server import ALLanguageServer from solidlsp.ls_config import Language from solidlsp.ls_utils import SymbolUtils from test.conftest import language_tests_enabled @@ -13,49 +13,49 @@ pytestmark = [pytest.mark.al, pytest.mark.skipif(not language_tests_enabled(Lang class TestExtractALDisplayName: - """Tests for the extract_al_display_name function.""" + """Tests for the ALLanguageServer._extract_al_display_name method.""" def test_table_with_quoted_name(self) -> None: """Test extraction from Table with quoted name.""" - assert extract_al_display_name('Table 50000 "TEST Customer"') == "TEST Customer" + assert ALLanguageServer._extract_al_display_name('Table 50000 "TEST Customer"') == "TEST Customer" def test_page_with_quoted_name(self) -> None: """Test extraction from Page with quoted name.""" - assert extract_al_display_name('Page 50001 "TEST Customer Card"') == "TEST Customer Card" + assert ALLanguageServer._extract_al_display_name('Page 50001 "TEST Customer Card"') == "TEST Customer Card" def test_codeunit_unquoted(self) -> None: """Test extraction from Codeunit with unquoted name.""" - assert extract_al_display_name("Codeunit 50000 CustomerMgt") == "CustomerMgt" + assert ALLanguageServer._extract_al_display_name("Codeunit 50000 CustomerMgt") == "CustomerMgt" def test_enum_unquoted(self) -> None: """Test extraction from Enum with unquoted name.""" - assert extract_al_display_name("Enum 50000 CustomerType") == "CustomerType" + assert ALLanguageServer._extract_al_display_name("Enum 50000 CustomerType") == "CustomerType" def test_interface_no_id(self) -> None: """Test extraction from Interface (no ID).""" - assert extract_al_display_name("Interface IPaymentProcessor") == "IPaymentProcessor" + assert ALLanguageServer._extract_al_display_name("Interface IPaymentProcessor") == "IPaymentProcessor" def test_table_extension(self) -> None: """Test extraction from TableExtension.""" - assert extract_al_display_name('TableExtension 50000 "Ext Customer"') == "Ext Customer" + assert ALLanguageServer._extract_al_display_name('TableExtension 50000 "Ext Customer"') == "Ext Customer" def test_page_extension(self) -> None: """Test extraction from PageExtension.""" - assert extract_al_display_name('PageExtension 50000 "My Page Ext"') == "My Page Ext" + assert ALLanguageServer._extract_al_display_name('PageExtension 50000 "My Page Ext"') == "My Page Ext" def test_non_al_object_unchanged(self) -> None: """Test that non-AL-object names pass through unchanged.""" - assert extract_al_display_name("fields") == "fields" - assert extract_al_display_name("CreateCustomer") == "CreateCustomer" - assert extract_al_display_name("Name") == "Name" + assert ALLanguageServer._extract_al_display_name("fields") == "fields" + assert ALLanguageServer._extract_al_display_name("CreateCustomer") == "CreateCustomer" + assert ALLanguageServer._extract_al_display_name("Name") == "Name" def test_report_with_quoted_name(self) -> None: """Test extraction from Report.""" - assert extract_al_display_name('Report 50000 "Sales Invoice"') == "Sales Invoice" + assert ALLanguageServer._extract_al_display_name('Report 50000 "Sales Invoice"') == "Sales Invoice" def test_query_unquoted(self) -> None: """Test extraction from Query.""" - assert extract_al_display_name("Query 50000 CustomerQuery") == "CustomerQuery" + assert ALLanguageServer._extract_al_display_name("Query 50000 CustomerQuery") == "CustomerQuery" @pytest.mark.al