|
23 | 23 | ApiResponseGetLineage, |
24 | 24 | ApiResponseGetModels, |
25 | 25 | ) |
| 26 | +from sqlmesh.lsp.commands import ExternalModelUpdateColumnsRequest, EXTERNAL_MODEL_UPDATE_COLUMNS |
26 | 27 | from sqlmesh.lsp.completions import get_sql_completions |
27 | 28 | from sqlmesh.lsp.context import ( |
28 | 29 | LSPContext, |
|
63 | 64 | from web.server.api.endpoints.models import get_models |
64 | 65 | from typing import Union |
65 | 66 | from dataclasses import dataclass, field |
| 67 | +from sqlmesh.utils import yaml |
66 | 68 |
|
67 | 69 |
|
68 | 70 | @dataclass |
@@ -313,6 +315,25 @@ def function_call(ls: LanguageServer, params: t.Any) -> t.Dict[str, t.Any]: |
313 | 315 |
|
314 | 316 | self.server.feature(name)(create_function_call(method)) |
315 | 317 |
|
| 318 | + @self.server.command(EXTERNAL_MODEL_UPDATE_COLUMNS) |
| 319 | + def execute_command(ls: LanguageServer, raw: t.Any) -> None: |
| 320 | + try: |
| 321 | + if not isinstance(raw, list) or not len(list) == 1 or not isinstance(raw[0], str): |
| 322 | + raise ValueError("Invalid command parameters") |
| 323 | + |
| 324 | + request = ExternalModelUpdateColumnsRequest.model_validate(raw[0]) |
| 325 | + context = self._context_get_or_load() |
| 326 | + if not isinstance(context, LSPContext): |
| 327 | + raise ValueError("Context is not loaded or invalid") |
| 328 | + context.context.update_external_model_columns( |
| 329 | + model_name=request.model_name, |
| 330 | + ) |
| 331 | + |
| 332 | + ls.show_message(f"Executing command to update external model columns {raw}", types.MessageType.Info) |
| 333 | + except Exception as e: |
| 334 | + ls.show_message(f"Error executing command: {e}", types.MessageType.Error) |
| 335 | + return None |
| 336 | + |
316 | 337 | @self.server.feature(types.INITIALIZE) |
317 | 338 | def initialize(ls: LanguageServer, params: types.InitializeParams) -> None: |
318 | 339 | """Initialize the server when the client connects.""" |
@@ -688,6 +709,17 @@ def code_action( |
688 | 709 | ls.log_trace(f"Error getting code actions: {e}") |
689 | 710 | return None |
690 | 711 |
|
| 712 | + @self.server.feature(types.TEXT_DOCUMENT_CODE_LENS) |
| 713 | + def code_lens(ls: LanguageServer, params: types.CodeLensParams) -> t.List[types.CodeLens]: |
| 714 | + try: |
| 715 | + uri = URI(params.text_document.uri) |
| 716 | + context = self._context_get_or_load(uri) |
| 717 | + code_lenses = context.get_code_lenses(uri) |
| 718 | + return code_lenses |
| 719 | + except Exception as e: |
| 720 | + ls.log_trace(f"Error getting code lenses: {e}") |
| 721 | + return [] |
| 722 | + |
691 | 723 | @self.server.feature( |
692 | 724 | types.TEXT_DOCUMENT_COMPLETION, |
693 | 725 | types.CompletionOptions(trigger_characters=["@"]), # advertise "@" for macros |
@@ -902,6 +934,68 @@ def _uri_to_path(uri: str) -> Path: |
902 | 934 | """Convert a URI to a path.""" |
903 | 935 | return URI(uri).to_path() |
904 | 936 |
|
| 937 | + def _update_external_model_columns(self, ls: LanguageServer, arguments: t.List[t.Any]) -> t.Any: |
| 938 | + """Update the columns for an external model in the YAML file.""" |
| 939 | + if len(arguments) != 3: |
| 940 | + ls.show_message("Invalid arguments for update columns command", types.MessageType.Error) |
| 941 | + return None |
| 942 | + |
| 943 | + uri_str, model_name, model_idx = arguments |
| 944 | + uri = URI(uri_str) |
| 945 | + path = uri.to_path() |
| 946 | + |
| 947 | + try: |
| 948 | + # Get the context |
| 949 | + context = self._context_get_or_load(uri) |
| 950 | + |
| 951 | + # Read the YAML file |
| 952 | + models = yaml.load(path) |
| 953 | + if not isinstance(models, list) or model_idx >= len(models): |
| 954 | + ls.show_message("Invalid model index", types.MessageType.Error) |
| 955 | + return None |
| 956 | + |
| 957 | + model = models[model_idx] |
| 958 | + if model.get("name") != model_name: |
| 959 | + ls.show_message("Model name mismatch", types.MessageType.Error) |
| 960 | + return None |
| 961 | + |
| 962 | + # Get the adapter and fetch columns |
| 963 | + adapter = context.context.engine_adapter |
| 964 | + |
| 965 | + # Get columns for the model |
| 966 | + try: |
| 967 | + columns = adapter.columns(model_name, include_pseudo_columns=True) |
| 968 | + except Exception as e: |
| 969 | + ls.show_message( |
| 970 | + f"Unable to fetch columns for {model_name}: {e}", types.MessageType.Error |
| 971 | + ) |
| 972 | + return None |
| 973 | + |
| 974 | + if not columns: |
| 975 | + ls.show_message(f"No columns found for {model_name}", types.MessageType.Warning) |
| 976 | + return None |
| 977 | + |
| 978 | + # Update the model's columns |
| 979 | + dialect = context.context.config.model_defaults.dialect |
| 980 | + model["columns"] = { |
| 981 | + col_name: dtype.sql(dialect=dialect) for col_name, dtype in columns.items() |
| 982 | + } |
| 983 | + |
| 984 | + # Write back to the file |
| 985 | + with open(path, "w", encoding="utf-8") as f: |
| 986 | + yaml.dump(models, f) |
| 987 | + |
| 988 | + ls.show_message(f"Updated columns for {model_name}", types.MessageType.Info) |
| 989 | + |
| 990 | + # Reload the context to pick up the changes |
| 991 | + self._reload_context_and_publish_diagnostics(ls, uri, uri_str) |
| 992 | + |
| 993 | + except Exception as e: |
| 994 | + ls.show_message(f"Error updating columns: {e}", types.MessageType.Error) |
| 995 | + ls.log_trace(f"Error updating columns: {e}") |
| 996 | + |
| 997 | + return None |
| 998 | + |
905 | 999 | def start(self) -> None: |
906 | 1000 | """Start the server with I/O transport.""" |
907 | 1001 | logging.basicConfig(level=logging.DEBUG) |
|
0 commit comments