Skip to content

Alt modifier dropped on named keys (Alt+Enter arrives as plain enter) #6663

Description

@vlebourl

Summary

XTermParser._sequence_to_key_events only applies the alt modifier when the resolved key name is a single character (if len(name) == 1 and alt). For a Meta-prefixed named key — e.g. Alt/Option+Enter, which a terminal sends as ESC + CR (\x1b\r) — the resolved name is enter (length 5), so the alt is silently dropped. The event arrives as plain enter, indistinguishable from a real Enter.

alt+j works; alt+enter, alt+tab, alt+backspace, etc. do not.

Impact

Apps can't bind alt+enter — the common "insert newline vs. submit" split in chat/agent CLIs — on terminals that deliver Meta as an ESC prefix rather than via the Kitty keyboard protocol (e.g. xterm.js-based terminals like Termius, macOS "Option as Meta").

Textual version

8.2.8 (latest on PyPI). The same guard is present on main.

Reproduction

from textual._xterm_parser import XTermParser

p = XTermParser(False)
keys = [t.key for t in p.feed("\x1b\r") if hasattr(t, "key")]
keys += [t.key for t in p.feed("") if hasattr(t, "key")]  # EOF flush
print(keys)   # ['enter']  -- expected ['alt+enter']

Location

src/textual/_xterm_parser.py, in _sequence_to_key_events:

if len(name) == 1 and alt:      # <-- drops alt for named keys
    if name.isupper():
        name = f"shift+{name.lower()}"
    name = f"alt+{name}"
yield events.Key(name, sequence)

Proposed fix

Apply the alt prefix regardless of name length; keep the shift/upper handling scoped to single characters:

if alt:
    if len(name) == 1 and name.isupper():
        name = f"shift+{name.lower()}"
    name = f"alt+{name}"
yield events.Key(name, sequence)

With this, \x1b\r yields alt+enter, and alt+j / plain enter / plain j are unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions