Skip to content

Options and keymaps ​

tsx
import { useEffect, useState } from 'react';
import { Switch } from '@adecore/ui';
import { EditorFrame } from '../shared/editor-frame.tsx';
import { SAMPLE, useEditor } from '../shared/editor.ts';

const OPTIONS = { text: SAMPLE, language: 'typescript', theme: 'demo', rightMargin: 60 } as const;

export default function EditorOptionsDemo() {
    const { host, editor } = useEditor(OPTIONS);
    const [wrap, setWrap] = useState(false);
    const [guides, setGuides] = useState(true);
    const [whitespace, setWhitespace] = useState(false);
    const [readOnly, setReadOnly] = useState(false);

    useEffect(() => editor?.setWrap(wrap), [editor, wrap]);
    useEffect(() => editor?.setGuides(guides), [editor, guides]);
    useEffect(() => editor?.setWhitespace(whitespace), [editor, whitespace]);
    useEffect(() => editor?.setReadOnly(readOnly, 'This demo is read-only.'), [editor, readOnly]);

    const toggle = (label: string, checked: boolean, onChange: (checked: boolean) => void) => (
        <label className="flex items-center gap-2 px-1 text-xs text-text">
            <Switch label={label} checked={checked} onCheckedChange={onChange} />
            {label}
        </label>
    );

    return (
        <EditorFrame
            host={host}
            className="h-64"
            toolbar={
                <>
                    {toggle('Wrap', wrap, setWrap)}
                    {toggle('Guides', guides, setGuides)}
                    {toggle('Whitespace', whitespace, setWhitespace)}
                    {toggle('Read-only', readOnly, setReadOnly)}
                </>
            }
        />
    );
}

Mount options ​

engine.mount(element, options) takes EditorOptions. Most options have a setter on the Editor that changes them later without a remount.

OptionDefaultSetterMeaning
textrequiredsetTextThe document
themerequiredsetThemeThe syntax theme id, handed to the tokenizer
languageplain textThe language id, for the tokenizer and the editing commands
pathThe file's path or name, for a language service to read a dialect off
readOnlyfalsesetReadOnlyWith readOnlyReason, what a person is told when typing into it
wrapfalsesetWrapWrap long lines
indentation4, spacessetIndentation{ tabSize, insertSpaces }
smartKeysDEFAULT_SMART_KEYSsetSmartKeysWhat happens as a person types; see below
guidestruesetGuidesA line at each indentation level
whitespacefalsesetWhitespaceSpaces as dots and tabs as arrows
rightMarginnonesetRightMarginA column to draw a line at; null removes it
foldOutline'hover'setFoldOutlineWhen the fold arrows show in the gutter: 'off', 'hover' or 'always'
messagesnone{ noMoreOccurrences }, said when select next occurrence finds no more; without it the editor says nothing
labelCode editorsetLabelThe accessible name of the editor, such as Commit message, in the app's language
foldsFolds kept from the last time; see folding
foldDefaultsFold roles to collapse when a file opens without kept folds
line, columnOne-based, where the caret starts
scrollTopIn pixels, where the view starts; without it the caret's line is in view

setReadOnly stops typing, commands and applyEdits. setText still works, since a reload from disk is not a person typing.

refreshFont() reads the code font from the page again. Call it after changing --font-mono, --code-font-size or --code-line-height, or after a web font loads, since measured widths, wrapping and hit testing depend on it.

Smart keys ​

EditorSmartKeys turns each typing behavior of the model on or off. All are on except camelHumps. setSmartKeys takes a partial object and leaves the rest as they are.

KeyWhat it does
autoPairBracketsA typed bracket brings its closer, and typing the closer steps over
autoPairQuotesThe same for quotes
surroundSelectionA bracket or quote typed over a selection wraps it
tabOutOfClosersTab steps over a closer the editor added
smartIndentOnEnterEnter indents, continues comments and closes braces
indentOnPasteA pasted block takes the indentation of the line it lands on
smartSemicolonA ; typed inside a call goes to the end of the statement
smartArrowIn PHP a - after a variable, a member, ) or ] becomes ->
camelHumpsWord moves also stop inside camelCase and snake_case words

Keymaps ​

@adecore/editor/keymap holds one table of every editor and language command, with a chord for macOS and one for the other platforms. Mod is Cmd on a Mac and Ctrl elsewhere; Ctrl and Meta are the physical keys. A null chord means the command has no key, such as the fold levels and the AI commands selectionToChat, inlineEdit, suggestInline and acceptGhostWord.

Resolve the table once with the app's overrides, and give the same table to the engine, to ProjectLanguage and to the menus that print the keys:

ts
import { chordOf, parseChord, resolveKeymap } from '@adecore/editor/keymap';

const keymap = resolveKeymap({
    duplicateLine: { mac: 'Mod+Shift+D', other: 'Ctrl+Shift+D' },
    goToSymbol: { mac: null, other: null }
});

const engine = createSmartEditorEngine({
    tokenizer,
    keymap,
    apple,
    handBack: [parseChord('Mod+Shift+P')]
});

chordOf('duplicateLine', apple, keymap); // 'Mod+Shift+D'

resolveKeymap fills every id the overrides leave out from KEYMAP. parseChord throws on a chord it cannot read, so validate a chord a person stored before using it. A KeyBinding may carry takenMac or takenOther ({ platform, by }) to record which app command owns a chord the platform also uses; the editor does not read it.

Two hooks let the app take keys:

  • handBack chords pass through the editor untouched, with their default intact, so the app's global shortcuts keep working while the editor has focus.
  • editor.onKeyDown(handler) sees every other key first. A handler that returns true takes the key; the editor prevents its default and does nothing with it.

A key table id is not always a command name. collapse runs the view command collapseRegion, for example. To run a command from a menu, pass an EditorRunCommand to runCommand; see Text and events.

Licenses and third-party notices are listed in each package.