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

This commit is contained in:
2026-02-07 18:26:29 +01:00
parent ab4f251f0c
commit 3fc4384251
10 changed files with 49 additions and 58 deletions

View File

@@ -24,7 +24,10 @@ from myfasthtml.controls.helpers import mk, icons
from myfasthtml.core.commands import Command from myfasthtml.core.commands import Command
from myfasthtml.core.constants import ColumnType, ROW_INDEX_ID, FooterAggregation, DATAGRID_PAGE_SIZE, FILTER_INPUT_CID from myfasthtml.core.constants import ColumnType, ROW_INDEX_ID, FooterAggregation, DATAGRID_PAGE_SIZE, FILTER_INPUT_CID
from myfasthtml.core.dbmanager import DbObject from myfasthtml.core.dbmanager import DbObject
from myfasthtml.core.dsls import DslsManager
from myfasthtml.core.formatting.dsl.completion.FormattingCompletionEngine import FormattingCompletionEngine
from myfasthtml.core.formatting.dsl.definition import FormattingDSL from myfasthtml.core.formatting.dsl.definition import FormattingDSL
from myfasthtml.core.formatting.dsl.parser import DSLParser
from myfasthtml.core.formatting.engine import FormattingEngine from myfasthtml.core.formatting.engine import FormattingEngine
from myfasthtml.core.instances import MultipleInstance from myfasthtml.core.instances import MultipleInstance
from myfasthtml.core.optimized_ft import OptimizedDiv from myfasthtml.core.optimized_ft import OptimizedDiv
@@ -97,6 +100,7 @@ class DatagridSettings(DbObject):
self.open_file_visible: bool = True self.open_file_visible: bool = True
self.open_settings_visible: bool = True self.open_settings_visible: bool = True
self.text_size: str = "sm" self.text_size: str = "sm"
self.enable_formatting: bool = True
class Commands(BaseCommands): class Commands(BaseCommands):
@@ -206,12 +210,20 @@ class DataGrid(MultipleInstance):
self._columns_manager.bind_command("ToggleColumn", self.commands.on_column_changed()) self._columns_manager.bind_command("ToggleColumn", self.commands.on_column_changed())
self._columns_manager.bind_command("UpdateColumn", self.commands.on_column_changed()) self._columns_manager.bind_command("UpdateColumn", self.commands.on_column_changed())
editor_conf = DslEditorConf() if self._settings.enable_formatting:
completion_engine = FormattingCompletionEngine(self._parent, self.get_table_name())
editor_conf = DslEditorConf(engine_id=completion_engine.get_id())
dsl = FormattingDSL()
self._formatting_editor = DataGridFormattingEditor(self, self._formatting_editor = DataGridFormattingEditor(self,
conf=editor_conf, conf=editor_conf,
dsl=FormattingDSL(), dsl=dsl,
save_state=self._settings.save_state, save_state=self._settings.save_state,
_id="-formatting_editor") _id="-formatting_editor")
# register the auto-completion for the formatter DSL
DslsManager.register(completion_engine,
DSLParser())
else:
self._formatting_editor = None
# other definitions # other definitions
self._mouse_support = { self._mouse_support = {
@@ -220,7 +232,7 @@ class DataGrid(MultipleInstance):
"shift+click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"}, "shift+click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"},
} }
logger.debug(f"DataGrid '{self._get_full_name()}' with id='{self._id}' created.") logger.debug(f"DataGrid '{self.get_table_name()}' with id='{self._id}' created.")
@property @property
def _df(self): def _df(self):
@@ -298,9 +310,6 @@ class DataGrid(MultipleInstance):
self._state.selection.selected = pos self._state.selection.selected = pos
self._state.save() self._state.save()
def _get_full_name(self):
return f"{self._settings.namespace}.{self._settings.name}" if self._settings.namespace else self._settings.name
def init_from_dataframe(self, df, init_state=True): def init_from_dataframe(self, df, init_state=True):
def _get_column_type(dtype): def _get_column_type(dtype):
@@ -497,6 +506,9 @@ class DataGrid(MultipleInstance):
def get_settings(self): def get_settings(self):
return self._settings return self._settings
def get_table_name(self):
return f"{self._settings.namespace}.{self._settings.name}" if self._settings.namespace else self._settings.name
def mk_headers(self): def mk_headers(self):
resize_cmd = self.commands.set_column_width() resize_cmd = self.commands.set_column_width()
move_cmd = self.commands.move_column() move_cmd = self.commands.move_column()

