mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-05 17:08:33 +00:00
fix(cli): make Alt+Enter insert a newline on LF-as-Enter terminals
On terminals that send a bare LF for plain Enter (WSL is the case
prompt_toolkit itself calls out), Alt+Enter arrives as ESC + LF
("\x1b\x0a" = Escape + ControlJ), not the ESC + CR the existing
"escape","enter" binding matches. The Escape was swallowed and the bare
LF hit prompt_toolkit's default submit, so the documented "universally
supported" Alt+Enter newline fallback failed on exactly the terminal path
plain Enter is preserved for.
Bind ESC + ControlJ to insert a newline too, and add a real-PromptSession
regression test covering the WSL Alt+Enter path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
25a477050d
commit
77a6003255
@ -346,7 +346,16 @@ def _build_cli_key_bindings() -> KeyBindings:
|
|||||||
def _(event):
|
def _(event):
|
||||||
event.current_buffer.validate_and_handle()
|
event.current_buffer.validate_and_handle()
|
||||||
|
|
||||||
@kb.add("escape", "enter") # Alt+Enter / Meta+Enter
|
@kb.add("escape", "enter") # Alt+Enter / Meta+Enter (ESC + CR, "\x1b\r")
|
||||||
|
def _(event):
|
||||||
|
event.current_buffer.insert_text("\n")
|
||||||
|
|
||||||
|
# On the same LF-as-Enter terminals (WSL) we preserve plain Enter for,
|
||||||
|
# Alt+Enter arrives as ESC + LF ("\x1b\x0a" = Escape + ControlJ) rather than
|
||||||
|
# ESC + CR, so the "escape","enter" binding above never matches: Escape is
|
||||||
|
# swallowed and the bare LF triggers prompt_toolkit's default submit. Bind
|
||||||
|
# ESC + ControlJ too so Alt+Enter reliably inserts a newline there as well.
|
||||||
|
@kb.add("escape", Keys.ControlJ) # Alt+Enter on LF-as-Enter terminals
|
||||||
def _(event):
|
def _(event):
|
||||||
event.current_buffer.insert_text("\n")
|
event.current_buffer.insert_text("\n")
|
||||||
|
|
||||||
|
|||||||
@ -140,6 +140,29 @@ async def test_xterm_modifyotherkeys_shift_enter_inserts_newline():
|
|||||||
assert result == "foo\nbar"
|
assert result == "foo\nbar"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_alt_enter_inserts_newline_on_lf_terminals():
|
||||||
|
"""On LF-as-Enter terminals (WSL), Alt+Enter arrives as ESC + LF
|
||||||
|
("\\x1b\\x0a" = Escape + ControlJ) rather than the ESC + CR that the
|
||||||
|
"escape","enter" binding matches. Without a dedicated ESC + ControlJ
|
||||||
|
binding the Escape is swallowed and the bare LF submits, so the documented
|
||||||
|
Alt+Enter newline fallback breaks on exactly the terminal path plain Enter
|
||||||
|
is preserved for. Drive a real PromptSession to prove it inserts a newline.
|
||||||
|
"""
|
||||||
|
from prompt_toolkit.application import create_app_session
|
||||||
|
from prompt_toolkit.input import create_pipe_input
|
||||||
|
from prompt_toolkit.output import DummyOutput
|
||||||
|
|
||||||
|
with create_pipe_input() as pipe_input:
|
||||||
|
with create_app_session(input=pipe_input, output=DummyOutput()):
|
||||||
|
commands._init_prompt_session()
|
||||||
|
session = commands._PROMPT_SESSION
|
||||||
|
pipe_input.send_text("foo\x1b\x0abar\r")
|
||||||
|
result = await session.prompt_async("> ")
|
||||||
|
|
||||||
|
assert result == "foo\nbar"
|
||||||
|
|
||||||
|
|
||||||
def test_thinking_spinner_pause_stops_and_restarts():
|
def test_thinking_spinner_pause_stops_and_restarts():
|
||||||
"""Pause should stop the active spinner and restart it afterward."""
|
"""Pause should stop the active spinner and restart it afterward."""
|
||||||
spinner = MagicMock()
|
spinner = MagicMock()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user