Improved completion to support the correct table name for autocompletion and formatting

This commit is contained in:
2026-02-07 20:56:44 +01:00
parent 3fc4384251
commit 08c8c00e28
3 changed files with 304 additions and 336 deletions

View File

@@ -7,7 +7,7 @@ Implements the BaseCompletionEngine for DataGrid formatting rules.
from myfasthtml.core.dsl.base_completion import BaseCompletionEngine
from myfasthtml.core.dsl.types import Position, Suggestion, CompletionResult
from myfasthtml.core.utils import make_safe_id
from . import suggestions as suggestions_module
from . import presets
from .contexts import Context, DetectedScope, detect_scope, detect_context
from .provider import DatagridMetadataProvider
@@ -80,13 +80,283 @@ class FormattingCompletionEngine(BaseCompletionEngine):
Returns:
List of suggestions
"""
return suggestions_module.get_suggestions(context, scope, self.provider)
match context:
# =================================================================
# Scope-level contexts
# =================================================================
case Context.NONE:
return []
case Context.SCOPE_KEYWORD:
return presets.SCOPE_KEYWORDS
case Context.COLUMN_NAME:
return self._get_column_suggestions()
case Context.ROW_INDEX:
return self._get_row_index_suggestions()
case Context.CELL_START:
return [Suggestion("(", "Start cell coordinates", "syntax")]
case Context.CELL_COLUMN:
return self._get_column_suggestions()
case Context.CELL_ROW:
return self._get_row_index_suggestions()
# =================================================================
# Rule-level contexts
# =================================================================
case Context.RULE_START:
return presets.RULE_START
# =================================================================
# Style contexts
# =================================================================
case Context.STYLE_ARGS:
# Presets (with quotes) + params
style_presets = self._get_style_preset_suggestions_quoted()
return style_presets + presets.STYLE_PARAMS
case Context.STYLE_PRESET:
return self._get_style_preset_suggestions()
case Context.STYLE_PARAM:
return presets.STYLE_PARAMS
# =================================================================
# Format contexts
# =================================================================
case Context.FORMAT_PRESET:
return self._get_format_preset_suggestions()
case Context.FORMAT_TYPE:
return presets.FORMAT_TYPES
case Context.FORMAT_PARAM_DATE:
return presets.FORMAT_PARAMS_DATE
case Context.FORMAT_PARAM_TEXT:
return presets.FORMAT_PARAMS_TEXT
# =================================================================
# After style/format
# =================================================================
case Context.AFTER_STYLE_OR_FORMAT:
return presets.AFTER_STYLE_OR_FORMAT
# =================================================================
# Condition contexts
# =================================================================
case Context.CONDITION_START:
return presets.CONDITION_START
case Context.CONDITION_AFTER_NOT:
return presets.CONDITION_AFTER_NOT
case Context.COLUMN_REF:
return self._get_column_suggestions()
case Context.COLUMN_REF_QUOTED:
return self._get_column_suggestions_with_closing_quote()
# =================================================================
# Operator contexts
# =================================================================
case Context.OPERATOR:
return presets.COMPARISON_OPERATORS
case Context.OPERATOR_VALUE | Context.BETWEEN_VALUE:
# col., True, False + column values
base = presets.OPERATOR_VALUE_BASE.copy()
base.extend(self._get_column_value_suggestions(scope))
return base
case Context.BETWEEN_AND:
return presets.BETWEEN_AND
case Context.IN_LIST_START:
return presets.IN_LIST_START
case Context.IN_LIST_VALUE:
return self._get_column_value_suggestions(scope)
# =================================================================
# Value contexts
# =================================================================
case Context.BOOLEAN_VALUE:
return presets.BOOLEAN_VALUES
case Context.COLOR_VALUE:
return presets.ALL_COLORS
case Context.DATE_FORMAT_VALUE:
return presets.DATE_PATTERNS
case Context.TRANSFORM_VALUE:
return presets.TEXT_TRANSFORMS
case _:
return []
def _get_column_suggestions(self) -> list[Suggestion]:
"""Get column name suggestions from provider."""
try:
# Try to get columns from the first available table
tables = self.provider.list_tables()
if tables:
columns = self.provider.list_columns(self.table_name)
return [Suggestion(col, "Column", "column") for col in columns]
except Exception:
pass
return []
def _get_column_suggestions_with_closing_quote(self) -> list[Suggestion]:
"""Get column name suggestions with closing quote."""
try:
tables = self.provider.list_tables()
if tables:
columns = self.provider.list_columns(self.table_name)
return [Suggestion(f'{col}"', "Column", "column") for col in columns]
except Exception:
pass
return []
def _get_style_preset_suggestions(self) -> list[Suggestion]:
"""Get style preset suggestions (without quotes)."""
suggestions = []
# Add provider presets if available
try:
custom_presets = self.provider.list_style_presets()
for preset in custom_presets:
# Check if it's already in default presets
if not any(s.label == preset for s in presets.STYLE_PRESETS):
suggestions.append(Suggestion(preset, "Custom preset", "preset"))
except Exception:
pass
# Add default presets (just the name, no quotes - we're inside quotes)
for preset in presets.STYLE_PRESETS:
suggestions.append(Suggestion(preset.label, preset.detail, preset.kind))
return suggestions
def _get_style_preset_suggestions_quoted(self) -> list[Suggestion]:
"""Get style preset suggestions with quotes."""
suggestions = []
# Add provider presets if available
try:
custom_presets = self.provider.list_style_presets()
for preset in custom_presets:
if not any(s.label == preset for s in presets.STYLE_PRESETS):
suggestions.append(Suggestion(f'"{preset}"', "Custom preset", "preset"))
except Exception:
pass
# Add default presets with quotes
for preset in presets.STYLE_PRESETS:
suggestions.append(Suggestion(f'"{preset.label}"', preset.detail, preset.kind))
return suggestions
def _get_format_preset_suggestions(self) -> list[Suggestion]:
"""Get format preset suggestions (without quotes)."""
suggestions = []
# Add provider presets if available
try:
custom_presets = self.provider.list_format_presets()
for preset in custom_presets:
if not any(s.label == preset for s in presets.FORMAT_PRESETS):
suggestions.append(Suggestion(preset, "Custom preset", "preset"))
except Exception:
pass
# Add default presets
for preset in presets.FORMAT_PRESETS:
suggestions.append(Suggestion(preset.label, preset.detail, preset.kind))
return suggestions
def _get_row_index_suggestions(self) -> list[Suggestion]:
"""Get row index suggestions (first 10 + last)."""
suggestions = []
try:
tables = self.provider.list_tables()
if tables:
row_count = self.provider.get_row_count(self.table_name)
if row_count > 0:
# First 10 rows
for i in range(min(10, row_count)):
suggestions.append(Suggestion(str(i), f"Row {i}", "index"))
# Last row if not already included
last_index = row_count - 1
if last_index >= 10:
suggestions.append(
Suggestion(str(last_index), f"Last row ({row_count} total)", "index")
)
except Exception:
pass
# Fallback if no provider data
if not suggestions:
for i in range(10):
suggestions.append(Suggestion(str(i), f"Row {i}", "index"))
return suggestions
def _get_column_value_suggestions(self, scope: DetectedScope) -> list[Suggestion]:
"""Get column value suggestions based on the current scope."""
if not scope.column_name:
return []
try:
# Use table_name from scope, or empty string as fallback
table_name = scope.table_name or ""
values = self.provider.list_column_values(table_name, scope.column_name)
suggestions = []
for value in values:
if value is None:
continue
# Format value appropriately
if isinstance(value, str):
label = f'"{value}"'
detail = "Text value"
elif isinstance(value, bool):
label = str(value)
detail = "Boolean value"
elif isinstance(value, (int, float)):
label = str(value)
detail = "Numeric value"
else:
label = f'"{value}"'
detail = "Value"
suggestions.append(Suggestion(label, detail, "value"))
return suggestions
except Exception:
return []
def get_completions(
text: str,
cursor: Position,
provider: DatagridMetadataProvider,
table_name: str,
) -> CompletionResult:
"""
Get autocompletion suggestions for the formatting DSL.
@@ -97,6 +367,7 @@ def get_completions(
text: The full DSL document text
cursor: Cursor position (line and ch are 0-based)
provider: DataGrid metadata provider
table_name: Table name for completions
Returns:
CompletionResult with suggestions and replacement range
@@ -105,9 +376,10 @@ def get_completions(
result = get_completions(
text='column amount:\\n style("err',
cursor=Position(line=1, ch=15),
provider=my_provider
provider=my_provider,
table_name="app.orders"
)
# result.suggestions contains ["error"] filtered by prefix "err"
"""
engine = FormattingCompletionEngine(provider)
engine = FormattingCompletionEngine(provider, table_name)
return engine.get_completions(text, cursor)

View File

@@ -1,313 +0,0 @@
"""
Suggestions generation for the formatting DSL.
Provides functions to generate appropriate suggestions
based on the detected context and scope.
"""
from myfasthtml.core.dsl.types import Suggestion
from . import presets
from .contexts import Context, DetectedScope
from .provider import DatagridMetadataProvider
def get_suggestions(
context: Context,
scope: DetectedScope,
provider: DatagridMetadataProvider,
) -> list[Suggestion]:
"""
Generate suggestions for the given context.
Args:
context: The detected completion context
scope: The detected scope
provider: Metadata provider for dynamic data
Returns:
List of suggestions
"""
match context:
# =================================================================
# Scope-level contexts
# =================================================================
case Context.NONE:
return []
case Context.SCOPE_KEYWORD:
return presets.SCOPE_KEYWORDS
case Context.COLUMN_NAME:
return _get_column_suggestions(provider)
case Context.ROW_INDEX:
return _get_row_index_suggestions(provider)
case Context.CELL_START:
return [Suggestion("(", "Start cell coordinates", "syntax")]
case Context.CELL_COLUMN:
return _get_column_suggestions(provider)
case Context.CELL_ROW:
return _get_row_index_suggestions(provider)
# =================================================================
# Rule-level contexts
# =================================================================
case Context.RULE_START:
return presets.RULE_START
# =================================================================
# Style contexts
# =================================================================
case Context.STYLE_ARGS:
# Presets (with quotes) + params
style_presets = _get_style_preset_suggestions_quoted(provider)
return style_presets + presets.STYLE_PARAMS
case Context.STYLE_PRESET:
return _get_style_preset_suggestions(provider)
case Context.STYLE_PARAM:
return presets.STYLE_PARAMS
# =================================================================
# Format contexts
# =================================================================
case Context.FORMAT_PRESET:
return _get_format_preset_suggestions(provider)
case Context.FORMAT_TYPE:
return presets.FORMAT_TYPES
case Context.FORMAT_PARAM_DATE:
return presets.FORMAT_PARAMS_DATE
case Context.FORMAT_PARAM_TEXT:
return presets.FORMAT_PARAMS_TEXT
# =================================================================
# After style/format
# =================================================================
case Context.AFTER_STYLE_OR_FORMAT:
return presets.AFTER_STYLE_OR_FORMAT
# =================================================================
# Condition contexts
# =================================================================
case Context.CONDITION_START:
return presets.CONDITION_START
case Context.CONDITION_AFTER_NOT:
return presets.CONDITION_AFTER_NOT
case Context.COLUMN_REF:
return _get_column_suggestions(provider)
case Context.COLUMN_REF_QUOTED:
return _get_column_suggestions_with_closing_quote(provider)
# =================================================================
# Operator contexts
# =================================================================
case Context.OPERATOR:
return presets.COMPARISON_OPERATORS
case Context.OPERATOR_VALUE | Context.BETWEEN_VALUE:
# col., True, False + column values
base = presets.OPERATOR_VALUE_BASE.copy()
base.extend(_get_column_value_suggestions(scope, provider))
return base
case Context.BETWEEN_AND:
return presets.BETWEEN_AND
case Context.IN_LIST_START:
return presets.IN_LIST_START
case Context.IN_LIST_VALUE:
return _get_column_value_suggestions(scope, provider)
# =================================================================
# Value contexts
# =================================================================
case Context.BOOLEAN_VALUE:
return presets.BOOLEAN_VALUES
case Context.COLOR_VALUE:
return presets.ALL_COLORS
case Context.DATE_FORMAT_VALUE:
return presets.DATE_PATTERNS
case Context.TRANSFORM_VALUE:
return presets.TEXT_TRANSFORMS
case _:
return []
def _get_column_suggestions(provider: DatagridMetadataProvider) -> list[Suggestion]:
"""Get column name suggestions from provider."""
try:
# Try to get columns from the first available table
tables = provider.list_tables()
if tables:
columns = provider.list_columns(tables[0])
return [Suggestion(col, "Column", "column") for col in columns]
except Exception:
pass
return []
def _get_column_suggestions_with_closing_quote(
provider: DatagridMetadataProvider,
) -> list[Suggestion]:
"""Get column name suggestions with closing quote."""
try:
tables = provider.list_tables()
if tables:
columns = provider.list_columns(tables[0])
return [Suggestion(f'{col}"', "Column", "column") for col in columns]
except Exception:
pass
return []
def _get_style_preset_suggestions(provider: DatagridMetadataProvider) -> list[Suggestion]:
"""Get style preset suggestions (without quotes)."""
suggestions = []
# Add provider presets if available
try:
custom_presets = provider.list_style_presets()
for preset in custom_presets:
# Check if it's already in default presets
if not any(s.label == preset for s in presets.STYLE_PRESETS):
suggestions.append(Suggestion(preset, "Custom preset", "preset"))
except Exception:
pass
# Add default presets (just the name, no quotes - we're inside quotes)
for preset in presets.STYLE_PRESETS:
suggestions.append(Suggestion(preset.label, preset.detail, preset.kind))
return suggestions
def _get_style_preset_suggestions_quoted(
provider: DatagridMetadataProvider,
) -> list[Suggestion]:
"""Get style preset suggestions with quotes."""
suggestions = []
# Add provider presets if available
try:
custom_presets = provider.list_style_presets()
for preset in custom_presets:
if not any(s.label == preset for s in presets.STYLE_PRESETS):
suggestions.append(Suggestion(f'"{preset}"', "Custom preset", "preset"))
except Exception:
pass
# Add default presets with quotes
for preset in presets.STYLE_PRESETS:
suggestions.append(Suggestion(f'"{preset.label}"', preset.detail, preset.kind))
return suggestions
def _get_format_preset_suggestions(provider: DatagridMetadataProvider) -> list[Suggestion]:
"""Get format preset suggestions (without quotes)."""
suggestions = []
# Add provider presets if available
try:
custom_presets = provider.list_format_presets()
for preset in custom_presets:
if not any(s.label == preset for s in presets.FORMAT_PRESETS):
suggestions.append(Suggestion(preset, "Custom preset", "preset"))
except Exception:
pass
# Add default presets
for preset in presets.FORMAT_PRESETS:
suggestions.append(Suggestion(preset.label, preset.detail, preset.kind))
return suggestions
def _get_row_index_suggestions(provider: DatagridMetadataProvider) -> list[Suggestion]:
"""Get row index suggestions (first 10 + last)."""
suggestions = []
try:
tables = provider.list_tables()
if tables:
row_count = provider.get_row_count(tables[0])
if row_count > 0:
# First 10 rows
for i in range(min(10, row_count)):
suggestions.append(Suggestion(str(i), f"Row {i}", "index"))
# Last row if not already included
last_index = row_count - 1
if last_index >= 10:
suggestions.append(
Suggestion(str(last_index), f"Last row ({row_count} total)", "index")
)
except Exception:
pass
# Fallback if no provider data
if not suggestions:
for i in range(10):
suggestions.append(Suggestion(str(i), f"Row {i}", "index"))
return suggestions
def _get_column_value_suggestions(
scope: DetectedScope,
provider: DatagridMetadataProvider,
) -> list[Suggestion]:
"""Get column value suggestions based on the current scope."""
if not scope.column_name:
return []
try:
# Use table_name from scope, or empty string as fallback
table_name = scope.table_name or ""
values = provider.list_column_values(table_name, scope.column_name)
suggestions = []
for value in values:
if value is None:
continue
# Format value appropriately
if isinstance(value, str):
label = f'"{value}"'
detail = "Text value"
elif isinstance(value, bool):
label = str(value)
detail = "Boolean value"
elif isinstance(value, (int, float)):
label = str(value)
detail = "Numeric value"
else:
label = f'"{value}"'
detail = "Value"
suggestions.append(Suggestion(label, detail, "value"))
return suggestions
except Exception:
return []