mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-18 14:49:18 +00:00
* feat(cli): readline Ctrl+P/N for history and selection navigation
Adds GNU-readline-style Ctrl+P (previous) and Ctrl+N (next) shortcuts
to the qwen-code TUI so users coming from bash/zsh, Emacs, or Claude
Code feel at home. The change has three orthogonal behavior groups:
1. Input prompt, history-versus-line-motion two-step edge
Ctrl+P / Ctrl+N and the arrow keys behave identically and apply a
two-step edge transition that matches GNU readline and Claude Code:
inside a multi-line buffer they move the cursor between visual
rows; on the top row with the cursor away from column 0 the first
Up press snaps the cursor to column 0 without changing history, and
only the second press walks one entry back. The mirror rule holds
for Down at the last row (snap to end of line, then advance). After
navigateUp the buffer is parked at offset 0 (the "start of older
entry" landing position); after navigateDown setText's default
end-of-text positioning keeps the cursor at the end. The same
two-step rule applies to single-line buffers so the
reverse-direction case the issue called out works: pressing Ctrl+N
immediately after Ctrl+P loaded a single-line older entry (cursor
at col 0) first snaps the cursor to end-of-line, and only the next
Ctrl+N moves forward through the history. Bare k/j inside the
input prompt remain ordinary typed letters — the vim aliases are
selection-list shortcuts, not text-editing ones.
2. Selection lists: arrows, k/j, and Ctrl+P/N are interchangeable
A new pair of Command bindings, SELECTION_UP and SELECTION_DOWN, is
wired into the shared useSelectionList hook and every dialog that
used to hand-roll an "up/down arrow only" or "up/k arrow + vim
only" navigation check. Covered surfaces: the main selection-list
hook itself, the MCP / extensions / agents / hooks / background-
tasks / rewind / plugin-choice / ask-user-question dialogs, the
memory dialog (both its file list and the auto-memory and
auto-cleanup toggle panel above the list), the settings dialog
list (with the in-place value editor's "block other keys while
editing" guard preserved), and the manage-models dialog's top
tabs row. The auth-provider wizard's Advanced Config focus rows
and the resume-session picker's cross-mode arrows are extended
with the readline Ctrl+P / Ctrl+N synonyms while keeping their
existing arrow-key and (for the session picker) vim k/j semantics
intact.
3. Selection surfaces that wrap an active text input
AskUserQuestionDialog's "Other / type a custom answer" field,
manage-models' search input, the resume-session picker's search
field, and the auth-wizard's Context-window number input all
coexist with the selection list on the same screen. In those
surfaces typing k or j has to land in the text buffer, not scroll
the surrounding list. The fix is to scope the input-aware handler
to unambiguous non-letter shortcuts only — arrow keys plus
readline-style Ctrl+P / Ctrl+N escape the text field, while bare
letters (including k / j / p / n) are delivered to the active
input. The keyBinding-level fix that backs this is the
`{ key: 'k', ctrl: false }` / `{ key: 'j', ctrl: false }` clauses
on SELECTION_UP / SELECTION_DOWN, which prevent Ctrl+K from
accidentally matching SELECTION_UP and thereby firing both the
list-up handler and the KILL_LINE_RIGHT handler in the same
keystroke (the P0 finding the quality-gate review surfaced).
Focus-traversal tokens (the agent tab bar and the background-task
pill) and chord shortcuts (Ctrl+Shift+Up/Down for embedded-shell
history) are deliberately left untouched because their existing
"any printable letter yields focus back to the composer" UX would
break under the new vim-style letter bindings, and the Help
viewer's scroll is a viewer rather than a selection list and is
out of this PR's scope.
Documentation: docs/users/reference/keyboard-shortcuts.md is updated
so the Ctrl+P / Ctrl+N entries describe the two-step edge rule and
the radio-button-select table mentions the new k/j and Ctrl+P/N
aliases. Per-dialog on-screen hints (which still read "↑↓ to
navigate") are intentionally not touched so the i18n string surface
stays unchanged; the global reference doc is the authoritative source
for the new shortcuts.
Tests:
- packages/cli/src/ui/keyMatchers.test.ts adds positive cases
covering ↑ / ↓ / bare k / bare j / Ctrl+P / Ctrl+N matching
SELECTION_UP / SELECTION_DOWN and negative cases asserting that
Ctrl+K and Ctrl+J do NOT match (the conflict guard).
- packages/cli/src/ui/components/InputPrompt.test.tsx adds a
"two-step edge transition for history navigation" describe block
with four cases: a mid-line Ctrl+P snaps to col 0 without invoking
navigateUp; an at-col-0 Ctrl+P does invoke navigateUp and then
parks the cursor via moveToOffset(0); a not-at-end Ctrl+N snaps to
end-of-line without invoking navigateDown; and arrow Up obeys the
same rule as Ctrl+P for keyboard-parity. The test file's mock
buffer's setText was also corrected to mirror the real buffer's
"cursor lands at the end of the new text" semantic so the cursor
field is internally consistent during keypress assertions; the
small InputPrompt render-frame snapshot in the same file's
__snapshots__/ directory was regenerated to reflect the now-
accurate cursor render position. Three pre-existing arrow-key
navigation tests were updated to pre-position the mock cursor at
the relevant edge before pressing the arrow, because the new
two-step rule means the first arrow press at a non-edge position
is a cursor snap, not a history step. Multi-line cursor-between-
rows movement is covered indirectly by the keyBinding-level
matcher tests plus the end-to-end manual demo plan.
The work landed in three rounds against the planner's gate: round 1
added the unified SELECTION_UP / SELECTION_DOWN Command binding and
the cursor-first dispatch in the input prompt; round 2 picked up the
quality-gate review's P0 (the Ctrl+K double-fire in the "Other"
custom-input field) and the user's hand-test feedback on the missing
two-step edge in the reverse direction plus the MemoryDialog
top-panel sections that weren't wired through SELECTION_*; round 3
swept the remaining adjacent dialogs (SettingsDialog list,
ManageModelsDialog tabs and search transitions, ProviderSetupSteps
advancedConfig, useSessionPicker's cross-mode arrows) so the
keyboard model is uniform across the TUI.
The original issue also asks for Meta+B / Meta+F word motion and
smarter Ctrl+H token-aware backspace among other readline
conveniences. The user explicitly scoped this PR down to Ctrl+P /
Ctrl+N at the planner approval gate; the remaining wish-list items
are deferred to follow-up issues.
Closes #3821
* docs(cli): refine Ctrl+P/N input-history rows; fix Ctrl+J in selection-list comment
Both items came from a non-blocking COMMENTED review on PR #4082
(https://github.com/QwenLM/qwen-code/pull/4082#pullrequestreview-4271527787),
flagging two polish points in the readline Ctrl+P/Ctrl+N feature the parent
commit `feat(cli): readline Ctrl+P/N for history and selection navigation`
(f66427b) introduced.
The `Up Arrow`, `Down Arrow`, `Ctrl+P`, and `Ctrl+N` rows of the Input
Prompt table in `docs/users/reference/keyboard-shortcuts.md` are reworded
to describe the three-phase keystroke sequence the implementation walks
through — an intra-buffer visual-row step (a no-op in a single-line
buffer, where there's exactly one visual row), a column-edge snap when
the cursor reaches the buffer's first or last visual row with the
cursor not already at column 0 (for the up-direction pair) or
end-of-line (for the down-direction pair), and the readline-style
previous-history or next-history walk on the press after the snap. The
reviewer specifically pointed out that the prior wording described
single-line input as "navigates the input history directly", which no
longer matches the post-PR-#4082 behavior: single-line input also goes
through the snap-then-walk two-press rule (the snap is a no-op when
the cursor is already at the line's edge column, in which case the
keystroke does the history walk on its first press). The new sentence
covers the single-line and multi-line cases in one shape — single-line
is the degenerate zero-row-walk-prefix instance of the same rule. The
up-direction text is shared verbatim between the `Up Arrow` row (L31)
and the `Ctrl+P` row (L43), and the down-direction text between the
`Down Arrow` row (L27) and the `Ctrl+N` row (L42), so the keyboard-
parity alias relationship is signaled by source-side text duplication
rather than a prose cross-reference. The Input Prompt table's 234-byte
canonical row width (the separator row's `| <50-dash> | <177-dash> |`
template, which sets the column-1 and column-2 source-side widths the
file's existing untouched rows already align to) is preserved by
trailing-ASCII-space padding inside the description column.
The comment above `[Command.SELECTION_UP]` and `[Command.SELECTION_DOWN]`
in `packages/cli/src/config/keyBindings.ts` previously read
// Selection list navigation — up/k/Ctrl+P move selection up; down/j/Ctrl+N move selection down
// ctrl: false on k/j ensures Ctrl+K (kill-line) and Ctrl+N (history-down) are not captured here
The `Ctrl+N` half of the second line is wrong: `Ctrl+N` is intentionally
matched here as the selection-down readline alias — the
`{ key: 'n', ctrl: true }` entry in the `SELECTION_DOWN` array literal
directly below the comment, mirroring the input-prompt-side
`[Command.HISTORY_DOWN]: [{ key: 'n', ctrl: true }]` binding at L134 of
the same file. The Ctrl-modified key the bare-letter `k` and `j`
matchers actually guard against — the one already bound elsewhere
whose double-match with the bare-letter selection-key the `ctrl: false`
opt-out is preventing — is `Ctrl+J`, the ASCII line-feed (0x0A) encoding
of the Enter family that appears as `{ key: 'j', ctrl: true }` inside
the four-alternative `[Command.NEWLINE]` array a few lines below. The
corrected one-liner is
// Selection-list nav: arrows + k/j + Ctrl+P/Ctrl+N
// ctrl: false on bare k/j skips Ctrl+K and Ctrl+J
in the same terse no-trailing-period section-label style as the file's
adjacent `// Screen control` (L129), `// History navigation` (L132),
`// Auto-completion` (L213, post-edit numbering), and `// Text input`
(L219) header comments. A 64-line block-comment that earlier in the
review-fix cycle wrapped this same correct fact in dispatch-broadcast-
model prose plus `keyMatchers.test.ts` backreferences was condensed to
those two lines for cell-budget consistency with the rest of the file.
No code behavior change. The local verification surface the reviewer
named at the bottom of the review summary stays green: from
`packages/cli`,
npx vitest run \
src/ui/keyMatchers.test.ts \
src/config/keyBindings.test.ts \
src/ui/components/InputPrompt.test.tsx
runs 178 cases with 177 passed and one unrelated skip (the
implementation file `InputPrompt.tsx`'s feature flag for the keyboard-
queue-input-editing case that was already skipped on the parent commit),
including all four cases inside the `InputPrompt > two-step edge
transition for history navigation` describe-block — `Ctrl+P with cursor
mid-line snaps to col 0 without touching history`, `Ctrl+N with cursor
not at end-of-line snaps to end without touching history`, `Ctrl+P at
col 0 walks history and parks the cursor at offset 0`, and `arrow Up
applies the same two-step rule as Ctrl+P (snap before navigate)`. Those
four test-case names are the implementation-side anchors the new docs
wording verbally mirrors. `npx tsc --noEmit -p .` in the same package
directory reports zero diagnostics.
* fix(cli): align readline history shortcuts with dialogs
* test(cli): cover readline navigation aliases
* fix(cli): guard readline shortcuts in dialog inputs
* test(cli): cover readline aliases in more dialogs
11 KiB
11 KiB
Qwen Code Keyboard Shortcuts
This document lists the available keyboard shortcuts in Qwen Code.
General
| Shortcut | Description |
|---|---|
Esc |
Close dialogs and suggestions. |
Ctrl+C |
Cancel the ongoing request and clear the input. Press twice to exit the application. |
Ctrl+D |
Exit the application if the input is empty. Press twice to confirm. |
Ctrl+L |
Clear the screen. |
Ctrl+O |
Toggle compact mode (hide/show tool output and thinking). |
Ctrl+S |
Allows long responses to print fully, disabling truncation. Use your terminal's scrollback to view the entire output. |
Ctrl+T |
Toggle the display of tool descriptions. |
Ctrl+B |
While a foreground shell command is running: promote it to a background task. The child keeps running, the agent's turn unblocks, and the shell appears in /tasks + the Background tasks dialog. No-op when no shell is executing — Ctrl+B then falls through to its prompt-area binding (cursor-left). |
Alt/Option+M |
Toggle Markdown output between rich rendered previews and raw/source mode. On macOS, the terminal must send Option as Meta. |
Shift+Tab (Tab on Windows) |
Cycle approval modes (plan → default → auto-edit → yolo) |
Input Prompt
| Shortcut | Description |
|---|---|
! |
Toggle shell mode when the input is empty. |
? |
Toggle keyboard shortcuts display when the input is empty. |
\ (at end of line) + Enter |
Insert a newline. |
Down Arrow |
Row down, then snap to end, then history next. |
Enter |
Submit the current prompt. |
Meta+Delete / Ctrl+Delete |
Delete the word to the right of the cursor. |
Tab |
Autocomplete the current suggestion if one exists. |
Up Arrow |
Row up, then snap to start, then history prev. |
Ctrl+A / Home |
Move the cursor to the beginning of the line. |
Ctrl+B / Left Arrow |
Move the cursor one character to the left. |
Ctrl+C |
Clear the input prompt |
Esc (double press) |
Clear the input prompt. |
Ctrl+D / Delete |
Delete the character to the right of the cursor. |
Ctrl+E / End |
Move the cursor to the end of the line. |
Ctrl+F / Right Arrow |
Move the cursor one character to the right. |
Ctrl+H / Backspace |
Delete the character to the left of the cursor. |
Ctrl+K |
Delete from the cursor to the end of the line. |
Ctrl+Left Arrow / Meta+Left Arrow / Meta+B |
Move the cursor one word to the left. |
Ctrl+N |
Row down, then snap to end, then history next. |
Ctrl+P |
Row up, then snap to start, then history prev. |
Ctrl+R |
Reverse search through input/shell history. |
Ctrl+Y |
Retry the last failed request. |
Ctrl+Right Arrow / Meta+Right Arrow / Meta+F |
Move the cursor one word to the right. |
Ctrl+U |
Delete from the cursor to the beginning of the line. |
Ctrl+V (Windows: Alt+V) |
Paste clipboard content. If the clipboard contains an image, it will be saved and a reference to it will be inserted in the prompt. |
Ctrl+W / Meta+Backspace / Ctrl+Backspace |
Delete the word to the left of the cursor. |
Ctrl+X / Meta+Enter |
Open the current input in an external editor. |
Suggestions
| Shortcut | Description |
|---|---|
Down Arrow |
Navigate down through the suggestions. |
Tab / Enter |
Accept the selected suggestion. |
Up Arrow |
Navigate up through the suggestions. |
Radio Button Select
| Shortcut | Description |
|---|---|
Down Arrow / j / Ctrl+N |
Move selection down. |
Enter |
Confirm selection. |
Up Arrow / k / Ctrl+P |
Move selection up. |
1-9 |
Select an item by its number. |
| (multi-digit) | For items with numbers greater than 9, press the digits in quick succession to select the corresponding item. |
IDE Integration
| Shortcut | Description |
|---|---|
Ctrl+G |
See context CLI received from IDE |