I can validate formatting in editor

This commit is contained in:
2026-02-01 21:49:46 +01:00
parent d7ec99c3d9
commit 0620cb678b
23 changed files with 794 additions and 501 deletions

View File

@@ -961,7 +961,7 @@ The autocompletion system provides context-aware suggestions for the DSL editor.
┌─────────────────────────────────────────────────────────────┐
│ REST API: /myfasthtml/autocompletion │
│ REST API: /myfasthtml/completions
│ Request: { "text": "...", "cursor": {"line": 1, "ch": 15} }│
└─────────────────────────────────────────────────────────────┘
@@ -1024,33 +1024,43 @@ When the engine needs column values for `OPERATOR_VALUE` context, it uses the de
### DatagridMetadataProvider
A helper class providing access to DataGrid metadata for context-aware suggestions:
A Protocol providing access to DataGrid metadata for context-aware suggestions:
```python
class DatagridMetadataProvider:
class DatagridMetadataProvider(Protocol):
"""Provides DataGrid metadata for autocompletion."""
def get_tables(self) -> list[str]:
def list_tables(self) -> list[str]:
"""List of available DataGrids (namespace.name format)."""
...
def get_columns(self, table_name: str) -> list[str]:
def list_columns(self, table_name: str) -> list[str]:
"""Column names for a specific DataGrid."""
...
def get_column_values(self, column_name: str) -> list[Any]:
def list_column_values(self, table_name: str, column_name: str) -> list[Any]:
"""Distinct values for a column in the current scope."""
...
def get_row_count(self, table_name: str) -> int:
"""Number of rows in a DataGrid."""
...
def list_style_presets(self) -> list[str]:
"""List of available style preset names."""
...
def list_format_presets(self) -> list[str]:
"""List of available format preset names."""
...
```
**Notes:**
- `DatagridMetadataProvider` is a Protocol (structural typing), not an abstract base class
- DataGrid names follow the pattern `namespace.name` (multi-level namespaces supported)
- The provider is passed at initialization, not with each API call
- Column values are fetched lazily when the scope is detected
- `DataGridsManager` implements this Protocol
### API Interface
@@ -1230,24 +1240,35 @@ The following features are excluded from autocompletion for simplicity:
| Component | Status | Location |
|-----------|--------|----------|
| **DSL Parser** | | |
| DSL Grammar (lark) | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/grammar.py` |
| DSL Parser | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/parser.py` |
| DSL Transformer | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/transformer.py` |
| Scope Dataclasses | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/scopes.py` |
| Exceptions | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/exceptions.py` |
| Public API (`parse_dsl()`) | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/__init__.py` |
| Unit Tests (Parser) | :white_check_mark: ~35 tests | `tests/core/formatting/test_dsl_parser.py` |
| Unit Tests (Parser) | :white_check_mark: ~35 tests | `tests/core/formatting/dsl/test_dsl_parser.py` |
| **Autocompletion** | | |
| DatagridMetadataProvider | :x: Not implemented | `src/myfasthtml/core/formatting/dsl/completion.py` |
| Scope Detector | :x: Not implemented | `src/myfasthtml/core/formatting/dsl/completion.py` |
| Context Detector | :x: Not implemented | `src/myfasthtml/core/formatting/dsl/completion.py` |
| Suggestions Generator | :x: Not implemented | `src/myfasthtml/core/formatting/dsl/completion.py` |
| REST Endpoint | :x: Not implemented | `/myfasthtml/autocompletion` |
| Unit Tests (Completion) | :x: Not implemented | `tests/core/formatting/test_dsl_completion.py` |
| DatagridMetadataProvider | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/completion/provider.py` |
| Scope Detector | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/completion/contexts.py` |
| Context Detector | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/completion/contexts.py` |
| Suggestions Generator | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/completion/suggestions.py` |
| Completion Engine | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/completion/engine.py` |
| Presets | :white_check_mark: Implemented | `src/myfasthtml/core/formatting/dsl/completion/presets.py` |
| Unit Tests (Completion) | :white_check_mark: ~50 tests | `tests/core/formatting/dsl/test_completion.py` |
| REST Endpoint | :white_check_mark: Implemented | `src/myfasthtml/core/utils.py``/myfasthtml/completions` |
| **Client-side** | | |
| Lezer Grammar | :x: Not implemented | `static/js/formatrules.grammar` |
| CodeMirror Extension | :x: Not implemented | `static/js/formatrules-editor.js` |
| DaisyUI Theme | :x: Not implemented | `static/js/daisy-theme.js` |
| Lark to Lezer converter | :white_check_mark: Implemented | `src/myfasthtml/core/dsl/lark_to_lezer.py` |
| CodeMirror 5 assets | :white_check_mark: Implemented | `assets/codemirror.min.js`, `show-hint.min.js` |
| DslEditor control | :white_check_mark: Implemented | `src/myfasthtml/controls/DslEditor.py` |
| initDslEditor() JS | :white_check_mark: Implemented | `assets/myfasthtml.js` (static completions only) |
| Dynamic completions (server calls) | :white_check_mark: Implemented | `assets/myfasthtml.js``/myfasthtml/completions` |
| DaisyUI Theme | :o: Deferred | - |
| **Syntax Validation (Linting)** | | |
| REST Endpoint | :white_check_mark: Implemented | `src/myfasthtml/core/utils.py``/myfasthtml/validations` |
| CodeMirror lint integration | :white_check_mark: Implemented | `assets/myfasthtml.js``dslLint()` |
| Lint CSS/JS assets | :white_check_mark: Added | `assets/lint.min.js`, `assets/lint.css` |
| Warnings (semantic validation) | :o: Future | Not yet implemented (see note below) |
---
@@ -1289,7 +1310,15 @@ src/myfasthtml/core/formatting/dsl/
├── parser.py # DSLParser class with Indenter
├── transformer.py # AST → dataclass conversion
├── scopes.py # ColumnScope, RowScope, CellScope, ScopedRule
── exceptions.py # DSLSyntaxError, DSLValidationError
── exceptions.py # DSLSyntaxError, DSLValidationError
├── definition.py # FormattingDSL class for DslEditor
└── completion/ # Autocompletion module
├── __init__.py
├── contexts.py # Context enum, detect_scope(), detect_context()
├── suggestions.py # get_suggestions() for each context
├── engine.py # FormattingCompletionEngine, get_completions()
├── presets.py # Static suggestions (keywords, operators, colors)
└── provider.py # DatagridMetadataProvider protocol
```
**Output structure:**
@@ -1303,15 +1332,80 @@ class ScopedRule:
rule: FormatRule # From core/formatting/dataclasses.py
```
### Syntax Validation (Linting)
The DSL editor provides real-time syntax validation via CodeMirror's lint addon.
**Architecture:**
```
┌─────────────────────────────────────────────────────────────┐
│ CodeMirror Editor │
│ User types invalid syntax │
└─────────────────────────────────────────────────────────────┘
│ (debounced, 500ms)
┌─────────────────────────────────────────────────────────────┐
│ REST API: /myfasthtml/validations │
│ Request: { e_id, text, line, ch } │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Python: parse_dsl(text) │
│ - If OK: return {"errors": []} │
│ - If DSLSyntaxError: return error with line/column/message │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ CodeMirror Lint Display │
│ - Gutter marker (error icon) │
│ - Underline on error position │
│ - Tooltip with error message on hover │
└─────────────────────────────────────────────────────────────┘
```
**API Response Format:**
```json
{
"errors": [
{
"line": 5,
"column": 12,
"message": "Expected 'column', 'row' or 'cell'",
"severity": "error"
}
]
}
```
**Note:** Line and column are 1-based (from lark parser). The JavaScript client converts to 0-based for CodeMirror.
**Future: Warnings Support**
Currently, only syntax errors (parse failures) are reported. Semantic warnings are planned for a future release:
| Warning Type | Example | Status |
|--------------|---------|--------|
| Unknown style preset | `style("unknown_preset")` | :o: Future |
| Unknown format preset | `format("invalid")` | :o: Future |
| Unknown parameter | `style(invalid_param=True)` | :o: Future |
| Non-existent column reference | `col.nonexistent` | :o: Future |
These warnings would be non-blocking (DSL still parses) but displayed with a different severity in the editor.
### Next Steps
1. ~~**Add `lark` to dependencies** in `pyproject.toml`~~ Done
2. **Implement autocompletion API**:
- `DatagridMetadataProvider` class
- Scope detection (column/row/cell)
- Context detection
- Suggestions generation
- REST endpoint `/myfasthtml/autocompletion`
3. **Translate lark grammar to Lezer** for client-side parsing
4. **Build CodeMirror extension** with DaisyUI theme
2. ~~**Implement autocompletion API**~~ Done
3. ~~**Translate lark grammar to Lezer**~~ Done (`lark_to_lezer.py`)
4. ~~**Build CodeMirror extension**~~ Done (`DslEditor.py` + `initDslEditor()`)
5. ~~**Integrate with DataGrid** - connect DSL output to formatting engine~~ Done
6. ~~**Implement dynamic client-side completions**~~ Done
- Engine ID: `DSLDefinition.get_id()` passed via `completionEngineId` in JS config
- Trigger: Hybrid (Ctrl+Space + auto after `.` `(` `"` and space)
- Files modified:
- `src/myfasthtml/controls/DslEditor.py:134` - Added `completionEngineId`
- `src/myfasthtml/assets/myfasthtml.js:2138-2300` - Async fetch to `/myfasthtml/completions`