flet-code-editor-dsl 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flet_code_editor_dsl-0.1.0/PKG-INFO +619 -0
- flet_code_editor_dsl-0.1.0/README.md +603 -0
- flet_code_editor_dsl-0.1.0/pyproject.toml +19 -0
- flet_code_editor_dsl-0.1.0/setup.cfg +4 -0
- flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl/__init__.py +77 -0
- flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl.egg-info/PKG-INFO +619 -0
- flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl.egg-info/SOURCES.txt +14 -0
- flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl.egg-info/dependency_links.txt +1 -0
- flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl.egg-info/requires.txt +1 -0
- flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl.egg-info/top_level.txt +2 -0
- flet_code_editor_dsl-0.1.0/src/flutter/__init__.py +0 -0
- flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/__init__.py +0 -0
- flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/lib/flet_code_editor_dsl.dart +3 -0
- flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/lib/src/code_editor.dart +294 -0
- flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/lib/src/extension.dart +13 -0
- flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/pubspec.yaml +13 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flet-code-editor-dsl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A real Flutter code editor exposed as a Flet control
|
|
5
|
+
Author: obgwew
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/obgwew/flet-code-editor-dsl
|
|
8
|
+
Project-URL: Issues, https://github.com/obgwew/flet-code-editor-dsl/issues
|
|
9
|
+
Keywords: flet,flutter,code-editor,syntax-highlighting
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: flet>=0.80
|
|
16
|
+
|
|
17
|
+
# flet-code-editor-dsl
|
|
18
|
+
|
|
19
|
+
> A real Flutter code editor exposed as a [Flet](https://flet.dev) control, with a small Python DSL for defining syntax-highlighting rules and bracket/quote pair matching.
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+

|
|
23
|
+

|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Table of Contents
|
|
29
|
+
|
|
30
|
+
1. [Overview](#overview)
|
|
31
|
+
2. [Features](#features)
|
|
32
|
+
3. [Requirements](#requirements)
|
|
33
|
+
4. [Installation](#installation)
|
|
34
|
+
- [Install directly from GitHub with pip](#option-1-install-directly-from-github-with-pip)
|
|
35
|
+
- [Use in a Flet app (`pyproject.toml`)](#option-2-declare-it-as-a-dependency-in-your-flet-app)
|
|
36
|
+
- [Pin to a branch, tag or commit](#pinning-a-version)
|
|
37
|
+
- [Local / editable install](#option-3-local-editable-install)
|
|
38
|
+
5. [Quick Start](#quick-start)
|
|
39
|
+
6. [API Reference](#api-reference)
|
|
40
|
+
- [`CodeEditor`](#codeeditor)
|
|
41
|
+
- [`rule()`](#rule)
|
|
42
|
+
- [`group()`](#group)
|
|
43
|
+
- [`pair()`](#pair)
|
|
44
|
+
- [`rules_json()` and `pairs_json()`](#rules_json-and-pairs_json)
|
|
45
|
+
7. [How the Highlighting Engine Works](#how-the-highlighting-engine-works)
|
|
46
|
+
8. [Controlling the Editor at Runtime](#controlling-the-editor-at-runtime)
|
|
47
|
+
9. [Complete Example](#complete-example)
|
|
48
|
+
10. [Building Your App](#building-your-app)
|
|
49
|
+
11. [Project Structure](#project-structure)
|
|
50
|
+
12. [Known Limitations](#known-limitations)
|
|
51
|
+
13. [Troubleshooting](#troubleshooting)
|
|
52
|
+
14. [Contributing](#contributing)
|
|
53
|
+
15. [License](#license)
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Overview
|
|
58
|
+
|
|
59
|
+
`flet-code-editor-dsl` is a Flet extension that embeds a multi-line code editor built on Flutter's `TextField`. Instead of shipping a fixed set of language grammars, it provides a compact **DSL** (domain-specific language) in Python that lets you describe exactly how text should be colored:
|
|
60
|
+
|
|
61
|
+
- **Rules** — regular expressions mapped to colors, with optional context conditions (`before`, `after`, `ignore_if`).
|
|
62
|
+
- **Pairs** — matching delimiters (brackets, quotes, tags) with distinct colors for matched and unmatched occurrences.
|
|
63
|
+
- **Strict mode** — any text not covered by a rule can be flagged as an error.
|
|
64
|
+
|
|
65
|
+
This makes it well suited for custom languages, configuration formats, templating syntaxes, and educational tools.
|
|
66
|
+
|
|
67
|
+
## Features
|
|
68
|
+
|
|
69
|
+
- Native Flutter rendering through the Flet extension mechanism.
|
|
70
|
+
- Declarative highlighting rules defined entirely from Python.
|
|
71
|
+
- Context-aware matching using look-behind (`before`), look-ahead (`after`) and exclusion (`ignore_if`) patterns.
|
|
72
|
+
- Grouped rules: several patterns sharing a single color.
|
|
73
|
+
- Bracket and quote pair matching, including nesting and unmatched-delimiter detection.
|
|
74
|
+
- Strict mode that paints unrecognized text with an error color.
|
|
75
|
+
- Configurable font family, font size and background color.
|
|
76
|
+
- `on_change` event for reading the current text.
|
|
77
|
+
- Inherits standard layout properties from `ft.LayoutControl` (`width`, `height`, `expand`, `margin`, etc.).
|
|
78
|
+
|
|
79
|
+
## Requirements
|
|
80
|
+
|
|
81
|
+
| Component | Version |
|
|
82
|
+
|-----------|---------|
|
|
83
|
+
| Python | `>= 3.10` |
|
|
84
|
+
| Flet | `>= 0.80` (the Flutter side depends on `flet: ^0.80.0`) |
|
|
85
|
+
| Dart SDK | `>= 3.0.0 < 4.0.0` |
|
|
86
|
+
| Flutter | `>= 3.10.0` |
|
|
87
|
+
| Git | Required for installing from GitHub |
|
|
88
|
+
|
|
89
|
+
> **Note:** Because this package contains a Flutter extension, it only takes effect when your app is packaged with `flet build` (or run with a Flet client that includes the extension). See [Building Your App](#building-your-app).
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Installation
|
|
94
|
+
|
|
95
|
+
### Option 1: Install directly from GitHub with pip
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or, using the explicit direct-reference form:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install "flet-code-editor-dsl @ git+https://github.com/obgwew/flet-code-editor-dsl.git"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Option 2: Declare it as a dependency in your Flet app
|
|
108
|
+
|
|
109
|
+
Add the package to the `dependencies` of your app's `pyproject.toml`:
|
|
110
|
+
|
|
111
|
+
```toml
|
|
112
|
+
[project]
|
|
113
|
+
name = "my-flet-app"
|
|
114
|
+
version = "0.1.0"
|
|
115
|
+
requires-python = ">=3.10"
|
|
116
|
+
dependencies = [
|
|
117
|
+
"flet",
|
|
118
|
+
"flet-code-editor-dsl @ git+https://github.com/obgwew/flet-code-editor-dsl.git",
|
|
119
|
+
]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Then install and run as usual:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install -e .
|
|
126
|
+
flet run
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
If you use a `requirements.txt` instead:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
flet
|
|
133
|
+
git+https://github.com/obgwew/flet-code-editor-dsl.git
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Pinning a version
|
|
137
|
+
|
|
138
|
+
Append `@<ref>` to the URL, where `<ref>` is a branch, tag, or commit hash:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Branch
|
|
142
|
+
pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git@main"
|
|
143
|
+
|
|
144
|
+
# Tag
|
|
145
|
+
pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git@v0.1.0"
|
|
146
|
+
|
|
147
|
+
# Commit
|
|
148
|
+
pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git@<commit-sha>"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Pinning to a tag or commit is strongly recommended for reproducible builds.
|
|
152
|
+
|
|
153
|
+
### Option 3: Local editable install
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
git clone https://github.com/obgwew/flet-code-editor-dsl.git
|
|
157
|
+
cd flet-code-editor-dsl
|
|
158
|
+
pip install -e .
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Verifying the installation
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python -c "import flet_code_editor_dsl as m; print(m.__all__)"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Expected output:
|
|
168
|
+
|
|
169
|
+
```text
|
|
170
|
+
['CodeEditor', 'rule', 'group', 'pair', 'rules_json', 'pairs_json']
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Quick Start
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
import flet as ft
|
|
179
|
+
import flet_code_editor_dsl as ce
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def main(page: ft.Page):
|
|
183
|
+
page.title = "Code Editor"
|
|
184
|
+
|
|
185
|
+
editor = ce.CodeEditor(
|
|
186
|
+
value="let x = (1 + 2)",
|
|
187
|
+
rules=ce.rules_json([
|
|
188
|
+
ce.group(["let", "if", "else"], color="#C586C0"), # keywords
|
|
189
|
+
ce.rule(r"\d+", color="#B5CEA8"), # numbers
|
|
190
|
+
]),
|
|
191
|
+
pairs=ce.pairs_json([
|
|
192
|
+
ce.pair("(", ")", color="#FFD700"),
|
|
193
|
+
]),
|
|
194
|
+
default_color="#D4D4D4",
|
|
195
|
+
background_color="#1E1E1E",
|
|
196
|
+
expand=True,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
page.add(editor)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
ft.run(main)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
> On Flet versions that still use the older entry point, replace `ft.run(main)` with `ft.app(main)`.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## API Reference
|
|
210
|
+
|
|
211
|
+
All public names are importable from the top-level package:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from flet_code_editor_dsl import CodeEditor, rule, group, pair, rules_json, pairs_json
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### `CodeEditor`
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
@ft.control("flet_code_editor_dsl")
|
|
221
|
+
class CodeEditor(ft.LayoutControl): ...
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
| Property | Type | Default | Description |
|
|
225
|
+
|----------|------|---------|-------------|
|
|
226
|
+
| `value` | `str` | `""` | The text shown in the editor. Setting it from Python replaces the editor content. |
|
|
227
|
+
| `rules` | `str` (JSON) | `"[]"` | Highlighting rules. Build it with `rules_json([...])`. |
|
|
228
|
+
| `pairs` | `str` (JSON) | `"[]"` | Delimiter pairs. Build it with `pairs_json([...])`. |
|
|
229
|
+
| `default_color` | `str` | `"#000000"` | Color for text not matched by any rule (when `strict` is `False`). |
|
|
230
|
+
| `strict` | `bool` | `False` | If `True`, unmatched text is painted with `error_color` instead of `default_color`. |
|
|
231
|
+
| `error_color` | `str` | `"#FF0000"` | Color used for unrecognized text in strict mode. |
|
|
232
|
+
| `background_color` | `ft.ColorValue \| None` | `None` | Editor background. When `None`, the Flutter side falls back to `#202124`. |
|
|
233
|
+
| `font_family` | `str` | `"monospace"` | Font family used by the editor. |
|
|
234
|
+
| `font_size` | `float` | `14.0` | Font size in logical pixels. |
|
|
235
|
+
| `on_change` | `ft.ControlEventHandler \| None` | `None` | Called whenever the text changes. The new text is available as `e.data`. |
|
|
236
|
+
|
|
237
|
+
In addition, all `ft.LayoutControl` properties (such as `width`, `height`, `expand`, `margin`, `visible`, `opacity`) are supported.
|
|
238
|
+
|
|
239
|
+
> **Tip:** The default `default_color` is black (`#000000`) while the default background is dark. Always set `default_color` explicitly (for example `"#D4D4D4"`) when using a dark theme.
|
|
240
|
+
|
|
241
|
+
#### Color format
|
|
242
|
+
|
|
243
|
+
Colors are strings in one of these forms:
|
|
244
|
+
|
|
245
|
+
| Format | Example | Meaning |
|
|
246
|
+
|--------|---------|---------|
|
|
247
|
+
| `#RRGGBB` | `#FF8800` | Opaque color (alpha `FF` is added automatically). |
|
|
248
|
+
| `#AARRGGBB` | `#80FF8800` | Color with explicit alpha channel. |
|
|
249
|
+
|
|
250
|
+
Invalid values fall back to a default color instead of raising an error.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### `rule()`
|
|
255
|
+
|
|
256
|
+
Creates a single highlighting rule.
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
rule(
|
|
260
|
+
form: str,
|
|
261
|
+
color: str,
|
|
262
|
+
before: Optional[str] = None,
|
|
263
|
+
after: Optional[str] = None,
|
|
264
|
+
ignore_if: Optional[str] = None,
|
|
265
|
+
) -> dict
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| Parameter | Description |
|
|
269
|
+
|-----------|-------------|
|
|
270
|
+
| `form` | Regular expression that selects the text to color. |
|
|
271
|
+
| `color` | Color applied to every match. |
|
|
272
|
+
| `before` | Regex that must match **immediately before** the match (it is anchored to the end of the preceding text). If it does not match, the occurrence is skipped. |
|
|
273
|
+
| `after` | Regex that must match **immediately after** the match (anchored to the start of the following text). |
|
|
274
|
+
| `ignore_if` | Regex tested at the **start of the match**. If it matches, the occurrence is skipped. |
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
# Highlight identifiers that directly follow "def "
|
|
278
|
+
ce.rule(r"[A-Za-z_]\w*", color="#DCDCAA", before=r"def\s+")
|
|
279
|
+
|
|
280
|
+
# Highlight names followed by "(" as function calls
|
|
281
|
+
ce.rule(r"[A-Za-z_]\w*", color="#DCDCAA", after=r"\(")
|
|
282
|
+
|
|
283
|
+
# Highlight words, but skip the keyword "let"
|
|
284
|
+
ce.rule(r"[A-Za-z_]\w*", color="#9CDCFE", ignore_if=r"let\b")
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### `group()`
|
|
288
|
+
|
|
289
|
+
Creates a rule that applies **one color to several patterns**. The patterns are combined into a single alternation.
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
group(
|
|
293
|
+
forms: list[str],
|
|
294
|
+
color: str,
|
|
295
|
+
before: Optional[str] = None,
|
|
296
|
+
after: Optional[str] = None,
|
|
297
|
+
ignore_if: Optional[str] = None,
|
|
298
|
+
) -> dict
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
ce.group([r"\bif\b", r"\belse\b", r"\bwhile\b", r"\breturn\b"], color="#C586C0")
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `pair()`
|
|
306
|
+
|
|
307
|
+
Defines a pair of delimiters that are matched against each other.
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
pair(
|
|
311
|
+
open: str,
|
|
312
|
+
close: str,
|
|
313
|
+
color: Optional[str] = None,
|
|
314
|
+
unmatched_color: str = "#FF0000",
|
|
315
|
+
) -> dict
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
| Parameter | Description |
|
|
319
|
+
|-----------|-------------|
|
|
320
|
+
| `open` | Opening delimiter (a **literal string**, not a regex). |
|
|
321
|
+
| `close` | Closing delimiter (a literal string). |
|
|
322
|
+
| `color` | Color for correctly matched delimiters. If `None`, matched delimiters keep the color assigned by rules. |
|
|
323
|
+
| `unmatched_color` | Color for delimiters that have no partner. Defaults to red. |
|
|
324
|
+
|
|
325
|
+
Behavior:
|
|
326
|
+
|
|
327
|
+
- When `open != close` (e.g. `(` and `)`), delimiters are matched using a stack, so nesting is supported. An unmatched closer, or an opener left on the stack, is painted with `unmatched_color`.
|
|
328
|
+
- When `open == close` (e.g. `"` and `"`), occurrences are counted in order. If the total count is odd, the **last** one is treated as unmatched.
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
ce.pair("(", ")", color="#FFD700")
|
|
332
|
+
ce.pair("[", "]", color="#DA70D6")
|
|
333
|
+
ce.pair("{", "}", color="#179FFF")
|
|
334
|
+
ce.pair('"', '"', color="#CE9178", unmatched_color="#FF5555")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### `rules_json()` and `pairs_json()`
|
|
338
|
+
|
|
339
|
+
Serialize a list of rules or pairs into the compact JSON string expected by `CodeEditor.rules` and `CodeEditor.pairs`.
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
rules_json(rules: Iterable[dict]) -> str
|
|
343
|
+
pairs_json(pairs: Iterable[dict]) -> str
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
editor.rules = ce.rules_json([ce.rule(r"\d+", "#B5CEA8")])
|
|
348
|
+
editor.pairs = ce.pairs_json([ce.pair("(", ")", "#FFD700")])
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## How the Highlighting Engine Works
|
|
354
|
+
|
|
355
|
+
Understanding the evaluation order helps you write predictable rules.
|
|
356
|
+
|
|
357
|
+
1. **Rule pass** — Rules are applied in the order they appear in the list. Each match paints its characters with the rule's color. **Later rules override earlier ones** where matches overlap.
|
|
358
|
+
2. **Context checks** — For every match, `before`, `after` and `ignore_if` are evaluated. If a check fails, that match is ignored.
|
|
359
|
+
3. **Fallback pass** — Characters not covered by any rule receive `default_color`, or `error_color` if `strict=True`.
|
|
360
|
+
4. **Pair pass** — Pair matching runs last and **overrides** the colors of delimiter characters. This ensures unmatched delimiters are always visible.
|
|
361
|
+
|
|
362
|
+
**Practical consequences**
|
|
363
|
+
|
|
364
|
+
- Put general rules first (identifiers, words) and specific rules last (keywords, literals).
|
|
365
|
+
- Regular expressions use the **Dart `RegExp`** engine (ECMAScript-style), not Python's `re`. Most common syntax is identical, but avoid Python-only constructs such as `(?P<name>...)` — use `(?<name>...)` instead.
|
|
366
|
+
- Patterns that fail to compile are silently skipped, so test new patterns incrementally.
|
|
367
|
+
- Use raw strings (`r"..."`) in Python to avoid double escaping.
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Controlling the Editor at Runtime
|
|
372
|
+
|
|
373
|
+
### Reading the text
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
def on_change(e: ft.ControlEvent):
|
|
377
|
+
print("Current text:", e.data)
|
|
378
|
+
|
|
379
|
+
editor = ce.CodeEditor(on_change=on_change)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Setting the text
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
editor.value = "new content"
|
|
386
|
+
editor.update()
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Updating rules or pairs dynamically
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
editor.rules = ce.rules_json([
|
|
393
|
+
ce.rule(r"\bTODO\b", "#FFCC00"),
|
|
394
|
+
])
|
|
395
|
+
editor.update()
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Switching strict mode
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
editor.strict = True
|
|
402
|
+
editor.error_color = "#FF5555"
|
|
403
|
+
editor.update()
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Changing appearance
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
editor.font_size = 16
|
|
410
|
+
editor.font_family = "Courier New"
|
|
411
|
+
editor.background_color = "#0D1117"
|
|
412
|
+
editor.update()
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Layout
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
ce.CodeEditor(width=600, height=400) # fixed size
|
|
419
|
+
ce.CodeEditor(expand=True) # fill available space
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Complete Example
|
|
425
|
+
|
|
426
|
+
A live-validating editor for a tiny expression language. Keywords, numbers, strings and comments are colored, brackets are matched, and anything unrecognized is flagged as an error.
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
import flet as ft
|
|
430
|
+
import flet_code_editor_dsl as ce
|
|
431
|
+
|
|
432
|
+
SAMPLE = '''\
|
|
433
|
+
# Example program
|
|
434
|
+
let total = (price * 3) + 12
|
|
435
|
+
if (total > 100) {
|
|
436
|
+
print("expensive")
|
|
437
|
+
} else {
|
|
438
|
+
print("cheap")
|
|
439
|
+
}
|
|
440
|
+
'''
|
|
441
|
+
|
|
442
|
+
RULES = ce.rules_json([
|
|
443
|
+
# Identifiers (general rule first)
|
|
444
|
+
ce.rule(r"[A-Za-z_]\w*", "#9CDCFE"),
|
|
445
|
+
|
|
446
|
+
# Function calls: a name followed by "("
|
|
447
|
+
ce.rule(r"[A-Za-z_]\w*", "#DCDCAA", after=r"\("),
|
|
448
|
+
|
|
449
|
+
# Keywords
|
|
450
|
+
ce.group([r"\blet\b", r"\bif\b", r"\belse\b"], "#C586C0"),
|
|
451
|
+
|
|
452
|
+
# Numbers
|
|
453
|
+
ce.rule(r"\b\d+(?:\.\d+)?\b", "#B5CEA8"),
|
|
454
|
+
|
|
455
|
+
# Operators and whitespace (so strict mode does not flag them)
|
|
456
|
+
ce.group([r"[+\-*/=<>!]+", r"\s+", r"[(){}\[\],;]"], "#D4D4D4"),
|
|
457
|
+
|
|
458
|
+
# Strings
|
|
459
|
+
ce.rule(r'"[^"\n]*"', "#CE9178"),
|
|
460
|
+
|
|
461
|
+
# Comments (last, so they override everything on the line)
|
|
462
|
+
ce.rule(r"#.*", "#6A9955"),
|
|
463
|
+
])
|
|
464
|
+
|
|
465
|
+
PAIRS = ce.pairs_json([
|
|
466
|
+
ce.pair("(", ")", color="#FFD700"),
|
|
467
|
+
ce.pair("{", "}", color="#179FFF"),
|
|
468
|
+
ce.pair("[", "]", color="#DA70D6"),
|
|
469
|
+
])
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def main(page: ft.Page):
|
|
473
|
+
page.title = "flet-code-editor-dsl demo"
|
|
474
|
+
page.theme_mode = ft.ThemeMode.DARK
|
|
475
|
+
|
|
476
|
+
status = ft.Text("Characters: %d" % len(SAMPLE))
|
|
477
|
+
|
|
478
|
+
def on_change(e: ft.ControlEvent):
|
|
479
|
+
status.value = "Characters: %d" % len(e.data or "")
|
|
480
|
+
status.update()
|
|
481
|
+
|
|
482
|
+
def toggle_strict(e: ft.ControlEvent):
|
|
483
|
+
editor.strict = e.control.value
|
|
484
|
+
editor.update()
|
|
485
|
+
|
|
486
|
+
editor = ce.CodeEditor(
|
|
487
|
+
value=SAMPLE,
|
|
488
|
+
rules=RULES,
|
|
489
|
+
pairs=PAIRS,
|
|
490
|
+
default_color="#D4D4D4",
|
|
491
|
+
strict=False,
|
|
492
|
+
error_color="#F44747",
|
|
493
|
+
background_color="#1E1E1E",
|
|
494
|
+
font_family="monospace",
|
|
495
|
+
font_size=15,
|
|
496
|
+
expand=True,
|
|
497
|
+
on_change=on_change,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
page.add(
|
|
501
|
+
ft.Row([ft.Switch(label="Strict mode", on_change=toggle_strict), status]),
|
|
502
|
+
editor,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
ft.run(main)
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## Building Your App
|
|
512
|
+
|
|
513
|
+
Flutter extensions are compiled into the Flet client, so you must build a custom client for your target platform. Make sure the package is listed in your app's `pyproject.toml` dependencies (see [Installation](#option-2-declare-it-as-a-dependency-in-your-flet-app)), then run:
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
# Desktop
|
|
517
|
+
flet build windows
|
|
518
|
+
flet build macos
|
|
519
|
+
flet build linux
|
|
520
|
+
|
|
521
|
+
# Mobile
|
|
522
|
+
flet build apk
|
|
523
|
+
flet build ipa
|
|
524
|
+
|
|
525
|
+
# Web
|
|
526
|
+
flet build web
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
`flet build` detects the Flutter package bundled inside `flutter/flet_code_editor_dsl/` and adds it to the generated Flutter project automatically. A working Flutter SDK is required; Flet will download one if it is missing.
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Project Structure
|
|
534
|
+
|
|
535
|
+
```text
|
|
536
|
+
flet-code-editor-dsl/
|
|
537
|
+
├── pyproject.toml
|
|
538
|
+
└── src/
|
|
539
|
+
├── flet_code_editor_dsl/
|
|
540
|
+
│ └── __init__.py # Python API: CodeEditor + DSL helpers
|
|
541
|
+
└── flutter/
|
|
542
|
+
├── __init__.py
|
|
543
|
+
└── flet_code_editor_dsl/
|
|
544
|
+
├── __init__.py
|
|
545
|
+
├── pubspec.yaml # Flutter package definition
|
|
546
|
+
└── lib/
|
|
547
|
+
├── flet_code_editor_dsl.dart
|
|
548
|
+
└── src/
|
|
549
|
+
├── extension.dart # Registers the control with Flet
|
|
550
|
+
└── code_editor.dart # Editor widget + highlighting engine
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
| File | Responsibility |
|
|
554
|
+
|------|----------------|
|
|
555
|
+
| `src/flet_code_editor_dsl/__init__.py` | Declares `CodeEditor` and the `rule` / `group` / `pair` helpers. |
|
|
556
|
+
| `.../lib/src/extension.dart` | Maps the control type `flet_code_editor_dsl` to the Flutter widget. |
|
|
557
|
+
| `.../lib/src/code_editor.dart` | Custom `TextEditingController` that builds colored `TextSpan`s from rules and pairs. |
|
|
558
|
+
| `pyproject.toml` | Packaging metadata; includes `pubspec.yaml` and `lib/**/*` as package data. |
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Known Limitations
|
|
563
|
+
|
|
564
|
+
- **No language awareness in pairs.** Delimiters inside strings or comments are still matched. Use `ignore_if` or rule ordering to reduce false positives, or avoid pairing characters that appear inside literals.
|
|
565
|
+
- **Highlighting is recomputed on each rebuild.** Very large documents with many rules may reduce typing performance.
|
|
566
|
+
- **Plain text editing only.** Line numbers, code folding, auto-indent and autocompletion are not included.
|
|
567
|
+
- **Dart regex semantics.** Patterns must be valid for Dart's `RegExp`; invalid patterns are ignored without an error message.
|
|
568
|
+
- **Client rebuild required.** As with all Flet extensions, the control is not available in the stock Flet client.
|
|
569
|
+
- **Minimum height.** The editor shows at least 12 lines.
|
|
570
|
+
|
|
571
|
+
## Troubleshooting
|
|
572
|
+
|
|
573
|
+
| Symptom | Likely cause and fix |
|
|
574
|
+
|---------|----------------------|
|
|
575
|
+
| The editor does not appear, or an "unknown control" error is shown | The app is running with a stock Flet client. Build with `flet build`, or ensure the extension is registered in your custom client. |
|
|
576
|
+
| Text is hard to see | `default_color` defaults to black while the background is dark. Set `default_color` (for example `#D4D4D4`) or change `background_color`. |
|
|
577
|
+
| A rule has no effect | Its regex may be invalid for Dart, or a later rule overrides it. Simplify the pattern and check rule order. |
|
|
578
|
+
| Everything is red | `strict=True` is set and some text is not covered by any rule. Add rules for whitespace, operators and punctuation, or disable strict mode. |
|
|
579
|
+
| `pip install` from GitHub fails | Confirm Git is installed, the URL is correct, and (for private repositories) that you are authenticated via SSH or a personal access token. |
|
|
580
|
+
| Changes made from Python are not visible | Call `editor.update()` after modifying a property. |
|
|
581
|
+
|
|
582
|
+
For private repositories, authenticate with a token:
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
pip install "git+https://<TOKEN>@github.com/obgwew/flet-code-editor-dsl.git"
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Contributing
|
|
589
|
+
|
|
590
|
+
Contributions are welcome.
|
|
591
|
+
|
|
592
|
+
1. Fork the repository.
|
|
593
|
+
2. Create a feature branch: `git checkout -b feature/my-feature`.
|
|
594
|
+
3. Commit your changes with clear messages.
|
|
595
|
+
4. Push the branch and open a Pull Request describing the change and its motivation.
|
|
596
|
+
|
|
597
|
+
Please open an issue first for larger changes or new features so the design can be discussed.
|
|
598
|
+
|
|
599
|
+
## License
|
|
600
|
+
|
|
601
|
+
This project is licensed under the **Apache License, Version 2.0**. See the [LICENSE](LICENSE) file for the full text.
|
|
602
|
+
|
|
603
|
+
```text
|
|
604
|
+
Copyright 2026 obgwew
|
|
605
|
+
|
|
606
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
607
|
+
you may not use this file except in compliance with the License.
|
|
608
|
+
You may obtain a copy of the License at
|
|
609
|
+
|
|
610
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
611
|
+
|
|
612
|
+
Unless required by applicable law or agreed to in writing, software
|
|
613
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
614
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
615
|
+
See the License for the specific language governing permissions and
|
|
616
|
+
limitations under the License.
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
By contributing to this repository, you agree that your contributions will be licensed under the same Apache 2.0 license.
|