Honor pager bindings for transcript page keys (#50396)

## Why

Transcript scrolling hardcoded `PageUp` and `PageDown` to full-page movement, ignoring customized pager bindings.

## What changed

Resolve unmodified `PageUp` and `PageDown` through the pager's scroll bindings so they can scroll by a full page, half a page, or one row, or remain unbound. Preserve the one-row overlap for full-page scrolling and leave pager typing shortcuts unhandled by the transcript.

## Testing

Add a regression test and snapshot covering default, half-page, single-row, and unbound configurations. Verify that `PageDown` reverses `PageUp` scrolling and that `j` and `Ctrl+u` remain unhandled.

GitOrigin-RevId: 945e99d7370f84f326ab71037be2924dddada32c
This commit is contained in:
Benjamin Carlsson
2026-10-02 18:29:19 +00:00
committed by copyberry
parent f88a6efe43
commit d61c7a824f
3 changed files with 132 additions and 11 deletions
+71
View File
@@ -63,6 +63,77 @@ pub(super) fn text(buffer: &Buffer) -> String {
.join("\n")
}
#[test]
fn page_keys_follow_pager_scroll_bindings() {
let cells = [cell(
(1..=30)
.map(|row| format!("row {row}\n"))
.collect::<String>(),
)];
let mut frames = Vec::new();
for (label, pager, rows) in [
("default", serde_json::json!({}), 7),
(
"half page",
serde_json::json!({
"page_up": [], "page_down": [],
"half_page_up": "page-up", "half_page_down": "page-down"
}),
4,
),
(
"single row",
serde_json::json!({
"page_up": [], "page_down": [],
"scroll_up": "page-up", "scroll_down": "page-down"
}),
1,
),
(
"unbound",
serde_json::json!({"page_up": [], "page_down": []}),
0,
),
] {
let config = serde_json::from_value(serde_json::json!({"pager": pager})).unwrap();
let keymap = crate::keymap::RuntimeKeymap::from_config(&config).unwrap();
let mut view = TranscriptView::default();
view.set_keymap_bindings(&keymap);
let pre_scroll = render(&mut view, &cells, /*width*/ 20, /*height*/ 8);
assert_eq!(
view.handle_key(KeyCode::PageUp.into(), &cells).is_some(),
rows != 0
);
let scrolled = render(&mut view, &cells, /*width*/ 20, /*height*/ 8);
let mut expected = TranscriptView::default();
render(&mut expected, &cells, /*width*/ 20, /*height*/ 8);
expected.scroll(&cells, -rows);
assert_eq!(
scrolled,
render(&mut expected, &cells, /*width*/ 20, /*height*/ 8)
);
frames.push(format!("{label}\n{}", text(&scrolled)));
assert_eq!(
view.handle_key(KeyCode::PageDown.into(), &cells).is_some(),
rows != 0
);
assert_eq!(
render(&mut view, &cells, /*width*/ 20, /*height*/ 8),
pre_scroll
);
for key in [
KeyCode::Char('j').into(),
KeyEvent::new(KeyCode::Char('u'), KeyModifiers::CONTROL),
] {
assert!(view.handle_key(key, &cells).is_none());
}
}
insta::assert_snapshot!(
"page_keys_follow_pager_scroll_bindings",
frames.join("\n\n")
);
}
#[test]
fn terminal_output_disclosure_follows_live_history_and_keymap() {
use crate::exec_cell::CommandOutput;