← Guides

Migrating from Aardink 0.6 to 0.7

0.7 makes the editor controllable and observable by its host: content and selection events, commands a toolbar or a menu can run, host items in the touch chrome, richer diagnostics, a completion list the editor arranges itself, language composition in Kotlin, Monaco theme data, and an XML language service generated from a schema. AARDINK_ROADMAP.md §5 has the plan and §11 the API ledger.

Most of it is additive. Three changes break source that implements or builds the editor’s types:

Every data class and composable that gained a parameter (CompletionItem, Diagnostic, EditorOptions, CodeEditorLayout, KeyboardToolbarRow, the LSP and web message classes) changed its binary signature: recompile against 0.7. Calls by name or by position keep compiling.

Dependencies

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

On the web: pnpm add @aardarch/aardink-web@^0.7.0.

LanguageService

0.60.7
fun smartIndent(document, lineIndex): Int, a number of spacesfun smartIndent(document, lineIndex, options: FormattingOptions): String, the whitespace itself, "" for none. With insertSpaces off the editor now gets tabs
suspend fun format(document): Stringsuspend fun format(document, options: FormattingOptions): String: indent with options
suspend fun formatRange(document, range): List<TextEdit>suspend fun formatRange(document, range, options: FormattingOptions): List<TextEdit>
(none)val completionsAreIncomplete: Boolean, default true: as 0.6, asked again on every letter. Return false to be asked once per word and let the editor filter

A service written for 0.6:

override fun smartIndent(document: CodeDocument, lineIndex: Int): Int {
    if (lineIndex <= 0) return 0
    val previous = document.lineText(lineIndex - 1)
    val indent = previous.takeWhile { it == ' ' }.length
    return if (previous.trimEnd().endsWith("{")) indent + 4 else indent
}

override suspend fun format(document: CodeDocument): String = myFormatter(document.text, indent = "    ")

becomes:

override fun smartIndent(document: CodeDocument, lineIndex: Int, options: FormattingOptions): String {
    if (lineIndex <= 0) return ""
    val previous = document.lineText(lineIndex - 1)
    return options.indentAfter(previous, levels = if (previous.trimEnd().endsWith("{")) 1 else 0)
}

override suspend fun format(document: CodeDocument, options: FormattingOptions): String =
    myFormatter(document.text, indent = options.indentUnit)

FormattingOptions measures indentation in columns: indentWidth(line) reads a line’s, indentOf(columns) writes one as spaces or tabs, indentAfter(line, levels) does both, and indentUnit is one level. The editor passes its EditorOptions.tabSize and insertSpaces. Callers can leave options out: it defaults to four spaces, as 0.6 assumed.

Diagnostic

0.60.7
Diagnostic(range, lineNumber, message, severity, source)Diagnostic(range, message, severity, source, code, codeDescription, tags, relatedInformation). The old constructor still compiles, deprecated, and ignores the line
diagnostic.lineNumberdiagnostic.lineIn(document): the line is worked out from range, which the editor keeps on its text as it is edited
DiagnosticSeverity.Error, Warning, InfoHint too: three dots under the start, no gutter dot. Add a branch to an exhaustive when
(none)code and codeDescription are shown after the source; tags draw Unnecessary code faded and Deprecated code struck through; relatedInformation points elsewhere
// 0.6
Diagnostic(range = start until end, lineNumber = line, message = "Unknown module", severity = DiagnosticSeverity.Error)
// 0.7
Diagnostic(range = start until end, message = "Unknown module", severity = DiagnosticSeverity.Error, code = "AF102")

A range may now run over several lines: the squiggle follows it, and on the web the marker carries endLineNumber.

CompletionItem and CompletionKind

0.60.7
Eight kinds, for XML and aardflexThe eight, then Monaco’s: Method, Function, Constructor, Field, Variable, Class, Struct, Interface, Enum, EnumMember, Keyword, Text, Color, File, Folder, Reference, Constant, Event, Operator, TypeParameter, Unit, User, Issue. A when over the kind needs an else
filterText and sortPriority documented, never readThe editor orders the list by sortPriority, keeping your order among equals. With completionsAreIncomplete = false it also filters by filterText
(none)detail, shown beside the label; commitCharacters, which accept the selected item when typed

The language server bridge

0.60.7
Formatting always asked for four spacesThe editor’s tabSize and insertSpaces
Severity 4 became InfoHint
Completion kinds squashed into eightLSP’s kinds, one for one
detail shown as the documentationCompletionItem.detail; documentation stays the documentation
Every keystroke asked the server againA list the server marks complete (isIncomplete: false, or a plain array) is asked for once per word and filtered by the editor; sortText orders it
LspDiagnostic, LspCompletionItemGained codeDescription, tags, relatedInformation and commitCharacters (new LspCodeDescription, LspDiagnosticRelatedInformation)