View File

@@ -14,11 +14,7 @@ from myfasthtml.controls.helpers import mk
from myfasthtml.core.DataGridsRegistry import DataGridsRegistry from myfasthtml.core.DataGridsRegistry import DataGridsRegistry
from myfasthtml.core.commands import Command from myfasthtml.core.commands import Command
from myfasthtml.core.dbmanager import DbObject from myfasthtml.core.dbmanager import DbObject
from myfasthtml.core.dsls import DslsManager
from myfasthtml.core.formatting.dsl.completion.engine import FormattingCompletionEngine
from myfasthtml.core.formatting.dsl.completion.provider import DatagridMetadataProvider from myfasthtml.core.formatting.dsl.completion.provider import DatagridMetadataProvider
from myfasthtml.core.formatting.dsl.definition import FormattingDSL
from myfasthtml.core.formatting.dsl.parser import DSLParser
from myfasthtml.core.formatting.presets import DEFAULT_STYLE_PRESETS, DEFAULT_FORMATTER_PRESETS from myfasthtml.core.formatting.presets import DEFAULT_STYLE_PRESETS, DEFAULT_FORMATTER_PRESETS
from myfasthtml.core.instances import InstancesManager, SingleInstance from myfasthtml.core.instances import InstancesManager, SingleInstance
from myfasthtml.icons.fluent_p1 import table_add20_regular from myfasthtml.icons.fluent_p1 import table_add20_regular
@@ -95,11 +91,6 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider):
self.style_presets: dict = DEFAULT_STYLE_PRESETS.copy() self.style_presets: dict = DEFAULT_STYLE_PRESETS.copy()
self.formatter_presets: dict = DEFAULT_FORMATTER_PRESETS.copy() self.formatter_presets: dict = DEFAULT_FORMATTER_PRESETS.copy()
# register the auto-completion for the formatter DSL
DslsManager.register(FormattingDSL().get_id(),
FormattingCompletionEngine(self),
DSLParser())
def upload_from_source(self): def upload_from_source(self):
file_upload = FileUpload(self) file_upload = FileUpload(self)
tab_id = self._tabs_manager.create_tab("Upload Datagrid", file_upload) tab_id = self._tabs_manager.create_tab("Upload Datagrid", file_upload)
@@ -112,7 +103,7 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider):
namespace = file_upload.get_file_basename() namespace = file_upload.get_file_basename()
name = file_upload.get_sheet_name() name = file_upload.get_sheet_name()
dg_conf = DatagridConf(namespace=namespace, name=name) dg_conf = DatagridConf(namespace=namespace, name=name)
dg = DataGrid(self._tabs_manager, conf=dg_conf, save_state=True) # first time the Datagrid is created dg = DataGrid(self, conf=dg_conf, save_state=True) # first time the Datagrid is created
dg.init_from_dataframe(df) dg.init_from_dataframe(df)
self._registry.put(namespace, name, dg.get_id()) self._registry.put(namespace, name, dg.get_id())
document = DocumentDefinition( document = DocumentDefinition(
@@ -133,7 +124,7 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider):
document_id = self._tree.get_bag(node_id) document_id = self._tree.get_bag(node_id)
try: try:
document = next(filter(lambda x: x.document_id == document_id, self._state.elements)) document = next(filter(lambda x: x.document_id == document_id, self._state.elements))
dg = DataGrid(self._tabs_manager, _id=document.datagrid_id) # reload the state & settings dg = DataGrid(self, _id=document.datagrid_id) # reload the state & settings
return self._tabs_manager.show_or_create_tab(document.tab_id, document.name, dg) return self._tabs_manager.show_or_create_tab(document.tab_id, document.name, dg)
except StopIteration: except StopIteration:
# the selected node is not a document (it's a folder) # the selected node is not a document (it's a folder)
@@ -157,7 +148,7 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider):
raise ValueError(f"No document found for tab {tab_id}") raise ValueError(f"No document found for tab {tab_id}")
# Recreate the DataGrid with its saved state # Recreate the DataGrid with its saved state
dg = DataGrid(self._tabs_manager, _id=document.datagrid_id) # reload the state & settings dg = DataGrid(self, _id=document.datagrid_id) # reload the state & settings
return dg return dg
def clear_tree(self): def clear_tree(self):

View File

@@ -32,6 +32,7 @@ class DslEditorConf:
linting: bool = True linting: bool = True
placeholder: str = "" placeholder: str = ""
readonly: bool = False readonly: bool = False
engine_id: str = None # id of the DSL engine to use for autocompletion
class DslEditorState(DbObject): class DslEditorState(DbObject):
@@ -151,7 +152,7 @@ class DslEditor(MultipleInstance):
"placeholder": self.conf.placeholder, "placeholder": self.conf.placeholder,
"readonly": self.conf.readonly, "readonly": self.conf.readonly,
"updateCommandId": str(self.commands.update_content().id), "updateCommandId": str(self.commands.update_content().id),
"dslId": self._dsl.get_id(), "dslId": self.conf.engine_id,
"dsl": { "dsl": {
"name": self._dsl.name, "name": self._dsl.name,
"completions": self._dsl.completions, "completions": self._dsl.completions,

View File

@@ -33,6 +33,7 @@ class BaseCompletionEngine(ABC):
provider: Metadata provider for context-aware suggestions provider: Metadata provider for context-aware suggestions
""" """
self.provider = provider self.provider = provider
self._id = type(self).__name__
def get_completions(self, text: str, cursor: Position) -> CompletionResult: def get_completions(self, text: str, cursor: Position) -> CompletionResult:
""" """
@@ -169,4 +170,4 @@ class BaseCompletionEngine(ABC):
) )
def get_id(self): def get_id(self):
return type(self).__name__ return self._id

View File

@@ -19,7 +19,7 @@ class BaseMetadataProvider(Protocol):
can extend this with additional methods. can extend this with additional methods.
""" """
def get_style_presets(self) -> list[str]: def list_style_presets(self) -> list[str]:
""" """
Return the list of available style preset names. Return the list of available style preset names.
@@ -28,7 +28,7 @@ class BaseMetadataProvider(Protocol):
""" """
... ...
def get_format_presets(self) -> list[str]: def list_format_presets(self) -> list[str]:
""" """
Return the list of available format preset names. Return the list of available format preset names.

View File

@@ -14,9 +14,9 @@ class DslsManager:
dsls: dict[str, DslDefinition] = {} dsls: dict[str, DslDefinition] = {}
@staticmethod @staticmethod
def register(dsl_id: str, completion: BaseCompletionEngine, validation: DSLParser): def register(completion: BaseCompletionEngine, validation: DSLParser):
# then engine_id is actually the DSL id # then engine_id is actually the DSL id
DslsManager.dsls[dsl_id] = DslDefinition(completion, validation) DslsManager.dsls[completion.get_id()] = DslDefinition(completion, validation)
@staticmethod @staticmethod
def get_completion_engine(engine_id) -> BaseCompletionEngine: def get_completion_engine(engine_id) -> BaseCompletionEngine:

View File

@@ -3,8 +3,10 @@ Completion engine for the formatting DSL.
Implements the BaseCompletionEngine for DataGrid formatting rules. Implements the BaseCompletionEngine for DataGrid formatting rules.
""" """
from myfasthtml.core.dsl.base_completion import BaseCompletionEngine from myfasthtml.core.dsl.base_completion import BaseCompletionEngine
from myfasthtml.core.dsl.types import Position, Suggestion, CompletionResult 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 suggestions as suggestions_module
from .contexts import Context, DetectedScope, detect_scope, detect_context from .contexts import Context, DetectedScope, detect_scope, detect_context
from .provider import DatagridMetadataProvider from .provider import DatagridMetadataProvider
@@ -21,7 +23,7 @@ class FormattingCompletionEngine(BaseCompletionEngine):
- Conditions with operators and values - Conditions with operators and values
""" """
def __init__(self, provider: DatagridMetadataProvider): def __init__(self, provider: DatagridMetadataProvider, table_name: str):
""" """
Initialize the completion engine. Initialize the completion engine.
@@ -30,6 +32,8 @@ class FormattingCompletionEngine(BaseCompletionEngine):
""" """
super().__init__(provider) super().__init__(provider)
self.provider: DatagridMetadataProvider = provider self.provider: DatagridMetadataProvider = provider
self.table_name: str = table_name # current table name
self._id = "formatting_completion_engine#" + make_safe_id(table_name)
def detect_scope(self, text: str, current_line: int) -> DetectedScope: def detect_scope(self, text: str, current_line: int) -> DetectedScope:
""" """

View File

@@ -5,10 +5,12 @@ Provides access to DataGrid metadata (columns, values, row counts)
for context-aware autocompletion. for context-aware autocompletion.
""" """
from typing import Protocol, Any from typing import Any
from myfasthtml.core.dsl.base_provider import BaseMetadataProvider
class DatagridMetadataProvider(Protocol): class DatagridMetadataProvider(BaseMetadataProvider):
""" """
Protocol for providing DataGrid metadata to the autocompletion engine. Protocol for providing DataGrid metadata to the autocompletion engine.
@@ -70,25 +72,3 @@ class DatagridMetadataProvider(Protocol):
Number of rows Number of rows
""" """
... ...
def list_style_presets(self) -> list[str]:
"""
Return the list of available style preset names.
Includes default presets (primary, error, etc.) and custom presets.
Returns:
List of style preset names
"""
...
def list_format_presets(self) -> list[str]:
"""
Return the list of available format preset names.
Includes default presets (EUR, USD, etc.) and custom presets.
Returns:
List of format preset names
"""
...

View File

@@ -375,7 +375,7 @@ def post(session, b_id: str, values: dict):
:param values: :param values:
:return: :return:
""" """
logger.debug(f"Entering {Routes.Bindings} with {session=}, {b_id=}, {values=}") logger.debug(f"Entering {Routes.Bindings} with session='{debug_session(session)}', {b_id=}, {values=}")
from myfasthtml.core.bindings import BindingsManager from myfasthtml.core.bindings import BindingsManager
binding = BindingsManager.get_binding(b_id) binding = BindingsManager.get_binding(b_id)
if binding: if binding:
@@ -396,7 +396,8 @@ def get(session, e_id: str, text: str, line: int, ch: int):
:param ch: :param ch:
:return: :return:
""" """
logger.debug(f"Entering {Routes.Completions} with {session=}, {e_id=}, {text=}, {line=}, {ch}") logger.debug(
f"Entering {Routes.Completions} with session='{debug_session(session)}', {e_id=}, text={len(text)} char(s), {line=}, {ch=}")
completion = DslsManager.get_completion_engine(e_id) completion = DslsManager.get_completion_engine(e_id)
result = completion.get_completions(text, Position(line, ch)) result = completion.get_completions(text, Position(line, ch))
return result.to_dict() return result.to_dict()
@@ -413,7 +414,8 @@ def get(session, e_id: str, text: str, line: int, ch: int):
:param ch: :param ch:
:return: :return:
""" """
logger.debug(f"Entering {Routes.Validations} with {session=}, {e_id=}, {text=}, {line=}, {ch}") logger.debug(
f"Entering {Routes.Validations} with session='{debug_session(session)}', {e_id=}, text={len(text)} char(s), {line=}, {ch=}")
validation = DslsManager.get_validation_parser(e_id) validation = DslsManager.get_validation_parser(e_id)
try: try:
validation.parse(text) validation.parse(text)

View File

@@ -15,7 +15,7 @@ from myfasthtml.core.formatting.dsl.completion.contexts import (
detect_context, detect_context,
) )
from myfasthtml.core.formatting.dsl.completion.suggestions import get_suggestions from myfasthtml.core.formatting.dsl.completion.suggestions import get_suggestions
from myfasthtml.core.formatting.dsl.completion.engine import ( from myfasthtml.core.formatting.dsl.completion.FormattingCompletionEngine import (
FormattingCompletionEngine, FormattingCompletionEngine,
get_completions, get_completions,
) )