← Guides

Migrating from Aardink 0.5 to 0.6

0.6.0 replaces the editor’s text field with a virtualised renderer and its own text input on every platform (Android, desktop, web). Typing cost no longer grows with document size, and the editor gains multi-cursor editing, a minimap, sticky scroll, bracket-pair colours and go-to-definition. The price is a deliberate API break: everything that assumed one whole-document BasicTextField is gone.

Most apps need three changes: pass EditorOptions instead of the boolean flags, stop reading CodeEditorState.textFieldState, and drop annotatedText. The rest of this guide lists every removed or changed public symbol, with a before/after for each.

The “Landed in” column names the pull request of AARDINK_0.6_PLAN.md §6 that made each change, for anyone reading the history. The last section upgrades a real app.

Dependencies

implementation("com.aardarch:aardink:0.6.0")
implementation("com.aardarch:aardink-languages:0.6.0")

The web package moves to npm: pnpm add @aardarch/aardink-web.

CodeEditorLayout

0.50.6Landed in
readOnly, softWrap, showGutter, showLineNumbers, showFoldMarkers, showDiagnosticAnnotations, showDiffMarkers parametersoptions = EditorOptions(readOnly = …, softWrap = …, …), a data class: change one switch with copyPR 7
annotatedText: AnnotatedString?Removed. Highlighting comes from the state’s tokenizer; override colours through EditorThemePR 7
diagnostics: List<Diagnostic> = emptyList()diagnostics: List<Diagnostic>? = null. null collects them from the language service (500 ms after the editor appears, and after each pause in typing) and reports each list to the new onDiagnosticsChange; pass a list to supply your own, as before. A host that passed nothing and has a language service now shows that service’s diagnostics: pass emptyList() to keep showing noneSignature PR 7; collecting PR 10

CodeEditorState

0.50.6Landed in
textFieldStateRemoved. Read text, selection / selections; edit with applyEdit / applyTextEditsPR 7
undoManager: EditorUndoManagerRemoved. Use undo(), redo(), canUndo, canRedo (snapshot state, for toolbar buttons) and pushUndoStop(); alternativeVersionId for dirty tracking; lastChangeKind to tell undo and redo from editsPR 4
tokenCache: TokenCacheRemoved. Use tokensForLine(line); a token that spans lines comes back as one token per linePR 2
tokenizer (val)var: assigning it re-highlights and keeps text, selection and undo historyPR 3
selection (read-only)Settable; setting it replaces every selection with this one. selections / setSelections handle several, the primary first (as in Monaco)PR 4

Other removed symbols

0.50.6Landed in
EditorGutter(...)Removed. CodeEditorLayout draws the gutter; toggle it with EditorOptionsPR 7
DrawScope.drawSquiggles(...)Removed; the editor draws squiggles itselfPR 7
annotateTokens(...)Removed; each line is coloured as it is laid outPR 7
applyFolding(...) (deprecated in 0.5)Removed. FoldState drives foldingPR 7
EditorTheme.fontFamily, fontSize, lineHeight (deprecated in 0.5)Removed. Provide LocalEditorTypographyPR 7
TokenCacheRemoved; the editor keeps its tokens internally. Read them with CodeEditorState.tokensForLinePR 2
EditorUndoManager, EditOperationRemoved; the editor keeps its undo history internallyPR 4

Changed behaviour

