Skip to content

feat: add Shift+Enter as newline insert keybinding#229

Draft
Arkevius wants to merge 1 commit intompfaffenberger:mainfrom
Arkevius:feature/shift-enter-newline
Draft

feat: add Shift+Enter as newline insert keybinding#229
Arkevius wants to merge 1 commit intompfaffenberger:mainfrom
Arkevius:feature/shift-enter-newline

Conversation

@Arkevius
Copy link
Copy Markdown
Contributor

@Arkevius Arkevius commented Mar 9, 2026

What

Adds Shift+Enter (s-enter) as a new keybinding for inserting newlines in the interactive prompt, alongside the existing Ctrl+J and Ctrl+Enter options.

Why

Shift+Enter is the most intuitive newline shortcut for most users — it's the standard in Slack, Discord, Teams, ChatGPT, and virtually every modern chat UI. Adding it reduces the learning curve for new users who would otherwise need to discover Ctrl+J or toggle multiline mode.

Changes

3 files changed, 20 insertions(+), 1 deletion(-)

code_puppy/command_line/prompt_toolkit_completion.py

  • Added s-enter key binding following the identical pattern used by c-enter (lines 637–643): try/except guard with eager=True, since Shift+Enter is terminal-dependent (works in modern terminals supporting CSI u / kitty keyboard protocol — kitty, WezTerm, iTerm2, Windows Terminal; silently ignored on older terminals).

code_puppy/cli_runner.py

  • Updated help text from "newline: Ctrl+J""newline: Shift+Enter or Ctrl+J" — uses "or" (not comma) to clearly indicate these are alternatives, not a sequence.

tests/command_line/test_prompt_toolkit_coverage.py

  • Added test_shift_enter_newline mirroring the existing test_ctrl_enter_newline pattern exactly: finds the handler via _find_handler, guards with if handler is not None (since the binding is terminal-dependent), and asserts insert_text("\n").

Note on command_line/ modification

Per AGENTS.md, new functionality should prefer the plugin/callback system over direct edits to code_puppy/command_line/. However, there is no hook for key binding registration — callbacks.py has no phase for input bindings, prompt session configuration, or TUI customization. The KeyBindings object is created locally inside get_input_with_combined_completion() and passed directly to PromptSession(), with no extension point for plugins.

This change adds 11 lines to an existing block of key binding definitions (Ctrl+J, Ctrl+Enter, multiline Enter) in the only location where those bindings can live. It follows the established pattern exactly.

Summary by CodeRabbit

New Features

  • Added Shift+Enter keyboard shortcut for inserting newlines in interactive CLI input.

Tests

  • Added test coverage for the Shift+Enter newline functionality.

Add Shift+Enter (s-enter) as an intuitive alternative to Ctrl+J for
inserting newlines in the prompt. Works in modern terminals that support
the CSI u / kitty keyboard protocol (kitty, WezTerm, iTerm2, Windows
Terminal). Silently ignored on older terminals via try/except guard.

- Add s-enter key binding following the established c-enter pattern
- Update help text to show 'Shift+Enter or Ctrl+J' (clarifies alternatives)
- Add test_shift_enter_newline matching existing test conventions
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

Adds support for Shift+Enter as an alternative keyboard shortcut to insert newlines in the CLI interface alongside the existing Ctrl+J binding. Changes include updating help documentation, implementing the new key binding in the prompt toolkit layer, and adding corresponding test coverage.

Changes

Cohort / File(s) Summary
Shift+Enter Newline Support
code_puppy/cli_runner.py
Updated interactive help text to document Shift+Enter as an alternative newline shortcut to Ctrl+J.
Shift+Enter Newline Support
code_puppy/command_line/prompt_toolkit_completion.py
Added new "s-enter" key binding with eager=True that inserts newline via buffer.insert_text(). Wrapped in try/except for terminal-dependent behavior consistency.
Shift+Enter Newline Support
tests/command_line/test_prompt_toolkit_coverage.py
Added test_shift_enter_newline test case mirroring existing newline binding tests to verify s-enter binding correctly inserts newline when present.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 With Shift+Enter, a hop and a bound,
New newlines spring forth without a sound,
No Ctrl+J needed, just shift and press,
The CLI rejoices—rabbit success! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add Shift+Enter as newline insert keybinding' directly and clearly describes the main change—adding a Shift+Enter keybinding for inserting newlines in the interactive prompt.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code_puppy/command_line/prompt_toolkit_completion.py`:
- Around line 645-654: The Shift+Enter keybinding registration using
bindings.add("s-enter", ...) is silently swallowed by the surrounding try/except
because prompt_toolkit 3.0.52 does not recognize "s-enter"; update the code by
either removing the failing handler or replacing it with a terminal-aware
implementation: detect terminal capabilities and register a valid key sequence
(or CSI/kitty protocol sequence) that prompt_toolkit accepts instead of
"s-enter", or skip registration and update help/tests; locate the handler added
via bindings.add("s-enter", eager=True) and the anonymous function def _(event)
that inserts "\n" and change its registration logic accordingly so the binding
is only installed when a supported key name/sequence is validated.

