zuraffa_code_editor 0.2.0
zuraffa_code_editor: ^0.2.0 copied to clipboard
A customizable Flutter code field supporting syntax highlighting, code folding with visible closing lines, autocompletion and themes.
Changelog #
0.2.0 #
Added #
-
Per-line background colour:
CodeController.lineBackgroundBuilder, a nullableColor? Function(int lineIndex)?, tints the background of individual lines — the mechanism behind a current-line highlight, search-hit tints and error-line tints (issue #86, upstreamakvelon/flutter-code-editor#239). The builder is called once per rendered line with the line's 0-based index in the full, unfolded buffer — the same coordinate space asIssue.lineandFoldableBlock.firstLine— and returningnullleaves that line untinted. The tint covers the whole width of the line's row, including the stretch past its last character, and sits behind the editor's search-match highlighting, which still wins on a matched run. Lines inside a folded block or a hidden section are not rendered, so the builder is not asked about them, and the tint tracks the same per-row heights the gutter uses, so it lands on the right row through wraps and folds. Left null the field renders exactly as before: the layer is not in the tree at all. -
A multi-line Scala selective import folds as one imports block (issue #117, upstream
akvelon/flutter-code-editor#78).import users.{ User, UserPreferences }spread over several lines used to produce two foldable blocks — animportsblock that stopped at theimportline and a straybracesblock over the member list — so folding the import left the members on screen. A brace opened on an import line is now part of the import statement: it is absorbed instead of reported, and every line it spans counts as an import line, so the whole statement folds once. Go's parenthesized import group (import ( ... )) keeps folding as its own block, and a member list holding its own pairs (Map("k" -> "v")) produces no extra block. This also reachessetFoundPossibleImportand thepossibleImportbranch ofsubmitCurrentLine, previously dead code, so four line exemptions were removed from the coverage gate rather than renumbered. -
Batch folding API (issue #116, upstream
akvelon/flutter-code-editor#106).CodeControllerfolds blocks one at a time, so folding many meant a loop offoldAtcalls — each rebuilding theCode, assigningsuper.valueand notifying listeners, producing N intermediate states the user never sees (a 300-import file paid for 300 value assignments before settling).CodeController.foldBlocks( Iterable<FoldableBlock>)folds the given blocks in one pass: one newCode, one value assignment, one notification, regardless of count. Blocks that are already folded, or that the current code no longer holds, are skipped.foldImports()andfoldOutsideSections()now delegate to it and keep their exact behaviour; the upstream TODOs are removed.test/src/code_field/code_controller_fold_blocks_test.dartpins the loop-equivalent result, the single notification, the skips, and the selection-recovery parity. -
The gutter error popup is replaceable (issue #81, upstream
akvelon/flutter-code-editor#216). The ask named both a custom glyph perIssueType(delivered earlier this cycle asGutterStyle.errorIcon/warningIcon/infoIcon) and "provide my ownGutterErrorWidget" — the half that was still open. The gutter constructedGutterErrorWidgetitself with no injection point, so the popup's layout and behaviour were frozen for every consumer.GutterStylenow accepts a nullableerrorPopupBuilder, aWidget Function(Issue, TextStyle, BoxDecoration?)?that the gutter calls per issue with the already-resolved popup text style and decoration; leaving it null renders exactly theGutterErrorWidgetthe gutter has always constructed.test/src/gutter/gutter_error_widget_builder_test.dartpins both halves, the per-issue call, the null-text-style throw, and thecopyWithrelay. -
The Insert key toggles insert/overtype mode (issue #83, upstream
akvelon/flutter-code-editor#200, which points at the still-openflutter/flutter#122673). Flutter'sDefaultTextEditingShortcutsbinds only Ctrl-Insert (copy) and Shift-Insert (paste), so bare Insert falls through to the editor's own bindings and is now the overtype toggle. While overtype is on, a plain one-character keystroke at a collapsed caret replaces the character after the caret instead of inserting in front of it; the caret at the end of the text, a deletion, a paste, a multi-character selection, a keystroke a modifier transforms (auto-pairing) and a read-only field all keep inserting as before. The mode is a publicCodeController.isOvertypeMode, defaults to insert, and the key binding lives in_shortcutsIgnoredWhileComposingso a keystroke mid-IME-composition still belongs to the IME.test/src/code_field/insert_overtype_test.dartpins both halves, and Ctrl-Insert keeps copying. -
The cost of an unhighlighted editor is pinned by a test (issue #111 and issue #93, upstream
akvelon/flutter-code-editor#246).CodeControllercallshighlight.parseon every edit whether or not a language is configured, and both reports read that as the cause of a ~20 s stall on a 4800-line document with no language selected. Measured, it is not:highlight0.7.0 resolves an empty or unrecognised language name toplaintext— an empty mode with no keywords, no sub-modes and no auto-detection — so the highlight step costs 10–18 µs against 78.9–96.5 ms withjava, one keystroke medians 13–23 ms against 119.9–129.5 ms, and a paste of the whole document 16–23 ms against 124.8–129.2 ms. Neither path approaches the reported stall, and skipping the parse would remove at most ~18 µs from a ~14 ms keystroke, so no code changed.test/src/code_field/no_language_keystroke_test.dartpins the conclusion as ratios between the two paths plus absolute bounds of 100 ms per keystroke and 1 s per paste, and a fault injection that makes the no-language fallback resolve to a real language (parse 80 532 µs against the with-language 76 120 µs, keystroke 109 045 µs, paste ratio 0.98) reddens it — so a future change that makes an unhighlighted editor expensive fails the suite instead of reaching users.
Changed #
-
The gutter stays on the code line grid at any code font size (issue #107, upstream
akvelon/flutter-code-editor#312and#215). The gutter lays its rows out in aTable, whose row height is the tallest cell in it. The number is one code line tall, but the two fixed icon columns always rendered their glyph at 16px, so whenever the code's line box was shorter than 16 the icon set the row's height instead: the row pitch exceeded the code line height by the overshoot and the gutter separated from the code by that much again on every line below. The reported configurations never reach the case —height: 1.5atfontSize: 16and18gives a 24-27px line box, and so does thefontSize: 13, height: 1.6the existing grid pins use — which is why those pins pass while the drift is real. Measured on master atheight: 1.0, the pitch ran 10.500 against a 9px line box atfontSize: 9(10.5px of drift by the last row) and 13.643 against 13px atfontSize: 13(4.5px). Both icon glyphs are now clamped to the code line height, which is a no-op above a 16px line box, so every rendering that already fits is byte-identical — only the column widths were fixed before and only the glyph now shrinks.test/src/gutter/gutter_icon_row_height_test.dartpins the invariant across the whole sub-16 range, and all three fault-detecting tests are red without the fix (Expected <30.5> Actual <32.0>, the pitch of two stacked 16px glyph boxes). -
The code area's horizontal scroll is pinned on both of its paths (issue #85, upstream
akvelon/flutter-code-editor#280, reported as "horizontal scrolling does not work on iOS, works on Android"). Measured, the platform claim is the wrong way round: on a 240 px drag over the code text the code area scrolls 0 → 201 px under an iOS platform override and stays at 0 under an android one. The split is Flutter's, not this package's —TextFieldregisters aTapAndHorizontalDragGestureRecognizerwhoseeagerVictoryOnDragisdefaultTargetPlatform != TargetPlatform.iOS, and being deeper in the hit-test tree than the code area'sSingleChildScrollViewit enters the gesture arena first and, on non-Apple platforms, claims the drag for text selection. Nothing inlib/changed and no behaviour is new: the caret reveal already works on every platform and is pinned with it, and the drag split is now pinned so a Flutter upgrade that flipseagerVictoryOnDragfails the suite instead of silently changing who wins the drag. The correct follow-up — an interactive horizontalScrollbar, whose thumb drag uses its own recognizer deeper than theTextField's and so wins the same arena without touching selection — is recorded in.specify/bugs/ios-horizontal-scroll-85/assessment.md. -
A single tap commits an autocomplete suggestion, and the popup stays inside the window (issue #84, upstream
akvelon/flutter-code-editor#205). On mobile the suggestion popup needed a double tap and was cut off, becausePopup's row had the commit on itsonDoubleTap— so the gesture arena held the first tap back until the double tap timeout — and the popup was placed against the editor's render box while it actually renders in the full-screen overlay: the horizontal clamp subtracted an unexplained 100 px, which put the popup off the left edge of any editor narrower than 400 px, and the vertical flip was refused outright when the editor was shorter than the popup's maximum height. A tap now commits the word on its row exactly as Enter does, the popup is clamped into the window it is measured against — the real window, whichCodeFieldnow hands it instead of its own box — and the flip is decided by the height the suggestions actually need, capped at the popup's maximum height. -
A visible section renders flush with its contents (issue #35, upstream
akvelon/flutter-code-editor#109). A section whose[START name]and[END name]tags sit on lines of their own used to present a blank line before and after its contents — the shell of each tag line, the whitespace before the comment and the line break after it — and the gutter carried a number for both. Those two lines are now cut whole, so the presented section starts and ends with its own code. A tag that shares its line with code (class A {// [START s]) keeps its line, because the code before the comment belongs to the section; a section with no start tag cuts its end tag alone; and a document with no visible section is byte-for-byte unchanged.
Added #
-
The gutter's error icon is overridable, by issue type (issue #81, upstream
akvelon/flutter-code-editor#216). The gutter rendered one hardcoded redIcons.cancelfor every issue and offered no way to change it.GutterStylenow carrieserrorIcon,warningIconandinfoIcon— nullableIconDatas resolved per issue throughGutterStyle.issueIcon(IssueType)— andGutterErrorWidgettakes an optionaliconwidget for a host that mounts it directly. A style that sets none of them renders exactly the icon it always did: every type still falls back todefaultIssueIcon, which is the glyph theerrorIconconstant is now built from so the two cannot drift apart. Only the glyph is overridable — the icon is drawn at the issue column's own width, so its size stays part of the row geometry. -
The gutter error popup's decoration is overridable (issue #82, upstream
akvelon/flutter-code-editor#217). The popup's container chrome was derived inline from the popup text style'sbackgroundColorandcolor, which meant those twoTextStylefields were load-bearing for something that is not text styling and there was no way to set a shape or a border.GutterStyle.errorPopupDecorationnow supplies theBoxDecorationoutright, andGutterErrorWidget.popupDecorationdoes the same for a host that mounts the widget directly. Leaving it null keeps the existing derivation exactly, and the popup's content — message, url, suggestion and divider — is untouched either way. ApopupBuilderthat would replace the whole overlay is deliberately not in this change. -
Tab, Shift+Tab and Enter are dispatched through Flutter's
Shortcuts/Actionsmachinery, and the raw key path is now pinned as owning none of them (issue #29):CodeField'sFocusableActionDetectorsends Tab toTabKeyIntent, Shift+Tab toOutdentIntent, Enter toEnterKeyIntent, Ctrl+/ toCommentUncommentIntentand Ctrl+F toSearchIntent, andCodeController.actionsanswers each with its action;CodeController.onKeyhandles only Ctrl+F and the autocomplete popup's arrows and returnsignoredfor everything else. The negative is whattest/src/code_field/tab_through_shortcuts_test.dartpins — a regression that moved Tab back into a raw handler would leave every behavioural test green, because the observable effect of a Tab is identical either way. -
Outdent on Backspace, and a modifier hook that can carry it (issue #21): pressing Backspace inside a line's leading whitespace now removes one indent level instead of one space, and the modifier that does it is shipped in
CodeController.defaultCodeModifiers. Supplying your ownmodifierslist withoutOutdentModifierkeeps the plain one-character delete.This needed a new hook, because the one the reporter reached for cannot work: a
CodeModifieris keyed on the character that was typed, and a backspace removes a character rather than inserting one, so no key was ever produced and no modifier — on any character — could fire.CodeModifiernow takes an optionaldeletesChar, andCodeControllerkeeps a second map for those modifiers, keyed on the character that was removed. The dispatch is guarded by a single-character collapsed deletion, and a backspace over real code is still a one-character delete: the modifier declines any line whose leading run is not all spaces. A deletion cannot cross a read-only range or a folded block either — the modifier's result is folded into the value before those guards run. -
Web is a genuinely supported platform (issue #23):
autotrie, the one dependency in the graph that reported noplatform:web, is replaced by an in-house engine atlib/src/autocomplete/auto_complete.dart.autotrie's only entry point re-exportssrc/autotrie_base.dart, whose first line isimport 'dart:io', anddart:iohas no web implementation — that single import is why pub.flutter-io.cn listedandroid, ios, linux, macos, windowsand notweb, and why a web build of anything reaching the autocompleter failed to compile. The two methods that need it,AutoComplete.fromFileandAutoComplete.persist, are file-persistence helpers with no caller in this package, so the port carries the in-memory surface only andhiveleaves the graph withautotrie. Pinned bytest/src/autocomplete/auto_complete_test.dart(36 tests); the port corrects twoautotriedefects that cannot be fixed upstream, both unreachable fromAutocompleter:containsnow reports entered words rather than surviving prefix paths, anddeletenow clears the hit of the word it removes instead of leaving it suggested when a longer word shares its prefix.pubspec.yamlnow declares all six platforms explicitly, so the support cannot silently regress when a dependency that reachesdart:iois added. -
Right-to-left text direction support (issue #14):
CodeFieldgained atextDirectionparameter. Left null it resolves the ambientDirectionality, so an Arabic locale already renders right to left with no configuration — text, caret movement, the gutter (which flips to the right edge with the text) and the platform's bidi reordering of mixed Latin/Arabic runs. Set it to force a direction for a right-to-left snippet inside a left-to-right app.The defect this fixes was in the measurement, not the rendering: every
TextPainterthe field builds hardcodedTextDirection.ltr, so in an RTL field the autocomplete popup was positioned where the caret would be if the text ran left to right — the far side of the editor. The resolved direction now reaches theTextFieldand the caret painter that positions the popup. The row-height painters stayltrdeliberately: a paragraph's direction moves text horizontally, never vertically. Pinned bytest/src/code_field/text_direction_test.dart. -
HTML is reachable and documented as a first-class language (issue #40):
highlightships nohtmlmode — HTML is one of the aliases of itsxmlmode, and that mode already classifies the doctype, element names, attributes, attribute values, comments and entity references of a real document. The README's Languages › Syntax Highlighting section now says so instead of leaving the reader to conclude from a missingpackage:highlight/languages/html.dartthat HTML is unsupported, and the demo's language picker gainedhtmlandxmlentries so the path is demonstrable. Pinned bytest/src/highlight/html_language_test.dart. In practice,<script>and<style>bodies stay markup rather than being re-parsed as JavaScript or CSS inhighlight0.7.0: the mode does declaresubLanguageentries for both, but their<script/<styletag modes never match, so those entries never engage (<?php ?>bodies are parsed, as PHP). If a futurehighlightrelease fixes the matching, the bodies would start being re-parsed. -
tool/coverage_gate.pygained a line-level exemption mechanism.EXEMPT_LINESremoves individual unreachable lines from both the numerator and the denominator of the file they sit in — the same contractEXEMPT_FILEShas always had for whole files — and a stale-entry check warns when a listed line no longer matches anything inlcov.info. Every entry carries its proof in the comment above it, and the list is meant to stay short: if an exempted line becomes reachable, the test that reaches it is added and the entry dropped. -
22 new test files closing the reachable coverage gaps left over from the previous rounds: the autocompleter's blacklist and unknown-keyword paths, all six
Actiondelegates of the code field, the tab modifier,TextSelectionextension, named sections,AbstractAnalyzer.dispose, the single-line comment parser,KeyEventExtension, the fallback foldable block parser and the parser factory, the gutter fold toggles, the hidden range sort tie-break,SearchNavigationState.copyWith, the search bar's close icon, the CRLF row measurement of a wrapped field, and — new in this round — the gestural surface of the completion popup (a tap moves the selection and returns focus, a double tap commits the word) plus the recomputation of the search navigation index after a mid-search edit. -
A committed keystroke-latency benchmark.
test/src/code_field/edit_latency_test.dartmeasures the median keystroke at 200, 1000 and 3000 lines, prints the per-line latency, and fails on a super-linear regression, so the next report of "sluggish past a few hundred rows" starts from numbers instead of a sentence. -
CodeFieldexposescontextMenuBuilder(issue #12): an app can now replace the selection toolbar with its own actions (search, format, go to line) by passing anEditableTextContextMenuBuilder. When no builder is given, the field forwardsTextField's own default instead of an explicitnull, so the platform menu — Copy, Select All — survives unchanged for every existingCodeField.
Fixed #
-
A deleted folded block no longer folds its surviving identical twin (issue #108, upstream
akvelon/flutter-code-editor#83). Folding one of two textually identical blocks and then deleting the folded one folded the survivor:FoldableBlockMatcherdecides a fold survived an edit by comparing the fold's hidden content lines, and identical blocks have identical content, so the deleted block's fold re-attached itself to the twin. The edit's changed old-line range now travels with the re-match, and a fold whose first hidden line overlaps it is dropped with the block that was edited away — while the block's own first line stays exempt, so editing the visible line still keeps the fold. Pinned bytest/src/folding/fold_survives_identical_blocks_test.dart(4 tests) and the flipped upstream-recorded scenario intest/src/code_field/code_controller_folding_editing_test.dart. -
A
#no longer costs a Java file its highlighting (issue #104, upstreamakvelon/flutter-code-editor#42).highlight0.7.0'sjavamode declaresillegal: "<\\/|#", and_parsetreats an illegal lexeme as a silent bail — it throws, catches the throw itself and hands back the entire document as one unclassified node, so a single#outside a comment or a string literal (or a stray</) erased every class in the file. A triple-quoted string literal reached the same end by another route: with no triple-quote mode, the third"of"""opens an ordinary string whose content crosses a newline, and an ordinaryjavastring declares a newline illegal.CodeControllernow registers a repaired copy of thejavamode with the highlighter — theillegalregex dropped, a"""…"""string mode listed ahead of the single-quoted ones. It has to be a copy rather than the shipped mode with a field cleared, becauseModecaches the regexes compiled fromillegalandcontainson the first parse, so a cleared field leaves the stale regexes in place and the defect alive. Only the highlight parse sees the copy:controller.languageis still the mode the caller supplied, and every other language is registered exactly as it was given. Measured on master, bothpublic class A {\n}\n#\nandString s = """\nmultiline\n""";came back asResult(relevance: 0, nodes: [one node]); after the repair the first classifiespublic,class,titleand the second classifies the literal as onestringrun spanning all three lines. A raw newline inside an ordinary"..."still bails — a Java syntax error rather than a defect, and the other side of that trade has no clear winner — whiletest/src/highlight/java_language_test.dartpins the repaired classification and the unchanged one side by side. -
Folding and unfolding no longer throw away the selection they cover (issue #106, upstream
akvelon/flutter-code-editor#189, reported as "Fold-unfold deselects text"). A selection lives in the visible text, so the part of a selection that a folded block hides can only be shown truncated, clamped to the fold marker — which is whatCodeController.foldAt/unfoldAtdid, via_getValueWithCodelifting the selection into the full text and projecting it straight back. The lifted selection was discarded, so unfolding had nothing to restore from and recovered the truncated one: selectingreturn;and its brace and folding that block left the selection at[42, 45]instead of the original[43, 58], and it never came back._getValueWithCodenow keeps the lifted selection when cutting it through the new ranges and lifting it back does not return it — an exact predicate for "the fold truncated the selection" — and drops it the moment anything else re-establishes the selection: a change from the field,applyHistoryRecord(whose record carries a selection of its own), a whole-document replacement throughfullText, or a line-modifying edit, whose directsuper.valuewrite bypasses the setter. The report's own claim that a selection untouched by the fold is dropped does not reproduce — a drag selection overvoid method2()withvoid method1()folded tracks[58, 97]→[45, 84]and unfolds back to[58, 97], and that behaviour is pinned as well so the fix cannot resurrect a selection the fold never hid.test/src/code_field/fold_unfold_keeps_selection_test.dartpins the round trip for a covered selection, a covered caret, a straddling one, an untouched one, a selection the user moves while the block is folded, an unrelated unfold, a keystroke after the round trip, and the two bypass paths — afullTextreplacement and an indent while the block is folded, each red without the clear beside it. -
XML documents are foldable again (issue #103, upstream
akvelon/flutter-code-editor#311, reported as "HTML/XML highlighting is broken"). The highlighting half of the report does not reproduce:highlight.parse(src, language: 'xml')classifies a document intometa,commentandtagnodes carryingname,attrandstring, and drivingSpanBuilderwithvsThemeover those nodes paints them in six distinct colours, so the widget path is the same path and cannot differ. What does reproduce is the other half, "the document structure is lost":CodeController(text: ..., language: xml).code.foldableBlocksis empty, where java yields two brace blocks onclass A { void m() { } }.FoldableBlockParserFactoryspecial-cased python, java and yaml and sent everything else toHighlightFoldableBlockParser, which opens blocks on(,[and{only — and markup nests on<tag>/</tag>, so no bracket ever appears and no block is ever opened. Markup now has its own parser, driven by the highlight nodes rather than a text scan so a<inside a comment or an attribute string can never look like an element: nested elements produce nestedFoldableBlockType.markupblocks, self-closing tags (<br/>,<img src="x" />) and the<?xml ?>declaration produce none, a<a>text</a>pair on one line produces none becauseendBlockalready drops same-line blocks, and an unclosed element or a stray closing tag is reported as anInvalidFoldableBlockthe way an unclosed brace is. The Android layout intest/src/folding/parsers/markup_parser_test.dartnow folds itsLinearLayoutandFrameLayoutinstead of neither. -
Deleting a folded block's closer no longer destroys the block's content (issue #98, upstream
akvelon/flutter-code-editor#176). Backspacing the}of a folded block — the first visible character after the block's hidden range — recovered the replaced range with downstream affinity, which expanded the range into the hidden content and spliced it out of the document along with the brace. An edit that begins where a folded block's hidden range collapses now recovers its start after the content, so only the removed characters are deleted: the brace goes, the body survives, and the block releases because there is no closer left to fold. The guard is keyed on what the edit removes, not on the direction the visible text's length moved, so replacing the closer with longer text (typing} else {over it) is protected too — a pure insertion at that point keeps the documented downstream recovery. Edits at any other hidden range — a named section's marker comment — are unchanged. -
Folding a two-line block no longer deletes the line that starts the next one (issue #99, upstream
akvelon/flutter-code-editor#213). When a block's closing line was also the opening line of the next block and only one content line separated them (if (a &&/b) {), the guard that gives the closer a row of its own had nothing to work with and the fold hid the shared line whole — taking the following block's first line, its line number and its fold toggle off the screen. The range now ends at the shared line's start instead, gluing it onto the opener's row: the same shape a leading closer (}void main(){) already folds to, and the most a two-line fold can offer, since aHiddenRangecannot be empty. Multi-line touching blocks keep their closer's own row, and a leading-closer two-line shape folds exactly as before. -
dart analyzewarnings in the new tests — unused imports removed. -
A keystroke no longer rebuilds the visible highlight or re-indexes the whole document (issue #39, upstream
akvelon/flutter-code-editor#255).Code.foldedAscut and re-split the entire highlight tree a second time per keystroke even when it folded nothing,HiddenRanges.cutHighlightedrebuilt a structurally identical copy of that tree whenever nothing was hidden, andAutocompleter.setTextre-entered every word of the document into the autotrie. Each now does only the work the edit requires. Median keystroke on a 3000-line document measured 88.7 ms before and 77.9 ms after (−12%; −34% at 200 lines). Growth stays linear in rows — the remaining ceiling ishighlight.parseof the whole document, which has no incremental API here and is left to issue #22.
Changed #
-
The keystroke cost is measured, decomposed and pinned against super-linear growth (issue #22, upstream
akvelon/flutter-code-editor#264). One single-character keystroke in a 3000-line document costs ~84 ms underflutter test, and it ishighlight.parseof the whole document (~62%, 52–59 ms), theCode(...)construction (~18%, 15–18 ms),Autocompleter.setText(~7%, 5.6–6.7 ms) and the edit diff, tab replacement and selection mapping (~13%). Each of the three big terms is whole-document work, and none can be trimmed without an architectural change: thehighlightpackage lexes from offset 0 on every call with no incremental API,Codeis immutable and rebuilt wholesale, and the autocompleter's remaining_splitWordsscans the full text. The issue asks for lazy loading; upstream declined it for the same reason ("you would have to mostly rewrite the entire text editor"), and the measurement here confirms it, so the deliverable is the number and a guard, not a rewrite. The guard istest/src/code_field/edit_latency_test.dart: the per-line keystroke cost at 3000 lines within 2× the per-line cost at 200 lines, tightened from 3× after measuring the unmodified ratio at 1.01–1.25 over five runs. An injected O(rows²) pass inCodeLinesBuilder.textToCodeLines(+29 µs/line at 3000 lines) reddens it at 2.13×; reverting either of #39's O(n) trims does not, which is the honest limit of a linearity bound and is recorded in the spec. -
The coverage gate ratchets to 100%. The measured value is
100.00% (3089/3089 lines over 102 files); 13 lines are exempted individually as provably unreachable (dead branches, defensive guards, theItemScrollControllerassignment inPopupthat can never observe an attached controller, and the fold-toggle lambda that the second loop ofGutter._fillFoldTogglesalways overwrites).--min 97.7becomes--min 100in.github/workflows/dart.yaml. -
Generated artifacts are no longer versioned.
coverage/lcov.infoand theexample/ios/Flutter/ephemeral/output offlutter pub getare untracked and added to.gitignore, so a local coverage run or a plugin re-resolution cannot show up as a source change.
0.1.2 #
Fixed #
-
Foldable blocks that merely touch are no longer merged.
joinIntersectingjoined two blocks when the second began on the first block's last line, so}void main(){— the closer of one block and the opener of the next — became a single block and folding the first folded the second (issue #25). Touching blocks now stay separate (0..1,2..8,3..5,8..11). -
The block below keeps its start on screen. When a block's closing line also opens the following foldable block, the hidden range ends one character earlier so the closer keeps a row of its own — and with it the line number and the fold toggle the next block hangs there. The guard no longer requires the closer to be the line's first token, so a multi-line condition (
if (a &&/b &&/c) {) is covered too. The upstream glue default (parsers: [ ],) is unchanged.
0.1.1 #
Fixed #
-
Fold ranges now end at the start of the last line only when that line is a closing line (
),],}…). 0.1.0 applied this unconditionally, which hid only the newline before a non-closing last line and glued the neighbouring lines together: folding// first line // second line // third lineproduced
// first line// third line. Content lines — the tail of a//comment block, a continued argument, an import group — are hidden whole again, exactly as upstream does, while folded closers stay on screen as before:parsers: [ ],,void method() { },ScraperConfig();. -
The gutter now uses the code style's line height.
CodeField._buildGutter()copiedfontSizeandfontFamilyfrom the code style "for consistency with lines" but notheight, so gutter rows fell back to the font's own metrics — 19 px against the code's 21 px at the default style. The two grids therefore drifted ~1.9 px on every line, reaching ~60 px by line 36 of a typical file. Because the fold chevron is centred in its gutter row it inherited that drift, so chevrons climbed above the line they labelled (over half a line off by line 9, worse further down). The gutter now starts on the code text's top edge with a row pitch equal to the rendered line height.
Added #
gutter_alignment_test.dartpins the gutter/code grid invariant — top edge and row pitch — in both the folded and unfolded states.- Comment-block regression tests pinning that non-closing blocks hide their last line whole, with no glued text.
0.1.0 #
First release of zuraffa_code_editor, a maintained fork of
flutter_code_editor 0.3.5.
Changed #
- Folded blocks keep their closing line visible. Upstream hid a folded
block through the end of its last line, so folding
parsers: [ ... ]left a danglingparsers: [with no matching],— broken-looking code. The fold range now ends at the start of the block's last line, so the closing line stays on screen:parsers: [ ],,void method() { },ScraperConfig();. Gutter line numbering, fold-toggle placement and the number of rendered rows are all unchanged — only the closing text is added back. - Repackaged from
flutter_code_editortozuraffa_code_editor; all imports are nowpackage:zuraffa_code_editor/zuraffa_code_editor.dart. - Linting moved from
total_lintstoflutter_lints.
Thanks #
Everything except the fold change above is the work of the upstream akvelon/flutter-code-editor contributors. Changes to this fork's own code are dedicated to the public domain under CC0.
0.3.5 #
- Fixed line numbers not aligning with lines (https://github.com/akvelon/flutter-code-editor/pull/307)
0.3.4 #
- Added default height to
defaultTextStyle(https://github.com/akvelon/flutter-code-editor/pull/297). - Added
disposedandmountedchecks toCodeFieldandCodeController(https://github.com/akvelon/flutter-code-editor/pull/298). - Added
UndoHistoryControllertoCodeField(https://github.com/akvelon/flutter-code-editor/pull/302). - Added support for
yamlcomments (https://github.com/akvelon/flutter-code-editor/pull/305).
0.3.3 #
- Added
smartDashesTypeandsmartQuotesTypetoCodeField(https://github.com/akvelon/flutter-code-editor/pull/278). - Added named sections support for JavaScript and TypeScript (https://github.com/akvelon/flutter-code-editor/pull/291).
0.3.2 #
- Flutter 3.22 WASM fixes
0.3.1 #
- Support for Flutter v3.13.6.
0.3.0 #
- BREAKING: Deleted
CodeController.stringMap. - BREAKING: Deleted
themefromCodeController, (Issue 172).
0.2.24 #
- Hide the suggestion box when
CodeFieldis disposed (Issue 241). - Fix issues with Flutter 3.10.
0.2.23 #
- Added
CodeController.readOnly.
0.2.21 #
- 'Enter' key in the search pattern input scrolls to the next match.
0.2.20 #
- Alpha version of search.
0.2.17 #
0.2.16 #
0.2.10 #
0.2.9 #
- Hiding line numbers, errors, and folding handles (Issue 159).
- Indent new line after
:in Python (Issue 135). - Track the test coverage, add the codecov badge (Issue 146).
- Do not pale the editor if a visible section is set (Issue 153).
- Added GIFs to README (Issue 148).
- Fixed 'Index out of range' exception with visible sections on the default factorial example (Issue 152).
- Fixed linter issues (Issue 147).
0.2.4 #
- Exported
StringExtension. - Added an example with changing the language and the theme.
0.2.3 #
- Fixed removing listeners in
_CodeFieldState.dispose().
0.2.2 #
- Added
CodeController.lastTextSpanfield (visible for testing) to return the lastTextSpanproduced bybuildTextSpan().
0.2.1 #
- Added the migration guide for 0.2 to README.
0.2.0 #
- BREAKING: Removed theme from
CodeController. UseCodeThemewidget instead. - BREAKING: Removed
webSpaceFix, https://github.com/flutter/flutter/issues/77929 - BREAKING: Fixed typo
IntendModifier→IndentModifier. - BREAKING:
CodeFieldStateis now private.
0.1.15 #
- Added a missing code file.
0.1.13 #
- Remove an accidentally published temp file.
0.1.12 #
- Reformatted the license, updated README.
0.1.11 #
- Updated README.
0.1.10 #
- Fixed formatting.
0.1.8 #
- Fixed linter issues.
0.1.7 #
- Fixed README errors.
0.1.6 #
- Improved README.
0.1.5 #
- Updated license formatting to match pub.flutter-io.cn requirements.
0.1.4 #
- Added
CodeController.readOnlySectionNamesgetter and setter (Issue 110). - Added
CodeController.foldCommentAtLineZero,foldImports,foldOutsideSections(Issue 89). - Added
CodeController.visibleSectionNames(Issue 27). - Fixed folding Python blocks with multiline
ifconditions (Issue 108). - Fixed folding duplicate blocks like
[{...}]etc (Issue 99). - Fixed
cutLineIndexIfVisiblebug (Issue 112).
0.1.3 #
- Custom undo/redo implementation (Issue 97).
- Remove
FoldableBlockduplicates (Issue 99). - Copy folded text (Issue 24).
0.1.2 #
- Preserve selection when folding and unfolding (Issue 81).
0.1.1 #
- Added code folding.
- Fixed editing around hidden text ranges.
- Updated dependencies.
0.1.0 #
- Highlights unterminated blocks for Java and Python.
0.0.9 #
- Forked https://github.com/BertrandBev/code_field
- Re-license under the Apache license, mention the original author as the original license required.
- Added hidden service comment support.
- Added read-only blocks support.
- Added autocomplete for keywords, already-in-the-editor words, and external dictionary.