feat(plugins): add tab history to the SessionUpdate Event (#4014)

* feat(plugins): add tab-history to the SessionInfo

* update snapshots

* style(fmt): rustfmt

* update changelog
This commit is contained in:
Aram Drevekenin 2025-02-21 19:34:38 +01:00 committed by GitHub
parent 834693f0c2
commit 04be2646ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 106 additions and 10 deletions

View file

@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* fix(clipboard): various clipboard message fixes (https://github.com/zellij-org/zellij/pull/4009) * fix(clipboard): various clipboard message fixes (https://github.com/zellij-org/zellij/pull/4009)
* fix(logging): fix segfault in chrono crate (https://github.com/zellij-org/zellij/pull/4010) * fix(logging): fix segfault in chrono crate (https://github.com/zellij-org/zellij/pull/4010)
* fix(floating-panes): reset damage in swap layouts when closing the last pane (https://github.com/zellij-org/zellij/pull/4012) * fix(floating-panes): reset damage in swap layouts when closing the last pane (https://github.com/zellij-org/zellij/pull/4012)
* feat(plugins): add tab_history to the session metadata (https://github.com/zellij-org/zellij/pull/4014)
## [0.41.2] - 2024-11-19 ## [0.41.2] - 2024-11-19
* fix(input): keypresses not being identified properly with kitty keyboard protocol in some terminals (https://github.com/zellij-org/zellij/pull/3725) * fix(input): keypresses not being identified properly with kitty keyboard protocol in some terminals (https://github.com/zellij-org/zellij/pull/3725)

View file

@ -1648,6 +1648,7 @@ impl Screen {
is_current_session: true, is_current_session: true,
available_layouts, available_layouts,
plugins: Default::default(), // these are filled in by the wasm thread plugins: Default::default(), // these are filled in by the wasm thread
tab_history: self.tab_history.clone(),
}; };
self.bus self.bus
.senders .senders

View file

@ -316,6 +316,16 @@ pub struct SessionManifest {
pub available_layouts: ::prost::alloc::vec::Vec<LayoutInfo>, pub available_layouts: ::prost::alloc::vec::Vec<LayoutInfo>,
#[prost(message, repeated, tag = "7")] #[prost(message, repeated, tag = "7")]
pub plugins: ::prost::alloc::vec::Vec<PluginInfo>, pub plugins: ::prost::alloc::vec::Vec<PluginInfo>,
#[prost(message, repeated, tag = "8")]
pub tab_history: ::prost::alloc::vec::Vec<ClientTabHistory>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ClientTabHistory {
#[prost(uint32, tag = "1")]
pub client_id: u32,
#[prost(uint32, repeated, tag = "2")]
pub tab_history: ::prost::alloc::vec::Vec<u32>,
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]

View file

@ -1554,6 +1554,7 @@ pub struct SessionInfo {
pub is_current_session: bool, pub is_current_session: bool,
pub available_layouts: Vec<LayoutInfo>, pub available_layouts: Vec<LayoutInfo>,
pub plugins: BTreeMap<u32, PluginInfo>, pub plugins: BTreeMap<u32, PluginInfo>,
pub tab_history: BTreeMap<ClientId, Vec<usize>>,
} }
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]

View file

@ -4462,6 +4462,29 @@ impl SessionInfo {
}) })
.ok_or("Failed to parse available_layouts")?; .ok_or("Failed to parse available_layouts")?;
let is_current_session = name == current_session_name; let is_current_session = name == current_session_name;
let mut tab_history = BTreeMap::new();
if let Some(kdl_tab_history) = kdl_document.get("tab_history").and_then(|p| p.children()) {
for client_node in kdl_tab_history.nodes() {
if let Some(client_id) = client_node.children().and_then(|c| {
c.get("id")
.and_then(|c| c.entries().iter().next().and_then(|e| e.value().as_i64()))
}) {
let mut history = vec![];
if let Some(history_entries) = client_node
.children()
.and_then(|c| c.get("history"))
.map(|h| h.entries())
{
for entry in history_entries {
if let Some(entry) = entry.value().as_i64() {
history.push(entry as usize);
}
}
}
tab_history.insert(client_id as u16, history);
}
}
}
Ok(SessionInfo { Ok(SessionInfo {
name, name,
tabs, tabs,
@ -4470,6 +4493,7 @@ impl SessionInfo {
is_current_session, is_current_session,
available_layouts, available_layouts,
plugins: Default::default(), // we do not serialize plugin information plugins: Default::default(), // we do not serialize plugin information
tab_history,
}) })
} }
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
@ -4510,11 +4534,30 @@ impl SessionInfo {
} }
available_layouts.set_children(available_layouts_children); available_layouts.set_children(available_layouts_children);
let mut tab_history = KdlNode::new("tab_history");
let mut tab_history_children = KdlDocument::new();
for (client_id, client_tab_history) in &self.tab_history {
let mut client_document = KdlDocument::new();
let mut client_node = KdlNode::new("client");
let mut id = KdlNode::new("id");
id.push(*client_id as i64);
client_document.nodes_mut().push(id);
let mut history = KdlNode::new("history");
for entry in client_tab_history {
history.push(*entry as i64);
}
client_document.nodes_mut().push(history);
client_node.set_children(client_document);
tab_history_children.nodes_mut().push(client_node);
}
tab_history.set_children(tab_history_children);
kdl_document.nodes_mut().push(name); kdl_document.nodes_mut().push(name);
kdl_document.nodes_mut().push(tabs); kdl_document.nodes_mut().push(tabs);
kdl_document.nodes_mut().push(panes); kdl_document.nodes_mut().push(panes);
kdl_document.nodes_mut().push(connected_clients); kdl_document.nodes_mut().push(connected_clients);
kdl_document.nodes_mut().push(available_layouts); kdl_document.nodes_mut().push(available_layouts);
kdl_document.nodes_mut().push(tab_history);
kdl_document.fmt(); kdl_document.fmt();
kdl_document.to_string() kdl_document.to_string()
} }
@ -5067,6 +5110,7 @@ fn serialize_and_deserialize_session_info_with_data() {
LayoutInfo::File("layout3".to_owned()), LayoutInfo::File("layout3".to_owned()),
], ],
plugins: Default::default(), plugins: Default::default(),
tab_history: Default::default(),
}; };
let serialized = session_info.to_string(); let serialized = session_info.to_string();
let deserealized = SessionInfo::from_string(&serialized, "not this session").unwrap(); let deserealized = SessionInfo::from_string(&serialized, "not this session").unwrap();