In `@tests/command_line/test_prompt_toolkit_coverage.py`:
- Around line 988-995: The test test_shift_enter_newline currently skips if the
Shift+Enter binding is missing; change it to fail instead by asserting the
handler exists and then invoking it: locate the use of
_find_handler(captured_bindings, "s-enter") in test_shift_enter_newline, replace
the conditional no-op with an explicit assertion that handler is not None (or
raise an AssertionError) so the test fails when the binding isn't registered,
then keep calling handler(event) and asserting
event.app.current_buffer.insert_text.assert_called_with("\n").

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0d7b39d-d113-4f4d-b47c-74a9bd44b7c2

📥 Commits

Reviewing files that changed from the base of the PR and between 5115146 and 699b919.

📒 Files selected for processing (3)
  • code_puppy/cli_runner.py
  • code_puppy/command_line/prompt_toolkit_completion.py
  • tests/command_line/test_prompt_toolkit_coverage.py

Comment on lines +645 to +654
# Shift+Enter for newline (terminal-dependent — works in modern terminals
# like kitty, WezTerm, iTerm2, and Windows Terminal that support CSI u /
# kitty keyboard protocol; silently ignored on older terminals).
try:

@bindings.add("s-enter", eager=True)
def _(event):
event.app.current_buffer.insert_text("\n")
except Exception:
pass
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== prompt_toolkit 3.0.52 supported key names =="
curl -fsSL https://raw.githubusercontent.com/prompt-toolkit/python-prompt-toolkit/3.0.52/src/prompt_toolkit/keys.py \
  | rg -n '"s-enter"|"c-enter"|Shift|Enter = "c-m"|BackTab'

echo
echo "== prompt_toolkit key validation path =="
curl -fsSL https://raw.githubusercontent.com/prompt-toolkit/python-prompt-toolkit/3.0.52/src/prompt_toolkit/key_binding/key_bindings.py \
  | rg -n '_parse_key|Invalid key|Keys\(key\)|len\(key\) != 1'

Repository: mpfaffenberger/code_puppy

Length of output: 1795


Update or remove Shift+Enter handler that silently fails to register.

prompt_toolkit 3.0.52 does not define s-enter as a valid key name. When KeyBindings.add("s-enter", ...) is called, _parse_key() rejects it with ValueError (unrecognized multi-character keys raise Invalid key), and the surrounding try/except silently swallows the error. This means Shift+Enter is never installed, even though the CLI help and test imply it works.

Either remove this handler if Shift+Enter support is not available, or replace it with a terminal-aware implementation that actually works. Update any help text or tests that describe this feature as available.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code_puppy/command_line/prompt_toolkit_completion.py` around lines 645 - 654,
The Shift+Enter keybinding registration using bindings.add("s-enter", ...) is
silently swallowed by the surrounding try/except because prompt_toolkit 3.0.52
does not recognize "s-enter"; update the code by either removing the failing
handler or replacing it with a terminal-aware implementation: detect terminal
capabilities and register a valid key sequence (or CSI/kitty protocol sequence)
that prompt_toolkit accepts instead of "s-enter", or skip registration and
update help/tests; locate the handler added via bindings.add("s-enter",
eager=True) and the anonymous function def _(event) that inserts "\n" and change
its registration logic accordingly so the binding is only installed when a
supported key name/sequence is validated.

Comment on lines +988 to +995
def test_shift_enter_newline(self, captured_bindings):
"""Test shift-enter inserts newline (terminal-dependent)."""
handler = self._find_handler(captured_bindings, "s-enter")
if handler is not None:
event = MagicMock()
handler(event)
event.app.current_buffer.insert_text.assert_called_with("\n")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Make this test fail when the binding is missing.

if handler is not None turns this into a no-op, so CI stays green even when Shift+Enter is never registered.

Proposed fix
     def test_shift_enter_newline(self, captured_bindings):
         """Test shift-enter inserts newline (terminal-dependent)."""
         handler = self._find_handler(captured_bindings, "s-enter")
-        if handler is not None:
-            event = MagicMock()
-            handler(event)
-            event.app.current_buffer.insert_text.assert_called_with("\n")
+        assert handler is not None
+        event = MagicMock()
+        handler(event)
+        event.app.current_buffer.insert_text.assert_called_with("\n")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_shift_enter_newline(self, captured_bindings):
"""Test shift-enter inserts newline (terminal-dependent)."""
handler = self._find_handler(captured_bindings, "s-enter")
if handler is not None:
event = MagicMock()
handler(event)
event.app.current_buffer.insert_text.assert_called_with("\n")
def test_shift_enter_newline(self, captured_bindings):
"""Test shift-enter inserts newline (terminal-dependent)."""
handler = self._find_handler(captured_bindings, "s-enter")
assert handler is not None
event = MagicMock()
handler(event)
event.app.current_buffer.insert_text.assert_called_with("\n")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/command_line/test_prompt_toolkit_coverage.py` around lines 988 - 995,
The test test_shift_enter_newline currently skips if the Shift+Enter binding is
missing; change it to fail instead by asserting the handler exists and then
invoking it: locate the use of _find_handler(captured_bindings, "s-enter") in
test_shift_enter_newline, replace the conditional no-op with an explicit
assertion that handler is not None (or raise an AssertionError) so the test
fails when the binding isn't registered, then keep calling handler(event) and
asserting event.app.current_buffer.insert_text.assert_called_with("\n").

@Arkevius Arkevius marked this pull request as draft March 9, 2026 20:43
@lijunzh
Copy link
Copy Markdown
Contributor

lijunzh commented Mar 18, 2026

@Arkevius Thanks for the PR. This is what I very much wanted too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants