flet-code-editor-dsl 0.1.0__tar.gz → 0.2.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.
Files changed (30) hide show
  1. flet_code_editor_dsl-0.2.0/PKG-INFO +633 -0
  2. flet_code_editor_dsl-0.2.0/README.md +620 -0
  3. flet_code_editor_dsl-0.2.0/pyproject.toml +35 -0
  4. flet_code_editor_dsl-0.2.0/src/flet_code_editor_dsl/__init__.py +33 -0
  5. flet_code_editor_dsl-0.2.0/src/flet_code_editor_dsl/flet_code_editor_dsl.py +75 -0
  6. flet_code_editor_dsl-0.2.0/src/flet_code_editor_dsl/language.py +110 -0
  7. flet_code_editor_dsl-0.2.0/src/flet_code_editor_dsl.egg-info/PKG-INFO +633 -0
  8. {flet_code_editor_dsl-0.1.0 → flet_code_editor_dsl-0.2.0}/src/flet_code_editor_dsl.egg-info/SOURCES.txt +5 -3
  9. flet_code_editor_dsl-0.2.0/src/flet_code_editor_dsl.egg-info/requires.txt +1 -0
  10. flet_code_editor_dsl-0.2.0/src/flutter/flet_code_editor_dsl/lib/flet_code_editor_dsl.dart +3 -0
  11. flet_code_editor_dsl-0.2.0/src/flutter/flet_code_editor_dsl/lib/src/code_editor.dart +273 -0
  12. flet_code_editor_dsl-0.2.0/src/flutter/flet_code_editor_dsl/lib/src/extension.dart +19 -0
  13. flet_code_editor_dsl-0.2.0/src/flutter/flet_code_editor_dsl/lib/src/flet_code_editor_dsl.dart +233 -0
  14. flet_code_editor_dsl-0.2.0/src/flutter/flet_code_editor_dsl/lib/src/tokenizer.dart +817 -0
  15. flet_code_editor_dsl-0.2.0/src/flutter/flet_code_editor_dsl/pubspec.yaml +20 -0
  16. flet_code_editor_dsl-0.1.0/PKG-INFO +0 -619
  17. flet_code_editor_dsl-0.1.0/README.md +0 -603
  18. flet_code_editor_dsl-0.1.0/pyproject.toml +0 -19
  19. flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl/__init__.py +0 -77
  20. flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl.egg-info/PKG-INFO +0 -619
  21. flet_code_editor_dsl-0.1.0/src/flet_code_editor_dsl.egg-info/requires.txt +0 -1
  22. flet_code_editor_dsl-0.1.0/src/flutter/__init__.py +0 -0
  23. flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/__init__.py +0 -0
  24. flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/lib/flet_code_editor_dsl.dart +0 -3
  25. flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/lib/src/code_editor.dart +0 -294
  26. flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/lib/src/extension.dart +0 -13
  27. flet_code_editor_dsl-0.1.0/src/flutter/flet_code_editor_dsl/pubspec.yaml +0 -13
  28. {flet_code_editor_dsl-0.1.0 → flet_code_editor_dsl-0.2.0}/setup.cfg +0 -0
  29. {flet_code_editor_dsl-0.1.0 → flet_code_editor_dsl-0.2.0}/src/flet_code_editor_dsl.egg-info/dependency_links.txt +0 -0
  30. {flet_code_editor_dsl-0.1.0 → flet_code_editor_dsl-0.2.0}/src/flet_code_editor_dsl.egg-info/top_level.txt +0 -0