Area0.50.6Landed in
FindReplaceState.show()Opened the panel with its replace row always shownshow(replace = false): the replace row shows only with replace = true (Ctrl+H) or the panel’s new toggle; replaceMode holds itPR 3
Find panelEvery matchAt most EditorLimits.maxFindMatches (10,000), shown as “N+”; a new query cancels a running searchPR 3
Built-in tokenizers’ tokenizeLinesReturned tokens for the whole documentReturns only the lines it rescanned, with zero-length tokens marking the span, and expects previousTokens to be its own last result. A wrapper that passes its own earlier result back as previousTokens (as aardflex’s XmlIncrementalTokenizer does) is not recognised, so the built-in one answers with a full scan: still correct, just not incremental. To get the speed-up, return the built-in tokenizer’s list unchanged or pass that list backPR 3
CodeDocument.dirtyLines after a delete that joins linesRan to the end of the documentJust the line the delete started on: lines below keep their text and their tokens move with themPR 3
SimpleDiffProviderCompared lines by position, so every line below an inserted one showed as modifiedTrims the shared leading and trailing lines firstPR 3
Language services and folding providersCalled with the live CodeDocument, which could change under a service running on another threadCalled with a read-only snapshot taken when the request was made; editing it throws IllegalStateExceptionPR 2
CodeDocumentA plain classA CharSequence: index it, slice it with subSequence, and run a Regex over it without copying text. text is built once per edit and sharedPR 2
Web updateOptions({ language })Rebuilt the editor state and lost undo historyKeeps undo historyPR 3
UndoUndid runs of typed characters up to a space, and put the caret where the edit wasGroups as Monaco does: typing until the caret moves, the kind of edit changes, or a space follows a word, so a word at a time; backspaces together; every other edit on its own. Undo restores every selection from before the stepPR 4
RenderingOne BasicTextField held the whole documentThe editor lays out and draws only the lines on screen and takes its own text input (keys, input methods, clipboard). UI tests find it by its semantics (editable text, set-text and insert-text actions) instead of a text field; performTextInput works as beforePR 7
Initial caretAt the end of initialTextAt the start, as after loadText and in MonacoPR 7
Replace allOne undo step per matchOne undo step for all of themPR 7
Selection colourMaterial’s text-selection colourEditorTheme.selectionColorPR 7
Find match colourMaterial’s tertiary colourEditorTheme.findMatchColor, the current match strongerPR 8
LookPlain text on the theme backgroundThe caret’s line highlighted, the bracket pair at the caret boxed, brackets coloured by depth. Each can be turned off in EditorOptions; the colours are EditorTheme.lineHighlight and the new EditorTheme.bracketPairColorsPR 8
EditorThemeParser, 8-digit coloursRead as #AARRGGBBRead as VS Code writes them, #RRGGBBAAPR 8
CaretsThe text field’s caret, blinking while focusedOne caret per selection, blinking while the editor has focus, hidden without itPR 7
Mouse and touchThe text field’sClick, Shift+click, Alt+click (another caret), double and triple click, drag; touch tap, long press, selection handles with the platform’s text toolbar; a right-click menu on desktop and the webPR 7
Several selectionsOne selectionAlt+click, Alt+drag, Ctrl/Cmd+D, Ctrl+Shift+L, Ctrl+Alt+Up/Down and column selection (Shift+Alt+drag, middle-drag, Ctrl+Shift+Alt+arrows) make more; every edit and input method composition happens at each, as one undo step. CodeEditorState.selections has them all; selection is the primary onePR 4, PR 11
PopupsSignature help and hover at the top of the editor; completions only as a stripNext to the caret or the symbol, flipping above or below to stay in the window. With a hardware keyboard completions are a list at the caret (Ctrl+Space; arrows, Enter or Tab, Escape); touch devices keep the strip. HoverDocPopup, SignatureHelpPopup and CodeActionMenu keep their signatures for hosts that show them themselvesPR 9
Touch selection menuThe platform’s text toolbarThe editor’s own menu: cut, copy, paste, select all, and “Info” with a language servicePR 9
Language service navigationdefinition, references, format and formatRange were never calledF12 and Ctrl/Cmd+click call definition, Shift+F12 references, Shift+Alt+F format (or formatRange for a selection); the right-click menu has them too. A location in another file (range IntRange.EMPTY) goes to the new onNavigateToLocation parameter; without one it is ignoredPR 12
Diagnostics while editingStayed at their offsets until the next list, drifting off the text they markedMove with the text they mark, the host’s list too; dropped when the whole text is replaced (loadText)PR 10
Web mount defaultsA new language registry per editor, and builtInThemesOne shared registry and theme map, which registerLanguage and registerTheme add to; pass registry and themes to keep your own. The export template’s aardinkCreate no longer passes themPR 14
Web editor elementCompose’s element straight in the container, left there by disposeAn element of the editor’s own inside the container, removed by dispose; the container is the host’s as beforePR 14
Web first frameIn the default monospace font until JetBrains Mono loadedWaits up to 500 ms for JetBrains Mono (call preloadAardink() early)PR 14
Web onDidChangeContentThe textThe text and { versionId, isUndoing, isRedoing, isFlush }; the template’s aardinkOnContentChange feeds it (aardinkOnChange still works)PR 14
Web diagnosticsOnly the host’s, from setDiagnosticsThe language’s own until the host calls setDiagnostics; setDiagnostics(null) goes back to them, and onDiagnosticsChange (onDidChangeDiagnostics in the npm package) reports them. Call setDiagnostics([]) at mount to keep showing nonePR 10
Keyboard toolbarInserted the character as isTypes it through the typing rules, as a key: ( gets its )PR 7
Web clipboardThe text field’sCtrl/Cmd+C, X and V are left to the browser, whose clipboard events carry the text without a permission promptPR 7

Worth adopting

Not needed to compile, but new in 0.6:

Example: the aardflex Android app

The aardflex app (apps/android/aardflex/v1.0 in the aardflex repository) goes from 0.4.0 to 0.6.0. It passes its own diagnostics and has no custom EditorTheme fonts, so most of its code compiles unchanged; one screen changes.

gradle/libs.versions.toml

aardink = "0.6.0"   # was "0.4.0"; skipping 0.5.0 is fine, the artifacts still resolve to -android

ui/screen/XmlEditorScreen.kt: the undo manager is gone, and canUndo / canRedo are snapshot state, so the derivedStateOf that re-read them on every text change goes too.

// 0.4 / 0.5
val canUndo by remember(editorViewModel) {
    derivedStateOf {
        editorViewModel.codeEditorState.textVersion
        editorViewModel.codeEditorState.undoManager.canUndo
    }
}
val canRedo by remember(editorViewModel) {
    derivedStateOf {
        editorViewModel.codeEditorState.textVersion
        editorViewModel.codeEditorState.undoManager.canRedo
    }
}

// 0.6
val canUndo = editorViewModel.codeEditorState.canUndo
val canRedo = editorViewModel.codeEditorState.canRedo

The CodeEditorLayout call compiles as it is. Its diagnostics = editorDiagnostics is a list, so the editor keeps showing the app’s own diagnostics rather than asking the language service. The app has no boolean flags to move into EditorOptions, but can now switch on what 0.6 adds:

CodeEditorLayout(
    state = editorViewModel.codeEditorState,
    languageService = editorViewModel.languageService,
    findReplaceState = editorViewModel.findReplaceState,
    foldState = editorViewModel.foldState,
    foldingProvider = editorViewModel.foldingProvider,
    diagnostics = editorDiagnostics,
    savedText = ui.savedText,
    onCursorChange = { line, col -> editorViewModel.syncCursorFromEditor(line, col) },
    options = EditorOptions(stickyScroll = true), // new in 0.6, optional
    modifier = modifier,
)

findReplaceState.show() still opens the find panel, now without its replace row; show(replace = true) opens both. GoToLineDialog, navigateTo, loadText, undo() and redo() are unchanged.

service/XmlIncrementalTokenizer.kt still compiles and highlights correctly. It post-processes the built-in XML tokenizer’s output and passes that list back as previousTokens, which the built-in tokenizer does not recognise as its own, so every pass is a full scan, as in 0.4. Two optional improvements:

Then re-run the app’s screenshot tests, and check an input method (Gboard: autocorrect, voice input, a Samsung keyboard’s deletes) and TalkBack by hand, since 0.6 replaces the text field underneath.

Source: docs/MIGRATION_0.6.mdat v0.8.0.