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

    Fields

    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