diff --git a/sqlmesh/lsp/main.py b/sqlmesh/lsp/main.py index 8bb161319f..24b287d74c 100755 --- a/sqlmesh/lsp/main.py +++ b/sqlmesh/lsp/main.py @@ -496,10 +496,10 @@ def goto_definition( target_range = reference.target_range target_selection_range = reference.target_range - if reference.uri is not None: + if reference.path is not None: location_links.append( types.LocationLink( - target_uri=reference.uri, + target_uri=URI.from_path(reference.path).value, target_selection_range=target_selection_range, target_range=target_range, origin_selection_range=reference.range, @@ -523,9 +523,9 @@ def find_references( # Convert references to Location objects locations = [ - types.Location(uri=ref.uri, range=ref.range) + types.Location(uri=URI.from_path(ref.path).value, range=ref.range) for ref in all_references - if ref.uri is not None + if ref.path is not None ] return locations if locations else None diff --git a/sqlmesh/lsp/reference.py b/sqlmesh/lsp/reference.py index 342cd86893..6849129a7e 100644 --- a/sqlmesh/lsp/reference.py +++ b/sqlmesh/lsp/reference.py @@ -29,7 +29,7 @@ class LSPModelReference(PydanticModel): """A LSP reference to a model, excluding external models.""" type: t.Literal["model"] = "model" - uri: str + path: Path range: Range markdown_description: t.Optional[str] = None @@ -40,9 +40,9 @@ class LSPExternalModelReference(PydanticModel): type: t.Literal["external_model"] = "external_model" range: Range target_range: t.Optional[Range] = None - uri: t.Optional[str] = None - """The URI of the external model, typically a YAML file, it is optional because - external models can be unregistered and so they URI is not available.""" + path: t.Optional[Path] = None + """The path of the external model, typically a YAML file, it is optional because + external models can be unregistered and so the path is not available.""" markdown_description: t.Optional[str] = None @@ -51,7 +51,7 @@ class LSPCteReference(PydanticModel): """A LSP reference to a CTE.""" type: t.Literal["cte"] = "cte" - uri: str + path: Path range: Range target_range: Range @@ -60,7 +60,7 @@ class LSPMacroReference(PydanticModel): """A LSP reference to a macro.""" type: t.Literal["macro"] = "macro" - uri: str + path: Path range: Range target_range: Range markdown_description: t.Optional[str] = None @@ -209,7 +209,7 @@ def get_model_definitions_for_a_path( references.append( LSPCteReference( - uri=document_uri.value, # Same file + path=document_uri.to_path(), # Same file range=table_range, target_range=target_range, ) @@ -219,7 +219,7 @@ def get_model_definitions_for_a_path( scope=scope, reference_name=table.name, read_file=read_file, - referenced_model_uri=document_uri, + referenced_model_path=document_uri.to_path(), description="", reference_type="cte", cte_target_range=target_range, @@ -270,7 +270,6 @@ def get_model_definitions_for_a_path( # Check whether the path exists if not referenced_model_path.is_file(): continue - referenced_model_uri = URI.from_path(referenced_model_path) # Extract metadata for positioning table_meta = TokenPositionDetails.from_meta(table.this.meta) @@ -299,7 +298,7 @@ def get_model_definitions_for_a_path( ) references.append( LSPExternalModelReference( - uri=referenced_model_uri.value, + path=referenced_model_path, range=Range( start=to_lsp_position(start_pos_sqlmesh), end=to_lsp_position(end_pos_sqlmesh), @@ -313,7 +312,7 @@ def get_model_definitions_for_a_path( scope=scope, reference_name=normalized_reference_name, read_file=read_file, - referenced_model_uri=referenced_model_uri, + referenced_model_path=referenced_model_path, description=description, yaml_target_range=yaml_target_range, reference_type="external_model", @@ -324,7 +323,7 @@ def get_model_definitions_for_a_path( else: references.append( LSPModelReference( - uri=referenced_model_uri.value, + path=referenced_model_path, range=Range( start=to_lsp_position(start_pos_sqlmesh), end=to_lsp_position(end_pos_sqlmesh), @@ -337,7 +336,7 @@ def get_model_definitions_for_a_path( scope=scope, reference_name=normalized_reference_name, read_file=read_file, - referenced_model_uri=referenced_model_uri, + referenced_model_path=referenced_model_path, description=description, reference_type="model", default_catalog=lint_context.context.default_catalog, @@ -481,10 +480,9 @@ def get_macro_reference( return None # Create a reference to the macro definition - macro_uri = URI.from_path(path) return LSPMacroReference( - uri=macro_uri.value, + path=path, range=to_lsp_range(macro_range), target_range=Range( start=Position(line=start_line - 1, character=0), @@ -517,7 +515,7 @@ def get_built_in_macro_reference(macro_name: str, macro_range: Range) -> t.Optio end_line_number = line_number + len(source_lines) - 1 return LSPMacroReference( - uri=URI.from_path(Path(filename)).value, + path=Path(filename), range=macro_range, target_range=Range( start=Position(line=line_number - 1, character=0), @@ -559,12 +557,12 @@ def get_model_find_all_references( assert isinstance(model_at_position, LSPModelReference) # for mypy - target_model_uri = model_at_position.uri + target_model_path = model_at_position.path # Start with the model definition all_references: t.List[LSPModelReference] = [ LSPModelReference( - uri=model_at_position.uri, + path=model_at_position.path, range=Range( start=Position(line=0, character=0), end=Position(line=0, character=0), @@ -575,7 +573,7 @@ def get_model_find_all_references( # Then add references from the current file current_file_refs = filter( - lambda ref: isinstance(ref, LSPModelReference) and ref.uri == target_model_uri, + lambda ref: isinstance(ref, LSPModelReference) and ref.path == target_model_path, get_model_definitions_for_a_path(lint_context, document_uri), ) @@ -584,7 +582,7 @@ def get_model_find_all_references( all_references.append( LSPModelReference( - uri=document_uri.value, + path=document_uri.to_path(), range=ref.range, markdown_description=ref.markdown_description, ) @@ -600,7 +598,7 @@ def get_model_find_all_references( # Get model references that point to the target model matching_refs = filter( - lambda ref: isinstance(ref, LSPModelReference) and ref.uri == target_model_uri, + lambda ref: isinstance(ref, LSPModelReference) and ref.path == target_model_path, get_model_definitions_for_a_path(lint_context, file_uri), ) @@ -609,7 +607,7 @@ def get_model_find_all_references( all_references.append( LSPModelReference( - uri=file_uri.value, + path=path, range=ref.range, markdown_description=ref.markdown_description, ) @@ -662,7 +660,7 @@ def get_cte_references( # Add the CTE definition matching_references = [ LSPCteReference( - uri=document_uri.value, + path=document_uri.to_path(), range=target_cte_definition_range, target_range=target_cte_definition_range, ) @@ -673,7 +671,7 @@ def get_cte_references( if ref.target_range == target_cte_definition_range: matching_references.append( LSPCteReference( - uri=document_uri.value, + path=document_uri.to_path(), range=ref.range, target_range=ref.target_range, ) @@ -713,13 +711,13 @@ def get_macro_find_all_references( assert isinstance(macro_at_position, LSPMacroReference) # for mypy - target_macro_uri = macro_at_position.uri + target_macro_path = macro_at_position.path target_macro_target_range = macro_at_position.target_range # Start with the macro definition all_references: t.List[LSPMacroReference] = [ LSPMacroReference( - uri=target_macro_uri, + path=target_macro_path, range=target_macro_target_range, target_range=target_macro_target_range, markdown_description=None, @@ -733,7 +731,7 @@ def get_macro_find_all_references( # Get macro references that point to the same macro definition matching_refs = filter( lambda ref: isinstance(ref, LSPMacroReference) - and ref.uri == target_macro_uri + and ref.path == target_macro_path and ref.target_range == target_macro_target_range, get_macro_definitions_for_a_path(lsp_context, file_uri), ) @@ -742,7 +740,7 @@ def get_macro_find_all_references( assert isinstance(ref, LSPMacroReference) # for mypy all_references.append( LSPMacroReference( - uri=file_uri.value, + path=path, range=ref.range, target_range=ref.target_range, markdown_description=ref.markdown_description, @@ -822,7 +820,7 @@ def _process_column_references( scope: t.Any, reference_name: str, read_file: t.List[str], - referenced_model_uri: URI, + referenced_model_path: Path, description: t.Optional[str] = None, yaml_target_range: t.Optional[Range] = None, reference_type: t.Literal["model", "external_model", "cte"] = "model", @@ -837,7 +835,7 @@ def _process_column_references( scope: The SQL scope to search for columns reference_name: The full reference name (may include database/catalog) read_file: The file content as list of lines - referenced_model_uri: URI of the referenced model + referenced_model_path: Path of the referenced model description: Markdown description for the reference yaml_target_range: Target range for external models (YAML files) reference_type: Type of reference - "model", "external_model", or "cte" @@ -857,7 +855,7 @@ def _process_column_references( table_range = _get_column_table_range(column, read_file) references.append( LSPCteReference( - uri=referenced_model_uri.value, + path=referenced_model_path, range=table_range, target_range=cte_target_range, ) @@ -875,7 +873,7 @@ def _process_column_references( if reference_type == "external_model": references.append( LSPExternalModelReference( - uri=referenced_model_uri.value, + path=referenced_model_path, range=table_range, markdown_description=description, target_range=yaml_target_range, @@ -884,7 +882,7 @@ def _process_column_references( else: references.append( LSPModelReference( - uri=referenced_model_uri.value, + path=referenced_model_path, range=table_range, markdown_description=description, ) diff --git a/sqlmesh/lsp/rename.py b/sqlmesh/lsp/rename.py index 0dbe2594ea..c388b7a305 100644 --- a/sqlmesh/lsp/rename.py +++ b/sqlmesh/lsp/rename.py @@ -90,7 +90,7 @@ def _rename_cte(cte_references: t.List[LSPCteReference], new_name: str) -> Works changes: t.Dict[str, t.List[TextEdit]] = {} for ref in cte_references: - uri = ref.uri + uri = URI.from_path(ref.path).value if uri not in changes: changes[uri] = [] diff --git a/tests/lsp/test_reference.py b/tests/lsp/test_reference.py index 736b995410..b78ed8145c 100644 --- a/tests/lsp/test_reference.py +++ b/tests/lsp/test_reference.py @@ -25,9 +25,9 @@ def test_reference() -> None: references = get_model_definitions_for_a_path(lsp_context, active_customers_uri) assert len(references) == 1 - uri = references[0].uri - assert uri is not None - assert URI(uri) == URI.from_path(sushi_customers_path) + path = references[0].path + assert path is not None + assert path == sushi_customers_path # Check that the reference in the correct range is sushi.customers path = active_customers_uri.to_path() @@ -61,7 +61,7 @@ def test_reference_with_alias() -> None: with open(waiter_revenue_by_day_path, "r") as file: read_file = file.readlines() - assert references[0].uri.endswith("orders.py") + assert str(references[0].path).endswith("orders.py") assert get_string_from_range(read_file, references[0].range) == "sushi.orders" assert ( references[0].markdown_description @@ -76,9 +76,9 @@ def test_reference_with_alias() -> None: | end_ts | INT | | | event_date | DATE | |""" ) - assert references[1].uri.endswith("order_items.py") + assert str(references[1].path).endswith("order_items.py") assert get_string_from_range(read_file, references[1].range) == "sushi.order_items" - assert references[2].uri.endswith("items.py") + assert str(references[2].path).endswith("items.py") assert get_string_from_range(read_file, references[2].range) == "sushi.items" @@ -102,7 +102,7 @@ def test_standalone_audit_reference() -> None: references = get_model_definitions_for_a_path(lsp_context, URI.from_path(audit_path)) assert len(references) == 1 - assert references[0].uri == URI.from_path(items_path).value + assert references[0].path == items_path # Check that the reference in the correct range is sushi.items with open(audit_path, "r") as file: @@ -161,7 +161,7 @@ def test_filter_references_by_position() -> None: position_inside = Position(line=middle_line, character=middle_char) filtered = list(filter(by_position(position_inside), all_references)) assert len(filtered) == 1 - assert filtered[0].uri == reference.uri + assert filtered[0].path == reference.path assert filtered[0].range == reference.range # For testing outside position, use a position before the current reference diff --git a/tests/lsp/test_reference_cte.py b/tests/lsp/test_reference_cte.py index 32cce8dc60..c6c56fd8a5 100644 --- a/tests/lsp/test_reference_cte.py +++ b/tests/lsp/test_reference_cte.py @@ -27,7 +27,7 @@ def test_cte_parsing(): position = Position(line=ranges[1].start.line, character=ranges[1].start.character + 4) references = get_references(lsp_context, URI.from_path(sushi_customers_path), position) assert len(references) == 1 - assert references[0].uri == URI.from_path(sushi_customers_path).value + assert references[0].path == sushi_customers_path assert isinstance(references[0], LSPCteReference) assert ( references[0].range.start.line == ranges[1].start.line @@ -42,7 +42,7 @@ def test_cte_parsing(): position = Position(line=ranges[1].start.line, character=ranges[1].start.character + 4) references = get_references(lsp_context, URI.from_path(sushi_customers_path), position) assert len(references) == 1 - assert references[0].uri == URI.from_path(sushi_customers_path).value + assert references[0].path == sushi_customers_path assert isinstance(references[0], LSPCteReference) assert ( references[0].range.start.line == ranges[1].start.line diff --git a/tests/lsp/test_reference_cte_find_all.py b/tests/lsp/test_reference_cte_find_all.py index 6a29224e75..dabe1589e2 100644 --- a/tests/lsp/test_reference_cte_find_all.py +++ b/tests/lsp/test_reference_cte_find_all.py @@ -28,7 +28,7 @@ def test_cte_find_all_references(): references = get_cte_references(lsp_context, URI.from_path(sushi_customers_path), position) # Should find the definition, FROM clause, and column prefix usages assert len(references) == 4 # definition + FROM + 2 column prefix uses - assert all(ref.uri == URI.from_path(sushi_customers_path).value for ref in references) + assert all(ref.path == sushi_customers_path for ref in references) reference_ranges = [ref.range for ref in references] for expected_range in ranges: @@ -46,7 +46,7 @@ def test_cte_find_all_references(): # Should find the same references assert len(references) == 4 # definition + FROM + 2 column prefix uses - assert all(ref.uri == URI.from_path(sushi_customers_path).value for ref in references) + assert all(ref.path == sushi_customers_path for ref in references) reference_ranges = [ref.range for ref in references] for expected_range in ranges: @@ -82,7 +82,7 @@ def test_cte_find_all_references_outer(): # Should find both the definition and the usage assert len(references) == 2 - assert all(ref.uri == URI.from_path(sushi_customers_path).value for ref in references) + assert all(ref.path == sushi_customers_path for ref in references) # Verify that we found both occurrences reference_ranges = [ref.range for ref in references] @@ -101,7 +101,7 @@ def test_cte_find_all_references_outer(): # Should find the same references assert len(references) == 2 - assert all(ref.uri == URI.from_path(sushi_customers_path).value for ref in references) + assert all(ref.path == sushi_customers_path for ref in references) reference_ranges = [ref.range for ref in references] for expected_range in ranges: diff --git a/tests/lsp/test_reference_external_model.py b/tests/lsp/test_reference_external_model.py index 43c873991e..8f2c2f7c1b 100644 --- a/tests/lsp/test_reference_external_model.py +++ b/tests/lsp/test_reference_external_model.py @@ -30,20 +30,18 @@ def test_reference() -> None: assert len(references) == 1 reference = references[0] assert isinstance(reference, LSPExternalModelReference) - uri = reference.uri - assert uri is not None - assert uri.endswith("external_models.yaml") + path = reference.path + assert path is not None + assert str(path).endswith("external_models.yaml") source_range = read_range_from_file(customers, to_sqlmesh_range(reference.range)) assert source_range == "raw.demographics" if reference.target_range is None: raise AssertionError("Reference target range should not be None") - uri = reference.uri - assert uri is not None - target_range = read_range_from_file( - URI(uri).to_path(), to_sqlmesh_range(reference.target_range) - ) + path = reference.path + assert path is not None + target_range = read_range_from_file(path, to_sqlmesh_range(reference.target_range)) assert target_range == "raw.demographics" @@ -60,7 +58,7 @@ def test_unregistered_external_model(tmp_path: Path): assert len(references) == 1 reference = references[0] assert isinstance(reference, LSPExternalModelReference) - assert reference.uri is None + assert reference.path is None assert reference.target_range is None assert reference.markdown_description == "Unregistered external model" assert read_range_from_file(model_path, to_sqlmesh_range(reference.range)) == "external_model" diff --git a/tests/lsp/test_reference_macro.py b/tests/lsp/test_reference_macro.py index 705a8b48e2..e287212dd2 100644 --- a/tests/lsp/test_reference_macro.py +++ b/tests/lsp/test_reference_macro.py @@ -25,5 +25,5 @@ def test_macro_references() -> None: # Check that all references point to the utils.py file for ref in macro_references: assert isinstance(ref, LSPMacroReference) - assert ref.uri.endswith("sushi/macros/utils.py") + assert URI.from_path(ref.path).value.endswith("sushi/macros/utils.py") assert ref.target_range is not None diff --git a/tests/lsp/test_reference_macro_find_all.py b/tests/lsp/test_reference_macro_find_all.py index 03f47d1dc2..328924599a 100644 --- a/tests/lsp/test_reference_macro_find_all.py +++ b/tests/lsp/test_reference_macro_find_all.py @@ -41,11 +41,11 @@ def test_find_all_references_for_macro_add_one(): assert len(all_references) >= 2, f"Expected at least 2 references, found {len(all_references)}" # Verify the macro definition is included - definition_refs = [ref for ref in all_references if "utils.py" in ref.uri] + definition_refs = [ref for ref in all_references if "utils.py" in str(ref.path)] assert len(definition_refs) >= 1, "Should include the macro definition in utils.py" # Verify the usage in top_waiters is included - usage_refs = [ref for ref in all_references if "top_waiters" in ref.uri] + usage_refs = [ref for ref in all_references if "top_waiters" in str(ref.path)] assert len(usage_refs) >= 1, "Should include the usage in top_waiters.sql" expected_files = { @@ -55,11 +55,11 @@ def test_find_all_references_for_macro_add_one(): } for expected_file, expectations in expected_files.items(): - file_refs = [ref for ref in all_references if expected_file in ref.uri] + file_refs = [ref for ref in all_references if expected_file in str(ref.path)] assert len(file_refs) >= 1, f"Should find at least one reference in {expected_file}" file_ref = file_refs[0] - file_path = URI(file_ref.uri).to_path() + file_path = file_ref.path sqlmesh_range = SQLMeshRange( start=SQLMeshPosition( @@ -104,8 +104,10 @@ def test_find_all_references_for_macro_multiply(): assert len(all_references) >= 2, f"Expected at least 2 references, found {len(all_references)}" # Verify both definition and usage are included - assert any("utils.py" in ref.uri for ref in all_references), "Should include macro definition" - assert any("top_waiters" in ref.uri for ref in all_references), "Should include usage" + assert any("utils.py" in str(ref.path) for ref in all_references), ( + "Should include macro definition" + ) + assert any("top_waiters" in str(ref.path) for ref in all_references), "Should include usage" def test_find_all_references_for_sql_literal_macro(): @@ -183,9 +185,11 @@ def test_multi_repo_macro_references(): assert len(all_references) == 2, f"Expected 2 references, found {len(all_references)}" # Verify references from repo_2 - assert any("repo_2" in ref.uri for ref in all_references), "Should find macro in repo_2" + assert any("repo_2" in str(ref.path) for ref in all_references), ( + "Should find macro in repo_2" + ) # But not references in repo_1 since despite identical name they're different macros - assert not any("repo_1" in ref.uri for ref in all_references), ( + assert not any("repo_1" in str(ref.path) for ref in all_references), ( "Shouldn't find macro in repo_1" ) diff --git a/tests/lsp/test_reference_macro_multi.py b/tests/lsp/test_reference_macro_multi.py index db4464ffac..8226085a1d 100644 --- a/tests/lsp/test_reference_macro_multi.py +++ b/tests/lsp/test_reference_macro_multi.py @@ -20,5 +20,5 @@ def test_macro_references_multirepo() -> None: assert len(macro_references) == 2 for ref in macro_references: assert isinstance(ref, LSPMacroReference) - assert ref.uri.endswith("multi/repo_2/macros/__init__.py") + assert str(URI.from_path(ref.path).value).endswith("multi/repo_2/macros/__init__.py") assert ref.target_range is not None diff --git a/tests/lsp/test_reference_model_find_all.py b/tests/lsp/test_reference_model_find_all.py index 7bb998150f..7c0077d6cd 100644 --- a/tests/lsp/test_reference_model_find_all.py +++ b/tests/lsp/test_reference_model_find_all.py @@ -35,7 +35,7 @@ def test_find_references_for_model_usages(): ) # Verify expected files are present - reference_files = {ref.uri for ref in references} + reference_files = {str(ref.path) for ref in references} expected_patterns = [ "orders", "customers", @@ -65,7 +65,7 @@ def test_find_references_for_model_usages(): for ref in references: matched_pattern = None for pattern in expected_patterns: - if pattern in ref.uri: + if pattern in str(ref.path): matched_pattern = pattern break @@ -136,7 +136,7 @@ def test_find_references_for_marketing_model(): ) # Verify files are present - reference_files = {ref.uri for ref in references} + reference_files = {str(ref.path) for ref in references} expected_patterns = ["marketing", "customers"] for pattern in expected_patterns: assert any(pattern in uri for uri in reference_files), ( @@ -169,7 +169,7 @@ def test_find_references_for_python_model(): assert len(references) == 5 # Verify expected files - reference_files = {ref.uri for ref in references} + reference_files = {str(ref.path) for ref in references} # Models and also the Audit which references it: assert_item_price_above_zero expected_patterns = [ @@ -222,13 +222,13 @@ def test_waiter_revenue_by_day_multiple_references(): ) # Count references in top_waiters file - top_waiters_refs = [ref for ref in references if "top_waiters" in ref.uri] + top_waiters_refs = [ref for ref in references if "top_waiters" in str(ref.path)] assert len(top_waiters_refs) == 3, ( f"Expected exactly 3 references in top_waiters, found {len(top_waiters_refs)}" ) # Verify model definition is included - assert any("waiter_revenue_by_day" in ref.uri for ref in references), ( + assert any("waiter_revenue_by_day" in str(ref.path) for ref in references), ( "Should include model definition" ) @@ -296,7 +296,7 @@ def test_audit_model_references(): assert len(references) == 5, "Should find references from audit files as well" - reference_files = {ref.uri for ref in references} + reference_files = {str(ref.path) for ref in references} # Models and also the Audit which references it: assert_item_price_above_zero expected_patterns = [