Skip to content

Commit cc7c696

Browse files
authored
feat: add configurable pane screen and scrollback clearing (#4383)
1 parent 3f2a6e7 commit cc7c696

27 files changed

Lines changed: 403 additions & 1 deletion

File tree

‎docs/next/api/herdr-api.schema.json‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5707,6 +5707,22 @@
57075707
],
57085708
"type": "object"
57095709
},
5710+
{
5711+
"properties": {
5712+
"method": {
5713+
"const": "pane.clear",
5714+
"type": "string"
5715+
},
5716+
"params": {
5717+
"$ref": "#/schemas/request/$defs/PaneTarget"
5718+
}
5719+
},
5720+
"required": [
5721+
"method",
5722+
"params"
5723+
],
5724+
"type": "object"
5725+
},
57105726
{
57115727
"properties": {
57125728
"method": {

‎docs/next/website/src/data/config-reference.json‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,12 @@
697697
"default": "\"prefix+e\"",
698698
"description": "Open the focused pane scrollback in $EDITOR."
699699
},
700+
{
701+
"key": "keys.clear_pane",
702+
"type": "keybinding",
703+
"default": "unset",
704+
"description": "Clear the focused pane screen and scrollback, keeping the current cursor line and its visible soft-wrapped rows. Sends no input to the running program and does nothing on the alternate screen (for example, Vim). Unset by default; for example, clear_pane = \"prefix+ctrl+k\"."
705+
},
700706
{
701707
"key": "keys.copy_mode",
702708
"type": "keybinding",

‎src/api/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub(crate) fn request_changes_ui(request: &Request) -> bool {
5959
| Method::PaneFocusDirection(_)
6060
| Method::PaneResize(_)
6161
| Method::PaneScroll(_)
62+
| Method::PaneClear(_)
6263
| Method::PaneEditScrollback(_)
6364
| Method::PaneFocus(_)
6465
| Method::PaneInputSet(_)

‎src/api/schema.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ pub enum Method {
165165
PaneResize(PaneResizeParams),
166166
#[serde(rename = "pane.scroll")]
167167
PaneScroll(PaneScrollParams),
168+
#[serde(rename = "pane.clear")]
169+
PaneClear(PaneTarget),
168170
#[serde(rename = "pane.edit_scrollback")]
169171
PaneEditScrollback(PaneTarget),
170172
#[serde(rename = "pane.selection.read")]

‎src/api/server.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ pub(crate) fn api_method_name(method: &Method) -> &'static str {
457457
Method::PaneFocusDirection(_) => "pane.focus_direction",
458458
Method::PaneResize(_) => "pane.resize",
459459
Method::PaneScroll(_) => "pane.scroll",
460+
Method::PaneClear(_) => "pane.clear",
460461
Method::PaneEditScrollback(_) => "pane.edit_scrollback",
461462
Method::PaneSelectionRead(_) => "pane.selection.read",
462463
Method::PaneCopyMotion(_) => "pane.copy_motion",

‎src/app/api.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,7 @@ impl App {
11011101
}
11021102
Method::PaneResize(params) => return self.handle_pane_resize(request.id, params),
11031103
Method::PaneScroll(params) => return self.handle_pane_scroll(request.id, params),
1104+
Method::PaneClear(target) => return self.handle_pane_clear(request.id, target),
11041105
Method::PaneEditScrollback(target) => {
11051106
return self.handle_pane_edit_scrollback(request.id, target);
11061107
}

‎src/app/api/panes.rs‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,22 @@ impl App {
167167
encode_success(id, ResponseResult::PaneInfo { pane })
168168
}
169169

170+
pub(super) fn handle_pane_clear(&mut self, id: String, target: PaneTarget) -> String {
171+
let Some((ws_idx, pane_id)) = self.parse_pane_id(&target.pane_id) else {
172+
return pane_not_found(id, &target.pane_id);
173+
};
174+
let Some(runtime) =
175+
self.state
176+
.runtime_for_pane_in_workspace(&self.terminal_runtimes, ws_idx, pane_id)
177+
else {
178+
return pane_not_found(id, &target.pane_id);
179+
};
180+
match runtime.clear_screen() {
181+
Ok(()) => encode_success(id, ResponseResult::Ok {}),
182+
Err(err) => encode_error(id, "pane_clear_failed", err.to_string()),
183+
}
184+
}
185+
170186
pub(super) fn handle_pane_scroll(&mut self, id: String, params: PaneScrollParams) -> String {
171187
let Some((ws_idx, pane_id)) = self.parse_pane_id(&params.pane_id) else {
172188
return pane_not_found(id, &params.pane_id);
@@ -2349,6 +2365,26 @@ mod tests {
23492365
assert!(rx.try_recv().is_err());
23502366
}
23512367

2368+
#[tokio::test]
2369+
async fn api_clear_pane_mutates_endpoint_owned_history() {
2370+
let (mut app, public_pane_id, pane_id) = app_with_scrollback_runtime();
2371+
let request = crate::api::schema::Request {
2372+
id: "clear".into(),
2373+
method: crate::api::schema::Method::PaneClear(PaneTarget {
2374+
pane_id: public_pane_id,
2375+
}),
2376+
};
2377+
assert!(crate::api::request_changes_ui(&request));
2378+
let response = app.handle_api_request(request);
2379+
let success: SuccessResponse = serde_json::from_str(&response).unwrap();
2380+
assert_eq!(success.result, ResponseResult::Ok {});
2381+
let runtime = app
2382+
.state
2383+
.runtime_for_pane_in_workspace(&app.terminal_runtimes, 0, pane_id)
2384+
.unwrap();
2385+
assert_eq!(runtime.scroll_metrics().unwrap().max_offset_from_bottom, 0);
2386+
}
2387+
23522388
#[tokio::test]
23532389
async fn api_pane_get_exposes_scroll_metrics() {
23542390
let (mut app, public_pane_id, pane_id) = app_with_scrollback_runtime();

‎src/client/shell/actions.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,9 @@ impl ClientShellState {
10641064
pane_id: focused_pane,
10651065
mode: PaneZoomMode::Toggle,
10661066
})),
1067+
KeybindAction::ClearPane => Some(Method::PaneClear(PaneTarget {
1068+
pane_id: focused_pane?,
1069+
})),
10671070
KeybindAction::EditScrollback => Some(Method::PaneEditScrollback(PaneTarget {
10681071
pane_id: focused_pane?,
10691072
})),

‎src/client/shell/tests/popup_focus_projection.rs‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,25 @@ fn pane_scrollbar_track_and_thumb_use_stable_endpoint_scroll_requests() {
884884
assert!(state.chrome_drag.is_none());
885885
}
886886

887+
#[test]
888+
fn clear_pane_binding_targets_the_focused_endpoint_pane() {
889+
let mut state = ClientShellState::new(ClientShellConfig::from_config(&Config::default()));
890+
state.set_snapshot(Box::new(snapshot()));
891+
state.set_pane_surface(surface());
892+
let mut input = ClientShellInput::default();
893+
state.record_binding(
894+
crate::input::KeybindMatch::Action(crate::input::KeybindAction::ClearPane),
895+
&mut input,
896+
);
897+
assert!(input.requests.is_empty());
898+
assert!(matches!(
899+
&input.actions[..],
900+
[ClientShellAction::Endpoint { request, .. }]
901+
if matches!(&request.method, crate::api::schema::Method::PaneClear(target)
902+
if target.pane_id == "pane_1")
903+
));
904+
}
905+
887906
#[test]
888907
fn edit_scrollback_binding_targets_the_focused_endpoint_pane() {
889908
let mut state = ClientShellState::new(ClientShellConfig::from_config(&Config::default()));

‎src/config/keybinds.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ pub struct Keybinds {
347347
pub close_tab: ActionKeybinds,
348348
pub rename_pane: ActionKeybinds,
349349
pub edit_scrollback: ActionKeybinds,
350+
pub clear_pane: ActionKeybinds,
350351
pub copy_mode: ActionKeybinds,
351352
pub focus_pane_left: ActionKeybinds,
352353
pub focus_pane_down: ActionKeybinds,
@@ -515,6 +516,7 @@ impl Config {
515516
close_tab: empty_action!(),
516517
rename_pane: empty_action!(),
517518
edit_scrollback: empty_action!(),
519+
clear_pane: empty_action!(),
518520
copy_mode: empty_action!(),
519521
focus_pane_left: empty_action!(),
520522
focus_pane_down: empty_action!(),
@@ -662,6 +664,7 @@ impl Config {
662664
apply_action!(keybinds.close_tab, close_tab, source);
663665
apply_action!(keybinds.rename_pane, rename_pane, source);
664666
apply_action!(keybinds.edit_scrollback, edit_scrollback, source);
667+
apply_action!(keybinds.clear_pane, clear_pane, source);
665668
apply_action!(keybinds.copy_mode, copy_mode, source);
666669
apply_action!(keybinds.focus_pane_left, focus_pane_left, source);
667670
apply_action!(keybinds.focus_pane_down, focus_pane_down, source);

0 commit comments

Comments
 (0)