On this page

Interactive shell

Keybinding reference

Configure Emacs or Vi editing, parse key chords, map every supported action, and diagnose terminal/keybinding conflicts.

Status
Current Reedline mapping
For
Interactive Shoal users
On this page
  1. Editing modes
  2. Chord grammar
    1. Modifiers
    2. Named keys
    3. Binding the dash key
    4. Terminal normalization caveats
  3. Supported actions
    1. History
    2. Directional editor events
    3. Screen
    4. Completion menu
    5. Submission and neutral action
    6. External editor
    7. Editing commands
  4. Complete action-name list
  5. Example: compact Emacs-style customizations
  6. Example: Vi mode with shared custom chords
  7. Overrides and conflicts
  8. Warnings and recovery
  9. Debug checklist

Shoal’s interactive editor uses Reedline’s Emacs or Vi defaults and layers user mappings from [editor.keybindings] on top.

[editor]
mode = "emacs"
bracketed_paste = true

[editor.keybindings]
"ctrl-r" = "history_search_backward"
"ctrl-l" = "clear_screen"
"ctrl-alt-e" = "open_editor"
"shift-tab" = "menu_previous"

Bad entries warn and are skipped; they do not prevent the shell from starting or discard valid siblings.

Editing modes🔗

[editor]
mode = "emacs" # default

or:

[editor]
mode = "vi"

The mode selects Reedline’s default binding table:

  • Emacs mode: custom bindings are added to the default Emacs table.
  • Vi mode: each custom binding is added to both insert and normal tables; the current config cannot target only one Vi mode.

Shoal explicitly maps unmodified Tab to the completion menu/next behavior before applying custom mappings. A user mapping for "tab" is added afterward and can replace that default.

Changing config requires a new interactive process; there is no live keymap reload.

Chord grammar🔗

A chord is:

[modifier-]...[modifier-]key

Examples:

r
ctrl-r
ctrl-alt-x
shift-tab
cmd-k
f5
ctrl--

Chord parsing is ASCII case-insensitive, so CTRL-R and ctrl-r are equivalent. Action names are case-sensitive.

Modifiers🔗

CanonicalAccepted spellings
Controlctrl, control
Altalt, option
Shiftshift
Supersuper, cmd, command, meta

Multiple modifiers are ORed:

"ctrl-alt-shift-x" = "clear_line"

Unknown modifiers reject the chord. hyper-r, for example, warns and is skipped.

Named keys🔗

KeyAccepted spellings
Tabtab
Enterenter, return
Escapeesc, escape
Backspacebackspace
Deletedelete, del
Arrowsleft, right, up, down
Line/document movementhome, end, pageup, pagedown
Insertinsert
Spacespace
Function keyf0 through f99 are syntactically accepted; actual terminal support is narrower.
CharacterAny single-byte character, such as a, /, +, or ,.

Only a one-byte character is accepted by the current parser. A multibyte Unicode key name is not a valid chord even if a terminal could emit it.

Binding the dash key🔗

Because - separates modifiers, a trailing dash has a special rule:

"-" = "none"
"ctrl--" = "cut_word_left"

The final dash is the character key. ctrl-- means Ctrl plus -, not an empty key segment.

Terminal normalization caveats🔗

Configuration describes the event Shoal expects, not what every terminal sends:

  • many terminals cannot distinguish some Ctrl-letter/control-byte combinations;
  • Shift with punctuation may arrive as the shifted character without a Shift modifier;
  • Super/Cmd is often consumed by the desktop/terminal and never reaches the application;
  • Alt may arrive as Escape-prefixed input depending on terminal settings;
  • tmux/screen can translate or reserve chords;
  • function keys beyond the physical set are unlikely to be emitted.

If a syntactically valid mapping does nothing, test whether the terminal/tmux receives/forwards it before changing Shoal.

Supported actions🔗

History🔗

Canonical actionAliasesReedline event
history_search_backwardsearch_historySearch history.
history_prevprevious_history, up_historyPrevious history entry.
history_nextnext_history, down_historyNext history entry.

Example:

[editor.keybindings]
"ctrl-r" = "history_search_backward"
"ctrl-p" = "history_prev"
"ctrl-n" = "history_next"

Line history is the editor’s command recall store, distinct from the structured execution journal.

Directional editor events🔗

ActionEvent
upGeneric editor Up.
downGeneric editor Down.
leftGeneric editor Left.
rightGeneric editor Right.

These are Reedline events rather than fixed cursor-edit commands; their behavior can depend on menu/multiline/editor state.

Screen🔗

ActionMeaning
clear_screenClear/redraw the visible screen event.
clear_scrollbackClear terminal scrollback event.
"ctrl-l" = "clear_screen"
"ctrl-alt-l" = "clear_scrollback"

Terminal support for clearing scrollback varies.

Completion menu🔗

Canonical actionAliasMeaning
menucompletion_menuOpen/select the menu named completion_menu.
menu_nextAdvance menu selection.
menu_previousMove to previous menu selection.
completeReedline’s unparameterized Complete edit command.

menu targets Shoal’s configured completion_menu. complete is an edit command and is not identical to explicitly opening/navigating the named popup.

Example:

"tab" = "menu_next"
"shift-tab" = "menu_previous"
"ctrl-space" = "menu"

If [completion].menu = false, Reedline is configured for quicker/partial completion where possible, but a popup can still appear when multiple candidates have no shared prefix.

Submission and neutral action🔗

ActionMeaning
enterReedline Enter event (works with Shoal’s multiline validator).
submitReedline Submit event.
noneConsume/map to no editor action.

Use enter for the normal return-key behavior unless you specifically understand Reedline’s Submit distinction and Shoal multiline validation.

External editor🔗

ActionMeaning
open_editorOpen the current buffer in the configured/external editor flow.
"ctrl-x" = "open_editor"

The host editor environment/config still determines whether an editor can launch.

Editing commands🔗

Canonical actionAliasesUnderlying edit command
backspaceBackspace.
deleteDelete.
clearclear_lineClear buffer.
cut_word_leftCut word left.
cut_word_rightCut word right.
completeComplete.
undoUndo edit.
redoRedo edit.

Only this curated unparameterized subset is configurable through strings. Reedline exposes many motion/selection/parameterized edit commands that Shoal’s config parser does not map today.

Complete action-name list🔗

For copy/paste/reference:

history_search_backward  search_history
history_prev             previous_history  up_history
history_next             next_history      down_history
up                       down              left             right
clear_screen             clear_scrollback
menu                     completion_menu
menu_next                menu_previous
open_editor
enter                    submit            none
backspace                delete
clear                    clear_line
cut_word_left            cut_word_right
complete                 undo              redo

Whitespace/case is not normalized for action values. Use the lowercase exact spellings.

Example: compact Emacs-style customizations🔗

[editor]
mode = "emacs"

[editor.keybindings]
"ctrl-r" = "history_search_backward"
"ctrl-p" = "history_prev"
"ctrl-n" = "history_next"
"ctrl-l" = "clear_screen"
"ctrl-w" = "cut_word_left"
"alt-d" = "cut_word_right"
"ctrl-_" = "undo"
"ctrl-alt-_" = "redo"
"ctrl-x" = "open_editor"
"shift-tab" = "menu_previous"

Whether ctrl-_ is distinguishable depends on terminal encoding; choose another chord if not.

Example: Vi mode with shared custom chords🔗

[editor]
mode = "vi"

[editor.keybindings]
"ctrl-r" = "history_search_backward"
"ctrl-l" = "clear_screen"
"ctrl-alt-e" = "open_editor"

These three custom chords are installed in both Vi insert and normal tables. There is currently no config such as [editor.keybindings.insert]/normal.

Overrides and conflicts🔗

Custom bindings are added after defaults. A matching (modifiers, key) replaces/overrides the prior table entry according to Reedline’s keybinding map.

Potential conflicts:

  1. a custom Tab replaces Shoal’s explicit completion Tab mapping;
  2. Vi custom chords apply to both modes and can replace a valuable normal-mode command;
  3. two textual chords that normalize to the same event (ctrl-r and control-r) produce two additions in sorted config-map iteration; avoid aliases for the same chord because the final winner is not a useful contract to rely on;
  4. terminal-level shortcuts can mask the application mapping;
  5. action aliases map to exactly the same Reedline event and add no distinct semantics.

Keep one canonical spelling per chord.

Warnings and recovery🔗

Invalid chord warning:

warning: editor.keybindings: unrecognized key chord `hyper-r`

Invalid action warning:

warning: editor.keybindings.ctrl-g: unrecognized action `cancel_everything`

The entry is skipped. To recover from a broken/unusable valid mapping:

  1. exit the shell from another available chord or terminal signal;
  2. edit shoal.toml with another shell/editor;
  3. remove/comment the custom table or launch with a clean temporary config environment;
  4. restart Shoal.

Because invalid mappings never hard-fail, a typo can look like “the default still works.” Read startup warnings.

Debug checklist🔗

  1. Confirm [editor.keybindings] is nested under [editor] correctly.
  2. Quote TOML keys containing dashes.
  3. Use a lowercase exact action name.
  4. Reduce to a simple mapping such as "ctrl-l" = "clear_screen".
  5. Restart the interactive process after editing.
  6. Check startup warnings and the active config layer.
  7. Test outside tmux/screen.
  8. Check terminal/OS shortcut settings.
  9. In Vi mode, test both insert and normal behavior.
  10. Restore defaults by removing the custom entry/table.

Configuration discovery and editor/history/completion options are in Configuration and prompt.

Type to search every guide navigate open esc close
Diagram