From 3fc438425102d29f5da5ea8cf81b80ad06c4412e Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Sat, 7 Feb 2026 18:26:29 +0100 Subject: [PATCH] Improving completion to support the correct table name for autocompletion and formatting --- src/myfasthtml/controls/DataGrid.py | 32 +++++++++++++------ src/myfasthtml/controls/DataGridsManager.py | 15 ++------- src/myfasthtml/controls/DslEditor.py | 5 +-- src/myfasthtml/core/dsl/base_completion.py | 3 +- src/myfasthtml/core/dsl/base_provider.py | 4 +-- src/myfasthtml/core/dsls.py | 4 +-- ...ngine.py => FormattingCompletionEngine.py} | 6 +++- .../formatting/dsl/completion/provider.py | 28 +++------------- src/myfasthtml/core/utils.py | 8 +++-- tests/core/formatting/dsl/test_completion.py | 2 +- 10 files changed, 49 insertions(+), 58 deletions(-) rename src/myfasthtml/core/formatting/dsl/completion/{engine.py => FormattingCompletionEngine.py} (92%) diff --git a/src/myfasthtml/controls/DataGrid.py b/src/myfasthtml/controls/DataGrid.py index 57e23dd..10d3580 100644 --- a/src/myfasthtml/controls/DataGrid.py +++ b/src/myfasthtml/controls/DataGrid.py @@ -24,7 +24,10 @@ from myfasthtml.controls.helpers import mk, icons 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.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.parser import DSLParser from myfasthtml.core.formatting.engine import FormattingEngine from myfasthtml.core.instances import MultipleInstance from myfasthtml.core.optimized_ft import OptimizedDiv @@ -97,6 +100,7 @@ class DatagridSettings(DbObject): self.open_file_visible: bool = True self.open_settings_visible: bool = True self.text_size: str = "sm" + self.enable_formatting: bool = True 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("UpdateColumn", self.commands.on_column_changed()) - editor_conf = DslEditorConf() - self._formatting_editor = DataGridFormattingEditor(self, - conf=editor_conf, - dsl=FormattingDSL(), - save_state=self._settings.save_state, - _id="-formatting_editor") + 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, + conf=editor_conf, + dsl=dsl, + save_state=self._settings.save_state, + _id="-formatting_editor") + # register the auto-completion for the formatter DSL + DslsManager.register(completion_engine, + DSLParser()) + else: + self._formatting_editor = None # other definitions self._mouse_support = { @@ -220,7 +232,7 @@ class DataGrid(MultipleInstance): "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 def _df(self): @@ -298,9 +310,6 @@ class DataGrid(MultipleInstance): self._state.selection.selected = pos 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 _get_column_type(dtype): @@ -497,6 +506,9 @@ class DataGrid(MultipleInstance): def get_settings(self): 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): resize_cmd = self.commands.set_column_width() move_cmd = self.commands.move_column() diff --git a/src/myfasthtml/controls/DataGridsManager.py b/src/myfasthtml/controls/DataGridsManager.py index 32ad2d9..3320366 100644 --- a/src/myfasthtml/controls/DataGridsManager.py +++ b/src/myfasthtml/controls/DataGridsManager.py @@ -14,11 +14,7 @@ from myfasthtml.controls.helpers import mk from myfasthtml.core.DataGridsRegistry import DataGridsRegistry from myfasthtml.core.commands import Command 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.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.instances import InstancesManager, SingleInstance from myfasthtml.icons.fluent_p1 import table_add20_regular @@ -94,11 +90,6 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider): # Global presets shared across all DataGrids self.style_presets: dict = DEFAULT_STYLE_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): file_upload = FileUpload(self) @@ -112,7 +103,7 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider): namespace = file_upload.get_file_basename() name = file_upload.get_sheet_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) self._registry.put(namespace, name, dg.get_id()) document = DocumentDefinition( @@ -133,7 +124,7 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider): document_id = self._tree.get_bag(node_id) try: 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) except StopIteration: # 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}") # 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 def clear_tree(self): diff --git a/src/myfasthtml/controls/DslEditor.py b/src/myfasthtml/controls/DslEditor.py index e0ba9e0..eca8c70 100644 --- a/src/myfasthtml/controls/DslEditor.py +++ b/src/myfasthtml/controls/DslEditor.py @@ -32,6 +32,7 @@ class DslEditorConf: linting: bool = True placeholder: str = "" readonly: bool = False + engine_id: str = None # id of the DSL engine to use for autocompletion class DslEditorState(DbObject): @@ -141,7 +142,7 @@ class DslEditor(MultipleInstance): simple_mode_config = None if hasattr(self._dsl, 'simple_mode_config'): simple_mode_config = self._dsl.simple_mode_config - + config = { "elementId": str(self._id), "textareaId": f"ta_{self._id}", @@ -151,7 +152,7 @@ class DslEditor(MultipleInstance): "placeholder": self.conf.placeholder, "readonly": self.conf.readonly, "updateCommandId": str(self.commands.update_content().id), - "dslId": self._dsl.get_id(), + "dslId": self.conf.engine_id, "dsl": { "name": self._dsl.name, "completions": self._dsl.completions, diff --git a/src/myfasthtml/core/dsl/base_completion.py b/src/myfasthtml/core/dsl/base_completion.py index 3c87d1f..94e3f99 100644 --- a/src/myfasthtml/core/dsl/base_completion.py +++ b/src/myfasthtml/core/dsl/base_completion.py @@ -33,6 +33,7 @@ class BaseCompletionEngine(ABC): provider: Metadata provider for context-aware suggestions """ self.provider = provider + self._id = type(self).__name__ def get_completions(self, text: str, cursor: Position) -> CompletionResult: """ @@ -169,4 +170,4 @@ class BaseCompletionEngine(ABC): ) def get_id(self): - return type(self).__name__ + return self._id diff --git a/src/myfasthtml/core/dsl/base_provider.py b/src/myfasthtml/core/dsl/base_provider.py index 4b2992a..58371dc 100644 --- a/src/myfasthtml/core/dsl/base_provider.py +++ b/src/myfasthtml/core/dsl/base_provider.py @@ -19,7 +19,7 @@ class BaseMetadataProvider(Protocol): 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. @@ -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. diff --git a/src/myfasthtml/core/dsls.py b/src/myfasthtml/core/dsls.py index 25276db..c6b7069 100644 --- a/src/myfasthtml/core/dsls.py +++ b/src/myfasthtml/core/dsls.py @@ -14,9 +14,9 @@ class DslsManager: dsls: dict[str, DslDefinition] = {} @staticmethod - def register(dsl_id: str, completion: BaseCompletionEngine, validation: DSLParser): + def register(completion: BaseCompletionEngine, validation: DSLParser): # then engine_id is actually the DSL id - DslsManager.dsls[dsl_id] = DslDefinition(completion, validation) + DslsManager.dsls[completion.get_id()] = DslDefinition(completion, validation) @staticmethod def get_completion_engine(engine_id) -> BaseCompletionEngine: diff --git a/src/myfasthtml/core/formatting/dsl/completion/engine.py b/src/myfasthtml/core/formatting/dsl/completion/FormattingCompletionEngine.py similarity index 92% rename from src/myfasthtml/core/formatting/dsl/completion/engine.py rename to src/myfasthtml/core/formatting/dsl/completion/FormattingCompletionEngine.py index 650cdb8..d73bf80 100644 --- a/src/myfasthtml/core/formatting/dsl/completion/engine.py +++ b/src/myfasthtml/core/formatting/dsl/completion/FormattingCompletionEngine.py @@ -3,8 +3,10 @@ Completion engine for the formatting DSL. 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 .contexts import Context, DetectedScope, detect_scope, detect_context from .provider import DatagridMetadataProvider @@ -21,7 +23,7 @@ class FormattingCompletionEngine(BaseCompletionEngine): - Conditions with operators and values """ - def __init__(self, provider: DatagridMetadataProvider): + def __init__(self, provider: DatagridMetadataProvider, table_name: str): """ Initialize the completion engine. @@ -30,6 +32,8 @@ class FormattingCompletionEngine(BaseCompletionEngine): """ super().__init__(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: """ diff --git a/src/myfasthtml/core/formatting/dsl/completion/provider.py b/src/myfasthtml/core/formatting/dsl/completion/provider.py index 627c7e6..a1592e3 100644 --- a/src/myfasthtml/core/formatting/dsl/completion/provider.py +++ b/src/myfasthtml/core/formatting/dsl/completion/provider.py @@ -5,10 +5,12 @@ Provides access to DataGrid metadata (columns, values, row counts) 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. @@ -70,25 +72,3 @@ class DatagridMetadataProvider(Protocol): 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 - """ - ... diff --git a/src/myfasthtml/core/utils.py b/src/myfasthtml/core/utils.py index 302e306..1f91021 100644 --- a/src/myfasthtml/core/utils.py +++ b/src/myfasthtml/core/utils.py @@ -375,7 +375,7 @@ def post(session, b_id: str, values: dict): :param values: :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 binding = BindingsManager.get_binding(b_id) if binding: @@ -396,7 +396,8 @@ def get(session, e_id: str, text: str, line: int, ch: int): :param ch: :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) result = completion.get_completions(text, Position(line, ch)) return result.to_dict() @@ -413,7 +414,8 @@ def get(session, e_id: str, text: str, line: int, ch: int): :param ch: :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) try: validation.parse(text) diff --git a/tests/core/formatting/dsl/test_completion.py b/tests/core/formatting/dsl/test_completion.py index b57c41a..974d40a 100644 --- a/tests/core/formatting/dsl/test_completion.py +++ b/tests/core/formatting/dsl/test_completion.py @@ -15,7 +15,7 @@ from myfasthtml.core.formatting.dsl.completion.contexts import ( detect_context, ) 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, get_completions, )