Skip to content

Commit e748f6f

Browse files
authored
feat(lsp): model descriptions in LSP completions (#4713)
1 parent c1a991e commit e748f6f

5 files changed

Lines changed: 60 additions & 25 deletions

File tree

examples/sushi/models/latest_order.sql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,3 @@ MODEL (
1212
SELECT id, customer_id, start_ts, end_ts, event_date
1313
FROM sushi.orders
1414
ORDER BY event_date DESC LIMIT 1
15-

sqlmesh/lsp/completions.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
from functools import lru_cache
22
from sqlglot import Dialect, Tokenizer
3-
from sqlmesh.lsp.custom import AllModelsResponse, MacroCompletion
3+
from sqlmesh.lsp.custom import (
4+
AllModelsResponse,
5+
MacroCompletion,
6+
ModelCompletion,
7+
)
48
from sqlmesh import macro
59
import typing as t
610
from sqlmesh.lsp.context import AuditTarget, LSPContext, ModelTarget
11+
from sqlmesh.lsp.description import generate_markdown_description
712
from sqlmesh.lsp.uri import URI
813

914

@@ -26,38 +31,42 @@ def get_sql_completions(
2631
# Combine keywords - SQL keywords first, then file keywords
2732
all_keywords = list(sql_keywords) + list(file_keywords - sql_keywords)
2833

34+
models = list(get_models(context, file_uri))
2935
return AllModelsResponse(
30-
models=list(get_models(context, file_uri)),
36+
models=[m.name for m in models],
37+
model_completions=models,
3138
keywords=all_keywords,
3239
macros=list(get_macros(context, file_uri)),
3340
)
3441

3542

36-
def get_models(context: t.Optional[LSPContext], file_uri: t.Optional[URI]) -> t.Set[str]:
43+
def get_models(
44+
context: t.Optional[LSPContext], file_uri: t.Optional[URI]
45+
) -> t.List[ModelCompletion]:
3746
"""
3847
Return a list of models for a given file.
3948
4049
If there is no context, return an empty list.
4150
If there is a context, return a list of all models bar the ones the file itself defines.
4251
"""
4352
if context is None:
44-
return set()
53+
return []
54+
55+
current_path = file_uri.to_path() if file_uri is not None else None
56+
57+
completions: t.List[ModelCompletion] = []
58+
for model in context.context.models.values():
59+
if current_path is not None and model._path == current_path:
60+
continue
61+
description = None
62+
try:
63+
description = generate_markdown_description(model)
64+
except Exception:
65+
description = getattr(model, "description", None)
66+
67+
completions.append(ModelCompletion(name=model.name, description=description))
4568

46-
all_models = set()
47-
# Extract model names from ModelInfo objects
48-
for file_info in context.map.values():
49-
if isinstance(file_info, ModelTarget):
50-
all_models.update(file_info.names)
51-
52-
# Remove models from the current file
53-
path = file_uri.to_path() if file_uri is not None else None
54-
if path is not None and path in context.map:
55-
file_info = context.map[path]
56-
if isinstance(file_info, ModelTarget):
57-
for model in file_info.names:
58-
all_models.discard(model)
59-
60-
return all_models
69+
return completions
6170

6271

6372
def get_macros(

sqlmesh/lsp/custom.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,19 @@ class MacroCompletion(PydanticModel):
3030
description: t.Optional[str] = None
3131

3232

33+
class ModelCompletion(PydanticModel):
34+
"""Information about a model for autocompletion."""
35+
36+
name: str
37+
description: t.Optional[str] = None
38+
39+
3340
class AllModelsResponse(CustomMethodResponseBaseClass):
34-
"""
35-
Response to get all the models that are in the current project.
36-
"""
41+
"""Response to get all models that are in the current project."""
3742

43+
#: Deprecated: use ``model_completions`` instead
3844
models: t.List[str]
45+
model_completions: t.List[ModelCompletion]
3946
keywords: t.List[str]
4047
macros: t.List[MacroCompletion]
4148

sqlmesh/lsp/main.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,12 +627,18 @@ def completion(
627627

628628
completion_items = []
629629
# Add model completions
630-
for model in completion_response.models:
630+
for model in completion_response.model_completions:
631631
completion_items.append(
632632
types.CompletionItem(
633-
label=model,
633+
label=model.name,
634634
kind=types.CompletionItemKind.Reference,
635635
detail="SQLMesh Model",
636+
documentation=types.MarkupContent(
637+
kind=types.MarkupKind.Markdown,
638+
value=model.description or "No description available",
639+
)
640+
if model.description
641+
else None,
636642
)
637643
)
638644
# Add macro completions

tests/lsp/test_completions.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,20 @@ def test_get_macros():
4141
assert add_one_macro.description
4242

4343

44+
def test_model_completions_include_descriptions():
45+
context = Context(paths=["examples/sushi"])
46+
lsp_context = LSPContext(context)
47+
48+
completions = LSPContext.get_completions(lsp_context, None)
49+
50+
model_entry = next(
51+
(m for m in completions.model_completions if m.name == "sushi.customers"),
52+
None,
53+
)
54+
assert model_entry is not None
55+
assert model_entry.description
56+
57+
4458
def test_get_sql_completions_with_context_no_file_uri():
4559
context = Context(paths=["examples/sushi"])
4660
lsp_context = LSPContext(context)

0 commit comments

Comments
 (0)