@@ -0,0 +1,633 @@
1
+ Metadata-Version: 2.4
2
+ Name: flet-code-editor-dsl
3
+ Version: 0.2.0
4
+ Summary: Flet code editor control with syntax highlighting, line numbers and scroll modes
5
+ Author-email: Flet contributors <you@example.com>
6
+ Project-URL: Homepage, https://mydomain.dev
7
+ Project-URL: Documentation, https://github.com/MyGithubAccount/flet-code-editor-dsl
8
+ Project-URL: Repository, https://github.com/MyGithubAccount/flet-code-editor-dsl
9
+ Project-URL: Issues, https://github.com/MyGithubAccount/flet-code-editor-dsl/issues
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: flet>=0.85.2
13
+
14
+ # flet-code-editor-dsl
15
+
16
+ > A code editor control for [Flet](https://flet.dev) with regex-based syntax highlighting, optional line numbers, toggleable colors and configurable scrolling.
17
+
18
+ ![Python](https://img.shields.io/badge/python-%3E%3D3.10-blue)
19
+ ![Flet](https://img.shields.io/badge/flet-%3E%3D0.85.2-informational)
20
+ ![License](https://img.shields.io/badge/license-Apache%202.0-blue)
21
+
22
+ ---
23
+
24
+ ## Table of Contents
25
+
26
+ 1. [Overview](#overview)
27
+ 2. [Features](#features)
28
+ 3. [Requirements](#requirements)
29
+ 4. [Installation](#installation)
30
+ 5. [Quick Start](#quick-start)
31
+ 6. [API Reference](#api-reference)
32
+ 7. [Line Numbers](#line-numbers)
33
+ 8. [Enabling and Disabling Colors](#enabling-and-disabling-colors)
34
+ 9. [Scrolling and Size Modes](#scrolling-and-size-modes)
35
+ 10. [Controlling the Editor at Runtime](#controlling-the-editor-at-runtime)
36
+ 11. [Complete Example](#complete-example)
37
+ 12. [Important: First Build Required](#important-first-build-required)
38
+ 13. [Building Your App](#building-your-app)
39
+ 14. [Project Structure](#project-structure)
40
+ 15. [Known Limitations](#known-limitations)
41
+ 16. [Troubleshooting](#troubleshooting)
42
+ 17. [Contributing](#contributing)
43
+ 18. [License](#license)
44
+
45
+ ---
46
+
47
+ ## Overview
48
+
49
+ `flet-code-editor-dsl` is a Flet extension that embeds code editors for Flutter. You describe how text is colored with a list of `HighlightRule` objects, each one a regular expression paired with a color. Two controls are provided:
50
+
51
+ | Control | Built on | Use it when |
52
+ |---------|----------|-------------|
53
+ | **`FletCodeEditor`** (recommended) | [`re_editor`](https://pub.dev/packages/re_editor), a custom editor that lays out and paints each line independently and builds only the visible lines. Highlighting uses a hand-written **tokenizer** (no regex), configured with `Language`. | You edit large scripts, or typing must stay fast. |
54
+ | `FletCodeField` | Flutter `TextField` ([`code_text_field`](https://pub.dev/packages/code_text_field)) with per-line highlight caching | Small texts, or you want the platform's native text field behavior. |
55
+
56
+ ## Features
57
+
58
+ - Native Flutter rendering through the Flet extension mechanism.
59
+ - Regex-based highlighting rules defined from Python.
60
+ - **Line numbers** that can be shown or hidden.
61
+ - **Colors** that can be enabled or disabled without losing your rules.
62
+ - **Scroll modes**: fixed size, bounded growth, or unlimited growth, plus horizontal scrolling or line wrapping.
63
+ - `on_change` event for reading the current text.
64
+ - Inherits standard layout properties from `ft.LayoutControl` (`width`, `height`, `expand`, `margin`, etc.).
65
+
66
+ ## Requirements
67
+
68
+ | Component | Version |
69
+ |-----------|---------|
70
+ | Python | `>= 3.10` |
71
+ | Flet | `>= 0.85.2` |
72
+ | Dart SDK | `>= 3.3.0 < 4.0.0` |
73
+ | Flutter | A version compatible with Flet 0.85 |
74
+
75
+ > **Note:** Because this package contains a Flutter extension, your app must declare it in `pyproject.toml` and be built once with `flet build`. See [Important: First Build Required](#important-first-build-required).
76
+
77
+ ---
78
+
79
+ ## Installation
80
+
81
+ ### From PyPI
82
+
83
+ ```bash
84
+ pip install flet-code-editor-dsl
85
+ ```
86
+
87
+ Declare it in your app's `pyproject.toml`:
88
+
89
+ ```toml
90
+ [project]
91
+ name = "my-flet-app"
92
+ version = "0.1.0"
93
+ requires-python = ">=3.10"
94
+ dependencies = [
95
+ "flet",
96
+ "flet-code-editor-dsl",
97
+ ]
98
+ ```
99
+
100
+ ### From GitHub
101
+
102
+ ```bash
103
+ pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git"
104
+ ```
105
+
106
+ ```toml
107
+ dependencies = [
108
+ "flet",
109
+ "flet-code-editor-dsl @ git+https://github.com/obgwew/flet-code-editor-dsl.git",
110
+ ]
111
+ ```
112
+
113
+ Append `@<branch|tag|commit>` to the URL to pin a version.
114
+
115
+ ### Local editable install
116
+
117
+ ```bash
118
+ git clone https://github.com/obgwew/flet-code-editor-dsl.git
119
+ cd flet-code-editor-dsl
120
+ pip install -e .
121
+ ```
122
+
123
+ ### Verifying the installation
124
+
125
+ ```bash
126
+ python -c "import flet_code_editor_dsl as m; print(m.__all__)"
127
+ ```
128
+
129
+ Expected output:
130
+
131
+ ```text
132
+ ['FletCodeEditor', 'FletCodeField', 'HighlightRule', 'VerticalMode']
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Quick Start
138
+
139
+ ```python
140
+ import flet as ft
141
+ from flet_code_editor_dsl import FletCodeField, HighlightRule
142
+
143
+
144
+ def main(page: ft.Page):
145
+ editor = FletCodeField(
146
+ value="let x = 42",
147
+ rules=[
148
+ HighlightRule(pattern=r"\b(let|if|else)\b", color="#C586C0"),
149
+ HighlightRule(pattern=r"\d+", color="#B5CEA8"),
150
+ ],
151
+ height=300,
152
+ )
153
+ page.add(editor)
154
+
155
+
156
+ ft.run(main)
157
+ ```
158
+
159
+ ### `FletCodeEditor`
160
+
161
+ ```python
162
+ import flet as ft
163
+ from flet_code_editor_dsl import (
164
+ FletCodeEditor, Language, WordGroup, StringRule, BlockComment, Sigil, TokenColors,
165
+ )
166
+
167
+ LANG = Language(
168
+ words=[WordGroup(["let", "if", "else", "while", "return"], "#C586C0")],
169
+ line_comments=["#"],
170
+ block_comments=[BlockComment("/*", "*/")],
171
+ strings=[StringRule('"'), StringRule("'")],
172
+ sigils=[Sigil("$", "#9CDCFE")],
173
+ colors=TokenColors(
174
+ comment="#6A9955", string="#CE9178", escape="#D7BA7D",
175
+ number="#B5CEA8", function="#DCDCAA", operator="#D4D4D4",
176
+ ),
177
+ )
178
+
179
+
180
+ def main(page: ft.Page):
181
+ page.add(
182
+ FletCodeEditor(
183
+ value='let x = 42 /* note */\nprint("hi\\n", $name)',
184
+ language=LANG.to_json(),
185
+ default_color="#D4D4D4",
186
+ background_color="#1E1E1E",
187
+ height=300,
188
+ )
189
+ )
190
+
191
+
192
+ ft.run(main)
193
+ ```
194
+
195
+ ---
196
+
197
+ ## API Reference
198
+
199
+ ```python
200
+ from flet_code_editor_dsl import FletCodeField, HighlightRule, VerticalMode
201
+ ```
202
+
203
+ ### `HighlightRule`
204
+
205
+ | Field | Type | Description |
206
+ |-------|------|-------------|
207
+ | `pattern` | `str` | Regular expression selecting the text to color. Uses the Dart `RegExp` engine. |
208
+ | `color` | `str` | `#RRGGBB` or `#AARRGGBB`. |
209
+
210
+ `HighlightRule` is used by **`FletCodeField`** only. `FletCodeEditor` uses a tokenizer described by [`Language`](#tokenizer-and-language) instead of regex rules.
211
+
212
+ ### `FletCodeEditor`
213
+
214
+ | Property | Type | Default | Description |
215
+ |----------|------|---------|-------------|
216
+ | `value` | `str` | `""` | The text. Setting it from Python replaces the editor content. |
217
+ | `language` | `str` | `""` | Tokenizer configuration: `Language(...).to_json()`. Empty = no highlighting. |
218
+ | `line_numbers` | `bool` | `True` | Show line numbers. |
219
+ | `syntax_highlighting` | `bool` | `True` | `False` disables all colors (rules are kept). |
220
+ | `wrap` | `bool` | `False` | `False`: horizontal scrolling. `True`: long lines wrap. |
221
+ | `read_only` | `bool` | `False` | Disable editing. |
222
+ | `vertical_mode` | `VerticalMode` | `FIXED` | `FIXED`, `BOUNDED` or `UNLIMITED` (see below). |
223
+ | `min_lines` / `max_lines` | `int \| None` | `None` | Limits for `BOUNDED` (fall back to 1 and 10). |
224
+ | `font_family` | `str` | `"Consolas"` | Editor font. |
225
+ | `font_size` | `float` | `12.0` | Font size. |
226
+ | `default_color` | `str \| None` | `None` | Color of text not matched by any rule (library default if `None`). |
227
+ | `background_color` | `str \| None` | `None` | Editor background (library default if `None`). |
228
+ | `debounce_ms` | `int` | `150` | Delay before the text is sent back to Python and `on_change` fires. Keeps typing fast in large documents. `value` on the Python side can lag by this delay. |
229
+ | `show_errors` | `bool` | `True` | Show error underlines (needs `Language(errors=ErrorRules(...))`). |
230
+ | `code_folding` | `bool` | `True` | Show fold markers (needs `Language(folds=[...])` or `fold_by_indent`). |
231
+ | `width_level` / `height_level` | `int \| None` | `13` | Visible size of the editor box, 1 to 30. See [Visible size](#visible-size). |
232
+ | `on_change` | handler | `None` | Called with the new text in `e.data` (after `debounce_ms`). |
233
+
234
+ Vertical modes for `FletCodeEditor`: `FIXED` fills the given height (set `height`, or `expand=True` inside a bounded parent) and scrolls inside. `BOUNDED` and `UNLIMITED` size the editor from the number of **lines** (wrapped lines are not counted), between `min_lines` and `max_lines` or without limit. The height is computed from the font metrics, so it can be off by a few pixels.
235
+
236
+ ### `FletCodeField`
237
+
238
+ | Property | Type | Default | Description |
239
+ |----------|------|---------|-------------|
240
+ | `value` | `str` | `""` | The text shown in the editor. |
241
+ | `rules` | `list[HighlightRule]` | `[]` | Regex highlighting rules (single-line). |
242
+ | `line_numbers` | `bool` | `True` | Show a line-number gutter. |
243
+ | `syntax_highlighting` | `bool` | `True` | When `False`, `rules` are ignored and no colors are applied. |
244
+ | `incremental_highlighting` | `bool` | `True` | Highlight line by line and re-highlight only the edited line. Set `False` to highlight the whole text at once (needed for patterns that span several lines). See [Performance](#performance). |
245
+ | `wrap` | `bool` | `False` | `False`: long lines scroll horizontally. `True`: long lines wrap. |
246
+ | `vertical_mode` | `VerticalMode` | `FIXED` | `FIXED`, `BOUNDED` or `UNLIMITED`. See [Scrolling and Size Modes](#scrolling-and-size-modes). |
247
+ | `min_lines` | `int \| None` | `None` | Minimum visible lines in `BOUNDED` mode (falls back to 1). |
248
+ | `max_lines` | `int \| None` | `None` | Maximum visible lines in `BOUNDED` mode (falls back to 10). |
249
+ | `on_change` | `ft.ControlEventHandler \| None` | `None` | Called whenever the text changes. |
250
+
251
+ All `ft.LayoutControl` properties (`width`, `height`, `expand`, `margin`, `visible`, `opacity`, ...) are supported.
252
+
253
+ ### Color format
254
+
255
+ | Format | Example | Meaning |
256
+ |--------|---------|---------|
257
+ | `#RRGGBB` | `#FF8800` | Opaque color (alpha `FF` is added automatically). |
258
+ | `#AARRGGBB` | `#80FF8800` | Color with an explicit alpha channel. |
259
+
260
+ ---
261
+
262
+ ## Line Numbers
263
+
264
+ ```python
265
+ FletCodeField(line_numbers=True) # default: gutter with line numbers
266
+ FletCodeField(line_numbers=False) # no gutter
267
+ ```
268
+
269
+ ## Enabling and Disabling Colors
270
+
271
+ `syntax_highlighting` switches coloring on or off while keeping your `rules`, so you can toggle it back at any time.
272
+
273
+ ```python
274
+ editor = FletCodeField(rules=[...], syntax_highlighting=False) # plain text
275
+
276
+ editor.syntax_highlighting = True
277
+ editor.update()
278
+ ```
279
+
280
+ ## Scrolling and Size Modes
281
+
282
+ Vertical behavior is controlled by `vertical_mode`; horizontal behavior by `wrap`.
283
+
284
+ | `vertical_mode` | Behavior |
285
+ |-----------------|----------|
286
+ | `FIXED` (`"fixed"`) | The editor fills the height it is given (`height=...`, or `expand=True` inside a `Column`) and scrolls internally. |
287
+ | `BOUNDED` (`"bounded"`) | The editor grows with the text between `min_lines` and `max_lines`, then scrolls. |
288
+ | `UNLIMITED` (`"unlimited"`) | The editor grows with the text without limit. It does not scroll by itself, so place it inside a scrollable parent. |
289
+
290
+ | `wrap` | Behavior |
291
+ |--------|----------|
292
+ | `False` | Long lines are not wrapped; the editor scrolls horizontally. |
293
+ | `True` | Long lines wrap to the next visual line. |
294
+
295
+ ```python
296
+ # Fixed height with internal scrolling
297
+ FletCodeField(height=300, vertical_mode="fixed")
298
+
299
+ # Between 5 and 15 lines, then scrolls
300
+ FletCodeField(vertical_mode="bounded", min_lines=5, max_lines=15)
301
+
302
+ # Grows without limit inside a scrollable column
303
+ ft.Column([FletCodeField(vertical_mode="unlimited", wrap=True)], scroll=ft.ScrollMode.AUTO)
304
+ ```
305
+
306
+ > `FIXED` mode needs a bounded height. Without `height` or a bounded parent, Flutter reports an unbounded-constraints error.
307
+
308
+ ---
309
+
310
+ ## Tokenizer and `Language`
311
+
312
+ `FletCodeEditor` does not use regular expressions. It scans each line with a hand-written, stateful lexer, and you describe the language with `Language`:
313
+
314
+ ```python
315
+ Language(
316
+ words=[WordGroup(["let", "if"], "#C586C0")], # exact words sharing a color
317
+ line_comments=["#", "//"],
318
+ block_comments=[BlockComment("/*", "*/")], # may span lines
319
+ strings=[StringRule('"'), StringRule('"""', multiline=True)],
320
+ sigils=[Sigil("$", "#9CDCFE"), Sigil("@", "#4EC9B0")], # $name, @user
321
+ numbers=True,
322
+ operators="+-*/%=<>!&|^~?:",
323
+ punctuation="(){}[],;.",
324
+ identifier_extra="", # e.g. "-" to allow my-command
325
+ case_insensitive=False,
326
+ colors=TokenColors(comment=..., string=..., escape=..., number=...,
327
+ function=..., identifier=..., operator=..., punctuation=...),
328
+ )
329
+ ```
330
+
331
+ | Field | Meaning |
332
+ |-------|---------|
333
+ | `words` | Groups of exact words (keywords, constants, commands), each group with one color. |
334
+ | `line_comments` | Prefixes that comment out the rest of the line. |
335
+ | `block_comments` | Start/end pairs; they can span several lines. |
336
+ | `strings` | `StringRule(open, close=None, escape="\\", multiline=False)`. The escape character and the character after it are colored with `colors.escape`. An unterminated single-line string ends at the end of the line. |
337
+ | `sigils` | A prefix followed by a name, colored as one token (`$var`, `@user`). |
338
+ | `numbers` | Integers, decimals, exponents, `_` separators and `0x` / `0b` / `0o` literals. |
339
+ | `operators` / `punctuation` | Character sets. Runs of operator characters form one token (`==`, `&&`). |
340
+ | `identifier_extra` | Extra characters allowed inside names. Letters from any script (including Arabic) are accepted. |
341
+ | `colors` | Colors for the built-in kinds. `function` colors a name directly followed by `(`. `None` keeps the default text color. |
342
+
343
+ **Order of matching** at each position: whitespace, line comments, block comments, strings, sigils, numbers, words/identifiers, operators, punctuation. Within a kind, the longest opener wins (`"""` before `"`). A word listed in `words` always wins over the `function` or `identifier` colors.
344
+
345
+ Pass the result with `language=LANG.to_json()`. Changing `language` at runtime repaints the editor; the editor view may reset its scroll position when you do.
346
+
347
+ ---
348
+
349
+ ## Visible size
350
+
351
+ `width_level` and `height_level` set the **visible box** of the editor (not its inner content, which scrolls). Each is a number from 1 to 30:
352
+
353
+ ```text
354
+ size = level / 30 x available space (minimum 48 px)
355
+ ```
356
+
357
+ | Level | Result |
358
+ |-------|--------|
359
+ | 30 | Covers all the available space |
360
+ | 13 (default) | About 43% of the available space |
361
+ | 1 | A small box (about 3%, at least 48 px) |
362
+
363
+ The two levels are independent, so you can enlarge only the width or only the height. The available space is the parent's size (or the screen, when the parent is unbounded).
364
+
365
+ ```python
366
+ FletCodeEditor(width_level=20, height_level=8)
367
+ ```
368
+
369
+ The levels are ignored when you set `width`, `height` or `expand` yourself, and `height_level` is ignored in `bounded`/`unlimited` modes (the height then follows the number of lines).
370
+
371
+ ## Error underlines
372
+
373
+ Errors are **optional and user-defined**. Without `errors`, nothing is checked. The highlighter colors text as usual, and the checks only add a red underline under the faulty part:
374
+
375
+ ```python
376
+ from flet_code_editor_dsl import ErrorRules, BracketPair
377
+
378
+ Language(
379
+ ...,
380
+ errors=ErrorRules(
381
+ unterminated_strings=True, # "text without a closing quote
382
+ brackets=[BracketPair("(", ")"), BracketPair("[", "]")], # unmatched in a line
383
+ unknown_words=True, # names not in `words` / `allowed_words`
384
+ allowed_words=["print", "len"],
385
+ forbidden_words=["goto"], # always flagged
386
+ color="#F44747",
387
+ wavy=True, # False = straight underline
388
+ ),
389
+ )
390
+ ```
391
+
392
+ All checks work **inside one line**, so brackets must balance within the same line (do not enable `brackets` for `{` `}` blocks that span lines). Hover messages are not shown yet; only the underline. Turn the underlines off at runtime with `show_errors=False`.
393
+
394
+ ## Code folding
395
+
396
+ Foldable regions are defined by you in `Language`, which suits small or unusual DSLs:
397
+
398
+ ```python
399
+ from flet_code_editor_dsl import FoldRule
400
+
401
+ Language(
402
+ ...,
403
+ folds=[
404
+ FoldRule("class", "end"), # from a line starting with `class` to a line starting with `end`
405
+ FoldRule("on", length=5), # the 5 lines after a line starting with `on`
406
+ FoldRule("{", "}"),
407
+ ],
408
+ fold_by_indent=False, # True: also fold by indentation
409
+ )
410
+ ```
411
+
412
+ - A marker matches at the beginning of a line (after indentation). Word markers match whole words only (`end` does not match `endpoint`).
413
+ - Start/end pairs can be nested.
414
+ - `length` folds a fixed number of lines after the start line.
415
+ - Without `folds` and `fold_by_indent`, no fold markers are shown. Turn folding off at runtime with `code_folding=False`.
416
+ - Whether the closing line stays visible when a region is folded is decided by `re_editor`.
417
+
418
+ ---
419
+
420
+ ## Performance
421
+
422
+ **`FletCodeEditor`** does not use a `TextField`. Each line is laid out and painted on its own and only the visible lines are built, so editing one line does not re-render the whole document. This is the recommended control for large scripts.
423
+
424
+ The rest of this section is about **`FletCodeField`**, which is a `TextField` that Flutter paints as a single block. By default (`incremental_highlighting=True`) the text is highlighted **line by line** and the result of every line is cached by its content. When you type, only the edited line is re-highlighted; all unchanged lines reuse their cached colors. Rules are also compiled only when `rules` or `syntax_highlighting` change, not on every keystroke.
425
+
426
+ Consequences:
427
+
428
+ - Each rule is matched **within a single line**. A pattern that spans lines (for example a `/* ... */` block comment) will not match. Set `incremental_highlighting=False` to match against the whole text, at the cost of re-highlighting everything on each change.
429
+ - When matches from several rules overlap, the **earlier rule in the list wins** for each character.
430
+ - Flutter still lays out and paints the editor as a single text block; the saving comes from not re-running the regexes and not rebuilding spans for unchanged lines.
431
+
432
+ ---
433
+
434
+ ## Controlling the Editor at Runtime
435
+
436
+ ### Reading the text
437
+
438
+ ```python
439
+ def on_change(e):
440
+ print("Current text:", e.data)
441
+
442
+ editor = FletCodeField(on_change=on_change)
443
+ ```
444
+
445
+ ### Setting the text or changing options
446
+
447
+ ```python
448
+ editor.value = "new content"
449
+ editor.line_numbers = False
450
+ editor.syntax_highlighting = False
451
+ editor.vertical_mode = "bounded"
452
+ editor.max_lines = 20
453
+ editor.update()
454
+ ```
455
+
456
+ ### Updating rules
457
+
458
+ ```python
459
+ editor.rules = [HighlightRule(pattern=r"\bTODO\b", color="#FFCC00")]
460
+ editor.update()
461
+ ```
462
+
463
+ ---
464
+
465
+ ## Complete Example
466
+
467
+ ```python
468
+ import flet as ft
469
+ from flet_code_editor_dsl import FletCodeField, HighlightRule
470
+
471
+ SAMPLE = '''\
472
+ # Example program
473
+ let total = (price * 3) + 12
474
+ if (total > 100) {
475
+ print("expensive")
476
+ } else {
477
+ print("cheap")
478
+ }
479
+ '''
480
+
481
+ RULES = [
482
+ HighlightRule(r"\b(let|if|else)\b", "#C586C0"),
483
+ HighlightRule(r"\b\d+(?:\.\d+)?\b", "#B5CEA8"),
484
+ HighlightRule(r'"[^"\n]*"', "#CE9178"),
485
+ HighlightRule(r"#.*", "#6A9955"),
486
+ ]
487
+
488
+
489
+ def main(page: ft.Page):
490
+ page.title = "flet-code-editor-dsl demo"
491
+ page.theme_mode = ft.ThemeMode.DARK
492
+
493
+ def toggle(prop):
494
+ def handler(e):
495
+ setattr(editor, prop, e.control.value)
496
+ editor.update()
497
+ return handler
498
+
499
+ def on_mode(e):
500
+ editor.vertical_mode = e.control.value
501
+ editor.update()
502
+
503
+ editor = FletCodeField(value=SAMPLE, rules=RULES, height=300)
504
+
505
+ page.add(
506
+ ft.Row([
507
+ ft.Switch(label="Line numbers", value=True, on_change=toggle("line_numbers")),
508
+ ft.Switch(label="Colors", value=True, on_change=toggle("syntax_highlighting")),
509
+ ft.Switch(label="Wrap", value=False, on_change=toggle("wrap")),
510
+ ]),
511
+ editor,
512
+ )
513
+
514
+
515
+ ft.run(main)
516
+ ```
517
+
518
+ ---
519
+
520
+ ## Important: First Build Required
521
+
522
+ This package is not pure Python. Its editor widget is written in Dart/Flutter and must be **compiled into the Flet client** before the control can be displayed.
523
+
524
+ 1. **Declare the library in your app's `pyproject.toml`** under `[project] dependencies`.
525
+ 2. **Build your app once** with `flet build <platform>`.
526
+ 3. **Run normally afterwards.** After the first build the control also works with `flet run`, as long as `pyproject.toml` still declares the library.
527
+
528
+ | Change | Rebuild needed? |
529
+ |--------|-----------------|
530
+ | Python code, `value`, `rules`, `line_numbers`, `syntax_highlighting`, `wrap`, `vertical_mode`, layout | No |
531
+ | Updating `flet-code-editor-dsl` to a version whose Dart code changed | Yes |
532
+ | Upgrading Flet to a new major/minor version | Yes |
533
+ | Adding or removing other Flet extensions | Yes |
534
+
535
+ ---
536
+
537
+ ## Building Your App
538
+
539
+ ```bash
540
+ # Desktop
541
+ flet build windows
542
+ flet build macos
543
+ flet build linux
544
+
545
+ # Mobile
546
+ flet build apk
547
+ flet build ipa
548
+
549
+ # Web
550
+ flet build web
551
+ ```
552
+
553
+ ---
554
+
555
+ ## Project Structure
556
+
557
+ ```text
558
+ flet-code-editor-dsl/
559
+ ├── pyproject.toml
560
+ ├── README.md
561
+ └── src/
562
+ ├── flet_code_editor_dsl/
563
+ │ ├── __init__.py
564
+ │ ├── flet_code_editor_dsl.py # FletCodeEditor, FletCodeField, HighlightRule, VerticalMode
565
+ │ └── language.py # Language, WordGroup, StringRule, ...
566
+ └── flutter/
567
+ └── flet_code_editor_dsl/
568
+ ├── pubspec.yaml
569
+ └── lib/
570
+ ├── flet_code_editor_dsl.dart
571
+ └── src/
572
+ ├── extension.dart # registers both controls
573
+ ├── code_editor.dart # FletCodeEditor (re_editor)
574
+ ├── tokenizer.dart # lexer + per-line cache
575
+ └── flet_code_editor_dsl.dart # FletCodeField (TextField)
576
+ ```
577
+
578
+ Generated files such as `*.egg-info/`, `__pycache__/`, `pubspec.lock`, `.dart_tool/`, `package_config.json` and `package_graph.json` should not be committed; add them to `.gitignore`.
579
+
580
+ ---
581
+
582
+ ## Known Limitations
583
+
584
+ - **Single-line rules by default.** See [Performance](#performance).
585
+ - **Regex highlighting only.** There is no language parsing, so delimiters inside strings or comments are not treated specially.
586
+ - **Dart regex semantics.** Patterns must be valid for Dart's `RegExp`; avoid Python-only syntax such as `(?P<name>...)`.
587
+ - **`FletCodeField` font.** It uses a fixed style (`Consolas`, size 12). Use `FletCodeEditor` to change font and size.
588
+ - **`FletCodeEditor` and multi-line constructs.** Opening a block comment or multi-line string on one line changes the state of the lines below. Those lines are recolored when the editor repaints them; if re_editor keeps already-painted lines as they are, they may keep their old colors until they are redrawn (scrolling, editing them). Test this in your app.
589
+ - **Errors and folding.** Error checks work per line; fold markers ignore strings and comments (a `{` inside a string still counts).
590
+ - **`FletCodeEditor` input.** It implements its own text input handling; test Arabic (RTL) input and mobile keyboards in your app.
591
+ - **First build required.** The control is not available in the stock Flet client.
592
+
593
+ ## Troubleshooting
594
+
595
+ | Symptom | Likely cause and fix |
596
+ |---------|----------------------|
597
+ | The editor does not appear, or an "unknown control" error is shown | The library is not declared in `pyproject.toml`, or the app was not rebuilt. Add it to `dependencies` and run `flet build`. |
598
+ | Unbounded height/constraints error | `vertical_mode="fixed"` without a bounded height. Set `height`, use `expand=True` inside a `Column`, or switch to `bounded`/`unlimited`. |
599
+ | A rule has no effect | The regex may be invalid for Dart, or `syntax_highlighting` is `False`. |
600
+ | Changes made from Python are not visible | Call `editor.update()` after modifying a property. |
601
+ | Compile error in `code_editor.dart` or `tokenizer.dart` | `re_editor` APIs change between versions (`CodeLineSpanBuilder`, `CodeLineEditingController`); check the version in `pubspec.yaml` and the error message. |
602
+ | No error underlines or fold markers | `Language.errors` / `Language.folds` is not set, or `show_errors` / `code_folding` is `False`. |
603
+ | `FletCodeEditor` shows no colors | `language` is empty or not valid JSON, or `syntax_highlighting=False`. Pass `Language(...).to_json()`. |
604
+ | Compile error in `CodeField` after upgrading | Parameter names depend on the `code_text_field` version; check `pubspec.yaml`. |
605
+
606
+ ## Contributing
607
+
608
+ 1. Fork the repository.
609
+ 2. Create a feature branch: `git checkout -b feature/my-feature`.
610
+ 3. Commit your changes with clear messages.
611
+ 4. Push the branch and open a Pull Request.
612
+
613
+ Please open an issue first for larger changes.
614
+
615
+ ## License
616
+
617
+ Licensed under the **Apache License, Version 2.0**. See the `LICENSE` file for the full text.
618
+
619
+ ```text
620
+ Copyright 2026 obgwew
621
+
622
+ Licensed under the Apache License, Version 2.0 (the "License");
623
+ you may not use this file except in compliance with the License.
624
+ You may obtain a copy of the License at
625
+
626
+ http://www.apache.org/licenses/LICENSE-2.0
627
+
628
+ Unless required by applicable law or agreed to in writing, software
629
+ distributed under the License is distributed on an "AS IS" BASIS,
630
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
631
+ See the License for the specific language governing permissions and
632
+ limitations under the License.
633
+ ```