View file

@ -1,6 +1,6 @@
--- ---
source: zellij-utils/src/kdl/mod.rs source: zellij-utils/src/kdl/mod.rs
assertion_line: 2459 assertion_line: 5000
expression: serialized expression: serialized
--- ---
name "" name ""
@ -11,4 +11,6 @@ panes {
connected_clients 0 connected_clients 0
available_layouts { available_layouts {
} }
tab_history {
}

View file

@ -1,6 +1,6 @@
--- ---
source: zellij-utils/src/kdl/mod.rs source: zellij-utils/src/kdl/mod.rs
assertion_line: 5070 assertion_line: 5111
expression: serialized expression: serialized
--- ---
name "my session name" name "my session name"
@ -95,4 +95,6 @@ available_layouts {
layout2 source="built-in" layout2 source="built-in"
layout3 source="file" layout3 source="file"
} }
tab_history {
}

View file

@ -257,6 +257,12 @@ message SessionManifest {
bool is_current_session = 5; bool is_current_session = 5;
repeated LayoutInfo available_layouts = 6; repeated LayoutInfo available_layouts = 6;
repeated PluginInfo plugins = 7; repeated PluginInfo plugins = 7;
repeated ClientTabHistory tab_history = 8;
}
message ClientTabHistory {
uint32 client_id = 1;
repeated uint32 tab_history = 2;
} }
message PluginInfo { message PluginInfo {

View file

@ -2,14 +2,14 @@ pub use super::generated_api::api::{
action::{Action as ProtobufAction, Position as ProtobufPosition}, action::{Action as ProtobufAction, Position as ProtobufPosition},
event::{ event::{
event::Payload as ProtobufEventPayload, ClientInfo as ProtobufClientInfo, event::Payload as ProtobufEventPayload, ClientInfo as ProtobufClientInfo,
CopyDestination as ProtobufCopyDestination, Event as ProtobufEvent, ClientTabHistory as ProtobufClientTabHistory, CopyDestination as ProtobufCopyDestination,
EventNameList as ProtobufEventNameList, EventType as ProtobufEventType, Event as ProtobufEvent, EventNameList as ProtobufEventNameList,
FileMetadata as ProtobufFileMetadata, InputModeKeybinds as ProtobufInputModeKeybinds, EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata,
KeyBind as ProtobufKeyBind, LayoutInfo as ProtobufLayoutInfo, InputModeKeybinds as ProtobufInputModeKeybinds, KeyBind as ProtobufKeyBind,
ModeUpdatePayload as ProtobufModeUpdatePayload, PaneId as ProtobufPaneId, LayoutInfo as ProtobufLayoutInfo, ModeUpdatePayload as ProtobufModeUpdatePayload,
PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest, PaneId as ProtobufPaneId, PaneInfo as ProtobufPaneInfo,
PaneType as ProtobufPaneType, PluginInfo as ProtobufPluginInfo, PaneManifest as ProtobufPaneManifest, PaneType as ProtobufPaneType,
ResurrectableSession as ProtobufResurrectableSession, PluginInfo as ProtobufPluginInfo, ResurrectableSession as ProtobufResurrectableSession,
SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *, SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *,
}, },
input_mode::InputMode as ProtobufInputMode, input_mode::InputMode as ProtobufInputMode,
@ -771,10 +771,23 @@ impl TryFrom<SessionInfo> for ProtobufSessionManifest {
.into_iter() .into_iter()
.map(|p| ProtobufPluginInfo::from(p)) .map(|p| ProtobufPluginInfo::from(p))
.collect(), .collect(),
tab_history: session_info
.tab_history
.into_iter()
.map(|t| ProtobufClientTabHistory::from(t))
.collect(),
}) })
} }
} }
impl From<(u16, Vec<usize>)> for ProtobufClientTabHistory {
fn from((client_id, tab_history): (u16, Vec<usize>)) -> ProtobufClientTabHistory {
ProtobufClientTabHistory {
client_id: client_id as u32,
tab_history: tab_history.into_iter().map(|t| t as u32).collect(),
}
}
}
impl From<(u32, PluginInfo)> for ProtobufPluginInfo { impl From<(u32, PluginInfo)> for ProtobufPluginInfo {
fn from((plugin_id, plugin_info): (u32, PluginInfo)) -> ProtobufPluginInfo { fn from((plugin_id, plugin_info): (u32, PluginInfo)) -> ProtobufPluginInfo {
ProtobufPluginInfo { ProtobufPluginInfo {
@ -821,6 +834,16 @@ impl TryFrom<ProtobufSessionManifest> for SessionInfo {
}, },
); );
} }
let mut tab_history = BTreeMap::new();
for client_tab_history in protobuf_session_manifest.tab_history.into_iter() {
let client_id = client_tab_history.client_id;
let tab_history_for_client = client_tab_history
.tab_history
.iter()
.map(|t| *t as usize)
.collect();
tab_history.insert(client_id as u16, tab_history_for_client);
}
Ok(SessionInfo { Ok(SessionInfo {
name: protobuf_session_manifest.name, name: protobuf_session_manifest.name,
tabs: protobuf_session_manifest tabs: protobuf_session_manifest
@ -837,6 +860,7 @@ impl TryFrom<ProtobufSessionManifest> for SessionInfo {
.filter_map(|l| LayoutInfo::try_from(l).ok()) .filter_map(|l| LayoutInfo::try_from(l).ok())
.collect(), .collect(),
plugins, plugins,
tab_history,
}) })
} }
} }
@ -1921,6 +1945,9 @@ fn serialize_session_update_event_with_non_default_values() {
configuration: plugin_configuration, configuration: plugin_configuration,
}, },
); );
let mut tab_history = BTreeMap::new();
tab_history.insert(1, vec![1, 2, 3]);
tab_history.insert(2, vec![1, 2, 3]);
let session_info_1 = SessionInfo { let session_info_1 = SessionInfo {
name: "session 1".to_owned(), name: "session 1".to_owned(),
tabs: tab_infos, tabs: tab_infos,
@ -1933,6 +1960,7 @@ fn serialize_session_update_event_with_non_default_values() {
LayoutInfo::File("layout3".to_owned()), LayoutInfo::File("layout3".to_owned()),
], ],
plugins, plugins,
tab_history,
}; };
let session_info_2 = SessionInfo { let session_info_2 = SessionInfo {
name: "session 2".to_owned(), name: "session 2".to_owned(),
@ -1948,6 +1976,7 @@ fn serialize_session_update_event_with_non_default_values() {
LayoutInfo::File("layout3".to_owned()), LayoutInfo::File("layout3".to_owned()),
], ],
plugins: Default::default(), plugins: Default::default(),
tab_history: Default::default(),
}; };
let session_infos = vec![session_info_1, session_info_2]; let session_infos = vec![session_info_1, session_info_2];
let resurrectable_sessions = vec![]; let resurrectable_sessions = vec![];