The web

The npm package

Nothing is removed. These change what an existing app sees:

Area0.60.7
A completion’s detailShown in place of documentation when there was noneIts own line beside the label
kind namesUnknown names showed as valueMonaco’s names ('method', 'class', 'keyword', …) and CompletionItemKind numbers are understood. 'function' and 'color' still mean transform and colorRef
Diagnostics from onDidChangeDiagnosticsOne line each, { line, startColumn, endColumn, message, severity }Also endLineNumber, startLineNumber, source, code, codeHref, tags and relatedInformation when they have them
setDiagnostics and provideDiagnosticsOur shape onlyMonaco’s IMarkerData passes as it is: startLineNumber, numeric MarkerSeverity, code as { value, target }; severity: 'hint'
F8Typed a w in some browsers (fixed in 0.6.3)Goes to the next problem, with Shift+F8 the previous one

A wasm module built from the export template

AardinkWeb.onContentChange’s callback takes a fourth argument, the changes as JSON. Copy the template’s new aardinkOnContentChange and the new exports (aardinkOnSelectionChange, aardinkOnFocusChange, aardinkGetPosition, aardinkSetPosition, aardinkTrigger, aardinkCanTrigger, aardinkActionLabel, aardinkAddAction, aardinkRemoveAction, aardinkAddCommand, aardinkRevealLine, aardinkRevealRange) from editor-web/src/wasmJsTest/.../ExportsTemplate.kt.

WebDiagnostic gained optional fields; WebEditorOptions gained quickSuggestions.

Changed behaviour

Area0.60.7
Gutter dotsA line with an error and an info showed the info’s dotThe most severe one’s
Ctrl+F / Ctrl+HOpened the panel with the last queryA selection on one line becomes the query, and the query is selected in its field, so typing replaces it, as in VS Code
The touch menuCut, Copy, Paste, Select all, InfoAlso Format and Quick Fix when a language service can, and your touchMenuItems; it scrolls sideways when it does not fit
The right-click menuNo rename or quick fixRename Symbol (F2) and Quick Fix (Ctrl+.), and your contextMenuItems
EscapeClosed completions or the find panelAlso the hover and the diagnostic banner
Enter and paste in a CR LF fileInserted LF, mixing line endingsInsert the file’s own line ending (CodeDocument.eol), or EditorOptions.eol

Worth adopting

Example: the aardflex Android app

The app (apps/android/aardflex/v1.0) is on 0.6.2. Its upgrade:

  1. gradle/libs.versions.toml: aardink = "0.7.0".
  2. service/language/AardflexXmlLanguageService.kt: smartIndent(document, lineIndex, options) returns options.indentAfter(previousLine, levels); format(document, options) passes options.indentUnit to XmlPrettyPrinter; the Diagnostic(...) call drops lineNumber and can now give the validator’s exact range and FriendlyErrorMapper’s message.
  3. Compose it with the built-in XML service, so its tag checks, closing tags on > and quotes come back: CompositeLanguageService(AardflexXmlLanguageService(...), BuiltInLanguages.Xml.languageService!!), or better, generate an XmlSchema from shared/generated/reference.json plus the structural descriptors and use XmlSchemaLanguageService(schema) with the expression completions as the primary.
  4. viewmodel/XmlEditorViewModel.kt: replace the cursor-callback refresh and the 500 ms timer with state.addContentListener { … }, and CodeDocument(document.text) with document.snapshot().
  5. ui/screen/XmlEditorScreen.kt: keyboardToolbarItems = listOf(KeyboardToolbarItem.Character('|'), KeyboardToolbarItem.Character(':'), KeyboardToolbarItem.Character('$'), KeyboardToolbarItem.Action("Format", command = EditorCommand.Format)), and a Validate button that runs EditorCommand.NextProblem.
  6. ui/theme/AardflexEditorTheme.kt: EditorThemeParser.fromMonacoJson(sharedThemeJson) instead of the hand-copied palette.

Example: aardflex-web-app

  1. pnpm add @aardarch/aardink-web@^0.7.0.
  2. src/editor/editor.ts: quickSuggestions: true; answer completions with { suggestions, incomplete: false } and drop the app’s own ranking in completions.ts.
  3. AppShell.svelte: replace the capture-phase Ctrl+S and F8 handlers with editor.addCommand('CtrlCmd+S', save); F8 and Shift+F8 are the editor’s own now. Remove the comment about F8 typing a w.
  4. registerLanguage({ id: 'aardflex-xml', extends: 'xml', schema, … }) with the schema built from reference.json, and delete what it replaces in schema.ts, completions.ts and diagnostics.ts. Keep the expression completions and hover in the providers.
  5. ProblemsPanel.svelte: diagnostics now span lines (endLineNumber); scan.ts no longer needs to cut them to one.

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