diff options
| author | user <user@clank> | 2026-08-11 21:30:00 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-11 21:30:00 +0200 |
| commit | 568c45ba47dd27c4fdfcf6bd8303beeb853b93d4 (patch) | |
| tree | 00e3581fdb0ee1779c2b853940d06e770102e8fd | |
| parent | sl0p: keep plugin panes pinned when synthesizing a layout (diff) | |
| download | sl0ppij-568c45ba47dd27c4fdfcf6bd8303beeb853b93d4.tar.gz sl0ppij-568c45ba47dd27c4fdfcf6bd8303beeb853b93d4.tar.xz sl0ppij-568c45ba47dd27c4fdfcf6bd8303beeb853b93d4.zip | |
sl0p: stop hide_default_pane_names blanking the plugin API
`hide_default_pane_names` is a frame-rendering option, but it was applied
inside current_title() -- which is also what fills PaneInfo.title. Every plugin
that lists panes therefore received an empty string for any pane whose program
had never set a title.
For the bundled session manager that is untidy. For a fuzzy pane finder it is
broken: the rows are identical AND unsearchable, so the plugin's whole purpose
stops working.
The blanking moves to the frame, which already had a blank_title concept, and
current_title() goes back to reporting a real title to everything that asks.
Frames still show nothing for an unnamed pane, named panes still show their
name, and PaneInfo reports "Pane #N" again.
Changes a value rather than adding a field, so it needs no protobuf
regeneration.
Only observable without a shell that sets a title: verified by disabling the
zsh hook, where the pane finder previously listed three identical blank rows
and now lists Pane #1/#2/#3. That masking is also why this went unnoticed --
and why I flagged it twice without confirming it was actually reachable.
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | zellij-server/src/panes/terminal_pane.rs | 39 | ||||
| -rw-r--r-- | zellij-server/src/tab/unit/tab_integration_tests.rs | 68 |
3 files changed, 91 insertions, 21 deletions
@@ -147,6 +147,11 @@ Two deliberate behaviours: It protected little in any case: expanding applies `BASE` or a grid, so a manual arrangement is replaced by a collapse/expand round trip either way. +Note this affects **frame rendering only**. Panes still report their `Pane #N` +placeholder to the plugin API, so anything that lists panes — the session +manager, a pane finder — can still label and search them. Blanking it there too +left such plugins with rows they could neither identify nor fuzzy-match. + ### Capping the stacked-pane picker A stack reserves one row per member plus a padding row, so a deep stack spends diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 735bdc9d..8085e6be 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -583,7 +583,11 @@ impl Pane for TerminalPane { } else { self.current_title() }; - let pane_title = if frame_params.blank_title { + let pane_title = if frame_params.blank_title + || (self.has_placeholder_title() && self.style.hide_default_pane_names) + { + // nothing named this pane: draw no title rather than "Pane #3". + // Read from style at render time so the option hot-reloads. String::new() } else if let Some(text_color_override) = self .pane_frame_color_override @@ -1219,30 +1223,27 @@ impl Pane for TerminalPane { fn current_title(&self) -> String { // precedence: user-given name > title set by the running program > // the "Pane #N" placeholder + // + // Deliberately does NOT consult `hide_default_pane_names`. That is a + // frame-rendering option, and blanking here leaked it into everything + // that asks a pane for its title -- including PaneInfo, so every plugin + // listing panes (the pane finder, the session manager) got empty rows + // it could neither label nor search. The frame does its own blanking; + // see `has_placeholder_title`. if !self.pane_name.is_empty() { return self.pane_name.to_owned(); } if let Some(program_title) = self.grid.title.as_deref() { return program_title.into(); } - if self.pane_title_is_placeholder && self.style.hide_default_pane_names { - // nothing has named this pane: show no title at all rather than - // "Pane #3". Read from style at render time so the option - // hot-reloads and the placeholder is still there if it is turned - // back off. - return String::new(); - } self.pane_title.clone() } fn stack_list_entry_label(&self) -> String { - // `hide_default_pane_names` deliberately does NOT apply here. - // - // In a frame, an empty title is fine: the pane is still visible, sized - // and positioned, so you can tell what it is by looking at it. In the - // stack list the label is the ENTIRE affordance -- blanking it leaves a - // row that cannot be identified or distinguished from its neighbours. - // So the stack list falls back to the "Pane #N" placeholder even when - // frames are configured to hide it. + // `hide_default_pane_names` does not reach current_title() any more, so + // this gets the "Pane #N" placeholder rather than an empty string. Kept + // as a floor anyway: in a frame an empty title is merely clean, but in + // the stack list the label is the ENTIRE affordance, and a blank row + // cannot be identified or told apart from its neighbours. let title = match self.current_title() { title if title.is_empty() => self.pane_title.clone(), title => title, @@ -1451,6 +1452,12 @@ impl Pane for TerminalPane { } impl TerminalPane { + /// True while nothing has named this pane, so its title is still the + /// auto-generated "Pane #N". Inherent rather than part of the Pane trait: + /// only terminal panes have a placeholder title to hide. + fn has_placeholder_title(&self) -> bool { + self.pane_name.is_empty() && self.grid.title.is_none() && self.pane_title_is_placeholder + } fn title_with_bell_indicator(&self, title: String) -> String { if self.has_bell_notification { format!("{} [!]", title) diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index d59e86b6..eb1df250 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -15972,13 +15972,13 @@ fn sl0p_stack_list_entry_keeps_a_label_when_default_pane_names_are_hidden() { ); for (pane_id, pane) in tab.tiled_panes.panes.iter() { - // precondition: the option really is blanking the frame title, or this - // test would pass for the wrong reason + // The option no longer reaches current_title(): it is applied by the + // frame at render time, so everything that ASKS a pane for its title + // still gets a real one. See the PaneInfo test below. assert_eq!( pane.current_title(), - "", - "precondition: {:?} should have a blank FRAME title while \ - hide_default_pane_names is on", + format!("Pane #{}", pane_id_index(pane_id)), + "{:?} should still report its placeholder title", pane_id ); let label = pane.stack_list_entry_label(); @@ -16746,3 +16746,61 @@ fn sl0p_stack_list_style_changes_the_cell_width_the_layout_reserves() { minimal.cells.len() ); } + +#[cfg(test)] +fn pane_id_index(pane_id: &PaneId) -> u32 { + match pane_id { + PaneId::Terminal(id) => *id, + PaneId::Plugin(id) => *id, + } +} + +#[test] +fn sl0p_hiding_default_pane_names_does_not_blank_the_plugin_api() { + // REGRESSION: `hide_default_pane_names` is a frame-rendering option, but it + // used to be applied inside current_title() -- which is also what fills + // PaneInfo.title. Every plugin listing panes (the pane finder, the session + // manager) therefore got empty rows it could neither label nor fuzzy-match, + // for any pane whose program never set a title. + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + tab.new_pane( + PaneId::Terminal(2), + None, + None, + false, + true, + NewPanePlacement::default(), + Some(client_id), + None, + ) + .unwrap(); + tab.update_frame_layout( + zellij_utils::data::PaneTitleAlignment::Left, + 0, + 0, + zellij_utils::data::PanePadding::default(), + 0, + true, // hide_default_pane_names + ); + + let current_pane_group = HashMap::new(); + for (pane_id, pane) in tab.tiled_panes.panes.iter() { + let pane_info = super::pane_info_for_pane(pane_id, pane, ¤t_pane_group); + assert!( + !pane_info.title.trim().is_empty(), + "{:?} reported an empty title to the plugin API, so a pane list \ + cannot label or search it", + pane_id + ); + assert_eq!( + pane_info.title, + format!("Pane #{}", pane_id_index(pane_id)), + "the API should report the real placeholder title" + ); + } +} |
