@@ -32,7 +32,8 @@ def __init__(
3232 """
3333 self .server = LanguageServer (server_name , version )
3434 self .context_class = context_class
35- self .context : t .Optional [Context ] = None
35+ # A tuple of (context, reverse_map) where the reverse_map is uri to model name
36+ self .context_and_reverse_map : t .Optional [t .Tuple [Context , t .Dict [str , str ]]] = None
3637 self .lint_cache : t .Dict [str , t .List [RuleViolationWithModelAndType ]] = {}
3738
3839 # Register LSP features (e.g., formatting, hover, etc.)
@@ -43,42 +44,39 @@ def _register_features(self) -> None:
4344
4445 @self .server .feature (types .TEXT_DOCUMENT_DID_OPEN )
4546 def did_open (ls : LanguageServer , params : types .DidOpenTextDocumentParams ) -> None :
46- if self .context is None :
47+ if self .context_and_reverse_map is None :
4748 self .ensure_context_for_document (
4849 ls .workspace .get_document (params .text_document .uri )
4950 )
50- if self .context is None :
51+ if self .context_and_reverse_map is None :
5152 raise RuntimeError ("No context found" )
52- self .lint_cache [params .text_document .uri ] = self .context .lint_models (
53- [params .text_document .uri ]
53+ self .lint_cache [params .text_document .uri ] = self .context_and_reverse_map [ 0 ] .lint_models (
54+ [SQLMeshLanguageServer . _uri_to_path ( params .text_document .uri ) ]
5455 )
5556 ls .publish_diagnostics (
5657 params .text_document .uri ,
57- [
58- self ._diagnostic_to_lsp_diagnostic (diagnostic )
59- for diagnostic in self .lint_cache [params .text_document .uri ]
60- if diagnostic is not None
61- ],
58+ SQLMeshLanguageServer ._diagnostics_to_lsp_diagnostics (
59+ self .lint_cache [params .text_document .uri ]
60+ ),
6261 )
6362
6463 @self .server .feature (types .TEXT_DOCUMENT_DID_CHANGE )
6564 def did_change (ls : LanguageServer , params : types .DidChangeTextDocumentParams ) -> None :
66- if self .context is None :
65+ if self .context_and_reverse_map is None :
6766 self .ensure_context_for_document (
6867 ls .workspace .get_document (params .text_document .uri )
6968 )
70- if self .context is None :
69+ if self .context_and_reverse_map is None :
7170 raise RuntimeError ("No context found" )
72- self .lint_cache [params .text_document .uri ] = self .context .lint_models (
73- [params .text_document .uri ]
71+ model = self .context_and_reverse_map [1 ][params .text_document .uri ]
72+ self .lint_cache [params .text_document .uri ] = self .context_and_reverse_map [0 ].lint_models (
73+ [model ]
7474 )
7575 ls .publish_diagnostics (
7676 params .text_document .uri ,
77- [
78- self ._diagnostic_to_lsp_diagnostic (diagnostic )
79- for diagnostic in self .lint_cache [params .text_document .uri ]
80- if diagnostic is not None
81- ],
77+ SQLMeshLanguageServer ._diagnostics_to_lsp_diagnostics (
78+ self .lint_cache [params .text_document .uri ]
79+ ),
8280 )
8381
8482 @self .server .feature (types .TEXT_DOCUMENT_DID_CLOSE )
@@ -87,22 +85,21 @@ def did_close(ls: LanguageServer, params: types.DidCloseTextDocumentParams) -> N
8785
8886 @self .server .feature (types .TEXT_DOCUMENT_DID_SAVE )
8987 def did_save (ls : LanguageServer , params : types .DidSaveTextDocumentParams ) -> None :
90- if self .context is None :
88+ if self .context_and_reverse_map is None :
9189 self .ensure_context_for_document (
9290 ls .workspace .get_document (params .text_document .uri )
9391 )
94- if self .context is None :
92+ if self .context_and_reverse_map is None :
9593 raise RuntimeError ("No context found" )
96- self .lint_cache [params .text_document .uri ] = self .context .lint_models (
97- [params .text_document .uri ]
94+ model = self .context_and_reverse_map [1 ][params .text_document .uri ]
95+ self .lint_cache [params .text_document .uri ] = self .context_and_reverse_map [0 ].lint_models (
96+ [model ]
9897 )
9998 ls .publish_diagnostics (
10099 params .text_document .uri ,
101- [
102- self ._diagnostic_to_lsp_diagnostic (diagnostic )
103- for diagnostic in self .lint_cache [params .text_document .uri ]
104- if diagnostic is not None
105- ],
100+ SQLMeshLanguageServer ._diagnostics_to_lsp_diagnostics (
101+ self .lint_cache [params .text_document .uri ]
102+ ),
106103 )
107104
108105 @self .server .feature (types .TEXT_DOCUMENT_FORMATTING )
@@ -115,11 +112,11 @@ def formatting(
115112 ls .workspace .get_document (params .text_document .uri )
116113 )
117114
118- if self .context is None :
115+ if self .context_and_reverse_map is None :
119116 raise RuntimeError (f"No context found for document: { document .path } " )
120117
121118 # Perform formatting using the loaded context
122- self .context .format (paths = (Path (document .path ),))
119+ self .context_and_reverse_map [ 0 ] .format (paths = (Path (document .path ),))
123120 with open (document .path , "r+" , encoding = "utf-8" ) as file :
124121 new_text = file .read ()
125122
@@ -146,8 +143,16 @@ def ensure_context_for_document(self, document: TextDocument) -> TextDocument:
146143 for a config.py or config.yml file in the parent directories.
147144 """
148145 # If the context is already loaded, check if this document belongs to it.
149- if self .context is not None :
150- self .context .load () # Reload or refresh context
146+ if self .context_and_reverse_map is not None :
147+ context , _ = self .context_and_reverse_map
148+ context .load () # Reload or refresh context
149+ self .context_and_reverse_map = (
150+ context ,
151+ {
152+ f"file://{ Path (model ._path ).resolve ().as_posix ()} " : model .name
153+ for model in context ._models .values ()
154+ },
155+ )
151156 return document
152157
153158 # No context yet: try to find config and load it
@@ -163,7 +168,14 @@ def ensure_context_for_document(self, document: TextDocument) -> TextDocument:
163168 if config_path .exists ():
164169 try :
165170 # Use user-provided instantiator to build the context
166- self .context = self .context_class (paths = [path ])
171+ context = self .context_class (paths = [path ])
172+ self .context_and_reverse_map = (
173+ context ,
174+ {
175+ f"file://{ Path (model ._path ).resolve ().as_posix ()} " : model .name
176+ for model in context ._models .values ()
177+ },
178+ )
167179 self .server .show_message (f"Context loaded for: { path } " )
168180 loaded = True
169181 # Re-check context for document now that it's loaded
@@ -195,6 +207,26 @@ def _diagnostic_to_lsp_diagnostic(
195207 else types .DiagnosticSeverity .Warning ,
196208 )
197209
210+ @staticmethod
211+ def _diagnostics_to_lsp_diagnostics (
212+ diagnostics : t .List [RuleViolationWithModelAndType ],
213+ ) -> t .List [types .Diagnostic ]:
214+ lsp_diagnostics : t .List [types .Diagnostic ] = []
215+ for diagnostic in diagnostics :
216+ if diagnostic is None :
217+ continue
218+ lsp_diagnostic = SQLMeshLanguageServer ._diagnostic_to_lsp_diagnostic (diagnostic )
219+ if lsp_diagnostic is not None :
220+ lsp_diagnostics .append (lsp_diagnostic )
221+ return lsp_diagnostics
222+
223+ @staticmethod
224+ def _uri_to_path (uri : str ) -> str :
225+ """Convert a URI to a path."""
226+ if uri .startswith ("file://" ):
227+ return Path (uri [7 :]).resolve ().as_posix ()
228+ return Path (uri ).resolve ().as_posix ()
229+
198230 def start (self ) -> None :
199231 """Start the server with I/O transport."""
200232 logging .basicConfig (level = logging .DEBUG )
0 commit comments