88from sqlmesh .core .linter .helpers import (
99 TokenPositionDetails ,
1010)
11- from sqlmesh .core .model .definition import SqlModel
11+ from sqlmesh .core .model .definition import SqlModel , ExternalModel
1212from sqlmesh .lsp .context import LSPContext , ModelTarget , AuditTarget
1313from sqlglot import exp
1414from sqlmesh .lsp .description import generate_markdown_description
2222from sqlmesh .core .model import Model
2323from sqlmesh import macro
2424import inspect
25+ from ruamel .yaml import YAML
2526
2627
2728class LSPModelReference (PydanticModel ):
28- """A LSP reference to a model."""
29+ """A LSP reference to a model, excluding external models ."""
2930
3031 type : t .Literal ["model" ] = "model"
3132 uri : str
3233 range : Range
3334 markdown_description : t .Optional [str ] = None
3435
3536
37+ class LSPExternalModelReference (PydanticModel ):
38+ """A LSP reference to an external model."""
39+
40+ type : t .Literal ["external_model" ] = "external_model"
41+ uri : str
42+ range : Range
43+ markdown_description : t .Optional [str ] = None
44+ target_range : t .Optional [Range ] = None
45+
46+
3647class LSPCteReference (PydanticModel ):
3748 """A LSP reference to a CTE."""
3849
@@ -53,7 +64,8 @@ class LSPMacroReference(PydanticModel):
5364
5465
5566Reference = t .Annotated [
56- t .Union [LSPModelReference , LSPCteReference , LSPMacroReference ], Field (discriminator = "type" )
67+ t .Union [LSPModelReference , LSPCteReference , LSPMacroReference , LSPExternalModelReference ],
68+ Field (discriminator = "type" ),
5769]
5870
5971
@@ -243,16 +255,38 @@ def get_model_definitions_for_a_path(
243255
244256 description = generate_markdown_description (referenced_model )
245257
246- references .append (
247- LSPModelReference (
248- uri = referenced_model_uri .value ,
249- range = Range (
250- start = to_lsp_position (start_pos_sqlmesh ),
251- end = to_lsp_position (end_pos_sqlmesh ),
252- ),
253- markdown_description = description ,
258+ # For external models in YAML files, find the specific model block
259+ if isinstance (referenced_model , ExternalModel ):
260+ yaml_target_range : t .Optional [Range ] = None
261+ if (
262+ referenced_model_path .suffix in (".yaml" , ".yml" )
263+ and referenced_model_path .is_file ()
264+ ):
265+ yaml_target_range = _get_yaml_model_range (
266+ referenced_model_path , referenced_model .name
267+ )
268+ references .append (
269+ LSPExternalModelReference (
270+ uri = referenced_model_uri .value ,
271+ range = Range (
272+ start = to_lsp_position (start_pos_sqlmesh ),
273+ end = to_lsp_position (end_pos_sqlmesh ),
274+ ),
275+ markdown_description = description ,
276+ target_range = yaml_target_range ,
277+ )
278+ )
279+ else :
280+ references .append (
281+ LSPModelReference (
282+ uri = referenced_model_uri .value ,
283+ range = Range (
284+ start = to_lsp_position (start_pos_sqlmesh ),
285+ end = to_lsp_position (end_pos_sqlmesh ),
286+ ),
287+ markdown_description = description ,
288+ )
254289 )
255- )
256290
257291 return references
258292
@@ -699,3 +733,31 @@ def _position_within_range(position: Position, range: Range) -> bool:
699733 range .end .line > position .line
700734 or (range .end .line == position .line and range .end .character >= position .character )
701735 )
736+
737+
738+ def _get_yaml_model_range (path : Path , model_name : str ) -> t .Optional [Range ]:
739+ """
740+ Find the range of a specific model block in a YAML file.
741+
742+ Args:
743+ yaml_path: Path to the YAML file
744+ model_name: Name of the model to find
745+
746+ Returns:
747+ The Range of the model block in the YAML file, or None if not found
748+ """
749+ yaml = YAML ()
750+ with path .open ("r" , encoding = "utf-8" ) as f :
751+ data = yaml .load (f )
752+
753+ if not isinstance (data , list ):
754+ return None
755+
756+ for item in data :
757+ if isinstance (item , dict ) and item .get ("name" ) == model_name :
758+ # Get size of block by taking the earliest line/col in the items block and the last line/col of the block
759+ position_data = item .lc .data ["name" ] # type: ignore
760+ start = Position (line = position_data [2 ], character = position_data [3 ])
761+ end = Position (line = position_data [2 ], character = position_data [3 ] + len (item ["name" ]))
762+ return Range (start = start , end = end )
763+ return None
0 commit comments