Skip to content

🐞 [BUG] Raw escape sequences ignored in CommandMode & Alt + Non-Latin layout requires raw Unicode bindings #4115

@p2ndemic

Description

@p2ndemic

Environment

Field Value
Micro version 2.0.16-dev.51 (CGO_ENABLED=1)
Distro CachyOS (Arch Linux)
Kernel 7.0.9-1-cachyos
CPU Intel Core i5-1240P
GPU Intel Iris Xe Graphics [ADL GT2]
GPU Driver i915
Mesa 26.1.1
Compositor labwc
My settings.json
{
    "*.bak": {
        "filetype": "micro"
    },
    "*.conf": {
        "filetype": "ini"
    },
    "*.txt": {
        "filetype": "micro"
    },
    "autosu": true,
    "clipboard": "external",
    "colorscheme": "dukedark_mod-tc",
    "diffgutter": true,
    "ft:asciidoc": {
        "tabsize": 2
    },
    "ft:asm": {
        "tabstospaces": false
    },
    "ft:awk": {
        "tabstospaces": false
    },
    "ft:c": {
        "tabsize": 2
    },
    "ft:c++": {
        "tabsize": 2
    },
    "ft:clojure": {
        "tabsize": 2
    },
    "ft:cmake": {
        "tabsize": 2
    },
    "ft:crystal": {
        "tabsize": 2
    },
    "ft:css": {
        "tabsize": 2
    },
    "ft:cuda": {
        "tabstospaces": false
    },
    "ft:dart": {
        "tabsize": 2
    },
    "ft:ebuild": {
        "tabstospaces": false
    },
    "ft:elixir": {
        "tabsize": 2
    },
    "ft:forth": {
        "tabstospaces": false
    },
    "ft:gdscript": {
        "tabstospaces": false
    },
    "ft:gleam": {
        "tabsize": 2
    },
    "ft:go": {
        "tabstospaces": false
    },
    "ft:gomod": {
        "tabstospaces": false
    },
    "ft:graphql": {
        "tabsize": 2
    },
    "ft:haskell": {
        "tabsize": 2
    },
    "ft:html": {
        "tabsize": 2
    },
    "ft:html5": {
        "tabsize": 2
    },
    "ft:jinja2": {
        "tabsize": 2
    },
    "ft:json": {
        "tabsize": 2
    },
    "ft:jsonnet": {
        "tabsize": 2
    },
    "ft:justfile": {
        "tabstospaces": false
    },
    "ft:lisp": {
        "tabsize": 2
    },
    "ft:lua": {
        "tabsize": 2
    },
    "ft:makefile": {
        "tabstospaces": false
    },
    "ft:markdown": {
        "tabsize": 2
    },
    "ft:meson": {
        "tabsize": 2
    },
    "ft:msbuild": {
        "tabsize": 2
    },
    "ft:nim": {
        "tabsize": 2
    },
    "ft:nix": {
        "tabsize": 2
    },
    "ft:ocaml": {
        "tabsize": 2
    },
    "ft:proto": {
        "tabsize": 2
    },
    "ft:puppet": {
        "tabsize": 2
    },
    "ft:r": {
        "tabsize": 2
    },
    "ft:ruby": {
        "tabsize": 2
    },
    "ft:scala": {
        "tabsize": 2
    },
    "ft:shell": {
        "tabsize": 4
    },
    "ft:svelte": {
        "tabsize": 2
    },
    "ft:terraform": {
        "tabsize": 2
    },
    "ft:tex": {
        "tabsize": 2
    },
    "ft:typescript": {
        "tabsize": 2
    },
    "ft:vue": {
        "tabsize": 2
    },
    "ft:xml": {
        "tabsize": 2
    },
    "ft:yaml": {
        "tabsize": 2
    },
    "ft:zsh": {
        "tabsize": 4
    },
    "helpsplit": "vsplit",
    "hlsearch": true,
    "matchbracestyle": "highlight",
    "mkparents": true,
    "parsecursor": true,
    "paste": false,
    "rmtrailingws": false,
    "ruler": false,
    "savecursor": false,
    "saveundo": false,
    "scrollbar": true,
    "scrollbarchar": "",
    "scrollspeed": 3,
    "smartpaste": true,
    "softwrap": true,
    "statusformatl": "$(filename) $(modified)$(overwrite) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
    "statusformatr": "$(line):$(col) | $(lines) ",
    "tabhighlight": false,
    "tabmovement": true,
    "tabsize": 4,
    "tabstospaces": true,
    "truecolor": "on",
    "useprimary": true,
    "wordwrap": true,
    "xterm": false
}
My bindings.json
{
    "Alt-r": "command:toggle ruler",
    "Alt-s": "command:setlocal showchars 'tab=›,space=·'",
    "Alt-Ctrl-s": "command:reset showchars",
    "Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
    "Alt-w": "command:toggle softwrap|command:toggle wordwrap",

    "\u001bк": "command:toggle ruler",
    "\u001bы": "command:setlocal showchars 'tab=›,space=·'",
    "\u001b\u0013": "command:reset showchars",
    "\u001bе": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
    "\u001bц": "command:toggle softwrap|command:toggle wordwrap",

    "Alt-Enter": "CommandMode",
    "\u001b[27;5;13~": "CommandMode",
    "\u001b[27;2;13~": "InsertNewline",

    "F3": "FindNext",
    "Shift-F3": "FindPrevious",

    "Alt-Ctrl-Down": "FindNext",
    "Alt-Ctrl-Up": "FindPrevious",

    "command": {
        "Alt-Enter": "AbortCommand",
        "\u001b[27;5;13~": "AbortCommand",
        "Ctrl-e": "AbortCommand"
    }
}

Description

I've encountered two keybinding bugs while configuring Micro in Foot/Kitty with a Cyrillic keyboard layout. Both issues stem from how Micro parses key events internally versus what the terminal actually sends.


Bug 1: Raw escape sequences are ignored in the command pane

Terminals such as Foot or Kitty send specific raw escape sequences for modified keys. For example, Ctrl+Enter sends \x1b[27;5;13~ and Shift+Enter sends \x1b[27;2;13~.

Using the > raw command, I can see Micro receives these sequences correctly in the main buffer. I can bind them in bindings.json:

{
    "Alt-Enter": "CommandMode",
    "\u001b[27;5;13~": "CommandMode",
    "\u001b[27;2;13~": "InsertNewline"
}
Image

This works perfectly in the main buffer. However, to create a toggle for the command mode, I need to bind the same key to AbortCommand inside the "command" pane type:

{
    "command": {
        "Alt-Enter": "AbortCommand",
        "\u001b[27;5;13~": "AbortCommand"
    }
}

Expected behavior: Pressing Ctrl+Enter while the command bar is open should execute AbortCommand and close the command bar (just like Alt-Enter does when bound this way).

Actual behavior: The raw escape sequence binding is completely ignored in the command pane. Pressing the key does nothing. The command pane parser does not support matching raw escape sequences the way the main buffer does.


Bug 2: Alt + Non-Latin keys require manual raw Unicode mappings

When using a non-Latin (Cyrillic ru_RU) keyboard layout, pressing Alt + a key sends an escape sequence containing the Unicode character of the Cyrillic letter, not the physical Latin key.

For example, Alt+r on a Cyrillic layout sends \x1bк (because 'к' is on the same physical key as 'r'). Micro does not map this to the logical Alt-r binding. To make it work, I have to manually figure out the raw sequence and duplicate every Alt binding (as seen in my bindings.json above):

{
    "Alt-r": "command:toggle ruler",
    "\u001bк": "command:toggle ruler"
}
Image

Expected behavior: Micro should ideally map keybindings based on the physical key position, or at least provide a mechanism to define layout-agnostic bindings so users don't have to duplicate all Alt bindings with raw Unicode codes.

Actual behavior: Users with non-Latin layouts must duplicate all Alt keybindings using raw \u001b<unicode_char> sequences, making the config bloated and hard to maintain.


And a few small requests ...

Feature Request 1: Native Shift-Enter support

Currently, to make Shift+Enter insert a newline, users of Foot/Kitty have to manually bind the raw escape sequence (\u001b[27;2;13~) to InsertNewline. It would be much more user-friendly if Micro natively supported the logical binding "Shift-Enter": "InsertNewline" out of the box, abstracting away the terminal-specific escape sequences.


Feature Request 2: Add a native CommandMode toggle

Currently, CommandMode only opens the command bar. To close it, you need a separate action (AbortCommand or Escape) in the "command" pane.

I heavily rely on the command bar for text manipulation using the manipulator-plugin. This plugin provides commands like > brace, > squote, > dquote, > upper, etc. My workflow constantly involves opening the command bar, typing a case conversion command, and executing it.

Having a single CommandMode toggle action (instead of separate open/close actions) would make this workflow significantly faster and more ergonomic.


Feature Request 3: Make Ctrl-e a toggle by default

Following up on the previous point, since working with plugins like manipulator requires frequent access to the command bar, it makes perfect sense for Ctrl-e to act as a toggle by default. Currently, if I press Ctrl-e to open the bar and change my mind, I have to reach for Esc to close it. If Ctrl-e were a toggle (mapped to a ToggleCommandMode action), the workflow would be seamless: press Ctrl-e, type snake, press Enter. Or press Ctrl-e again to cancel.


Summary of requested changes:

  1. Fix: Allow raw escape sequence bindings (like \u001b[27;5;13~) to work inside the "command" pane type.
  2. Fix: Improve handling of Alt modifiers for non-Latin keyboard layouts (physical key mapping).
  3. Feature: Add native support for "Shift-Enter": "InsertNewline" without requiring raw escape hacks.
  4. Feature: Add a ToggleCommandMode action.
  5. Feature: Change the default Ctrl-e binding from CommandMode to ToggleCommandMode.

Thank you for your time and for maintaining the best editor

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions