diff --git a/CHANGELOG.md b/CHANGELOG.md index 61c20420..dcdae5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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(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) +* feat(plugins): add tab_history to the session metadata (https://github.com/zellij-org/zellij/pull/4014) ## [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) diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 0b9d0c67..16cc3d39 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -1648,6 +1648,7 @@ impl Screen { is_current_session: true, available_layouts, plugins: Default::default(), // these are filled in by the wasm thread + tab_history: self.tab_history.clone(), }; self.bus .senders diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs index 493e0dc4..46372db5 100644 --- a/zellij-utils/assets/prost/api.event.rs +++ b/zellij-utils/assets/prost/api.event.rs @@ -316,6 +316,16 @@ pub struct SessionManifest { pub available_layouts: ::prost::alloc::vec::Vec, #[prost(message, repeated, tag = "7")] pub plugins: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub tab_history: ::prost::alloc::vec::Vec, +} +#[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, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index a1be6274..368b3e2a 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -1554,6 +1554,7 @@ pub struct SessionInfo { pub is_current_session: bool, pub available_layouts: Vec, pub plugins: BTreeMap, + pub tab_history: BTreeMap>, } #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 27230c2f..93b111f4 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -4462,6 +4462,29 @@ impl SessionInfo { }) .ok_or("Failed to parse available_layouts")?; 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 { name, tabs, @@ -4470,6 +4493,7 @@ impl SessionInfo { is_current_session, available_layouts, plugins: Default::default(), // we do not serialize plugin information + tab_history, }) } pub fn to_string(&self) -> String { @@ -4510,11 +4534,30 @@ impl SessionInfo { } 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(tabs); kdl_document.nodes_mut().push(panes); kdl_document.nodes_mut().push(connected_clients); kdl_document.nodes_mut().push(available_layouts); + kdl_document.nodes_mut().push(tab_history); kdl_document.fmt(); kdl_document.to_string() } @@ -5067,6 +5110,7 @@ fn serialize_and_deserialize_session_info_with_data() { LayoutInfo::File("layout3".to_owned()), ], plugins: Default::default(), + tab_history: Default::default(), }; let serialized = session_info.to_string(); let deserealized = SessionInfo::from_string(&serialized, "not this session").unwrap(); diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info.snap index 32925ec1..aaaa6ba7 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 2459 +assertion_line: 5000 expression: serialized --- name "" @@ -11,4 +11,6 @@ panes { connected_clients 0 available_layouts { } +tab_history { +} diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info_with_data.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info_with_data.snap index 6e0ba4c5..a0401386 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info_with_data.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__serialize_and_deserialize_session_info_with_data.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5070 +assertion_line: 5111 expression: serialized --- name "my session name" @@ -95,4 +95,6 @@ available_layouts { layout2 source="built-in" layout3 source="file" } +tab_history { +} diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index 7269cc47..14eb0c40 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -257,6 +257,12 @@ message SessionManifest { bool is_current_session = 5; repeated LayoutInfo available_layouts = 6; repeated PluginInfo plugins = 7; + repeated ClientTabHistory tab_history = 8; +} + +message ClientTabHistory { + uint32 client_id = 1; + repeated uint32 tab_history = 2; } message PluginInfo { diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 743e1569..4707d657 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -2,14 +2,14 @@ pub use super::generated_api::api::{ action::{Action as ProtobufAction, Position as ProtobufPosition}, event::{ event::Payload as ProtobufEventPayload, ClientInfo as ProtobufClientInfo, - CopyDestination as ProtobufCopyDestination, Event as ProtobufEvent, - EventNameList as ProtobufEventNameList, EventType as ProtobufEventType, - FileMetadata as ProtobufFileMetadata, InputModeKeybinds as ProtobufInputModeKeybinds, - KeyBind as ProtobufKeyBind, LayoutInfo as ProtobufLayoutInfo, - ModeUpdatePayload as ProtobufModeUpdatePayload, PaneId as ProtobufPaneId, - PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest, - PaneType as ProtobufPaneType, PluginInfo as ProtobufPluginInfo, - ResurrectableSession as ProtobufResurrectableSession, + ClientTabHistory as ProtobufClientTabHistory, CopyDestination as ProtobufCopyDestination, + Event as ProtobufEvent, EventNameList as ProtobufEventNameList, + EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata, + InputModeKeybinds as ProtobufInputModeKeybinds, KeyBind as ProtobufKeyBind, + LayoutInfo as ProtobufLayoutInfo, ModeUpdatePayload as ProtobufModeUpdatePayload, + PaneId as ProtobufPaneId, PaneInfo as ProtobufPaneInfo, + PaneManifest as ProtobufPaneManifest, PaneType as ProtobufPaneType, + PluginInfo as ProtobufPluginInfo, ResurrectableSession as ProtobufResurrectableSession, SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *, }, input_mode::InputMode as ProtobufInputMode, @@ -771,10 +771,23 @@ impl TryFrom for ProtobufSessionManifest { .into_iter() .map(|p| ProtobufPluginInfo::from(p)) .collect(), + tab_history: session_info + .tab_history + .into_iter() + .map(|t| ProtobufClientTabHistory::from(t)) + .collect(), }) } } +impl From<(u16, Vec)> for ProtobufClientTabHistory { + fn from((client_id, tab_history): (u16, Vec)) -> 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 { fn from((plugin_id, plugin_info): (u32, PluginInfo)) -> ProtobufPluginInfo { ProtobufPluginInfo { @@ -821,6 +834,16 @@ impl TryFrom 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 { name: protobuf_session_manifest.name, tabs: protobuf_session_manifest @@ -837,6 +860,7 @@ impl TryFrom for SessionInfo { .filter_map(|l| LayoutInfo::try_from(l).ok()) .collect(), plugins, + tab_history, }) } } @@ -1921,6 +1945,9 @@ fn serialize_session_update_event_with_non_default_values() { 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 { name: "session 1".to_owned(), tabs: tab_infos, @@ -1933,6 +1960,7 @@ fn serialize_session_update_event_with_non_default_values() { LayoutInfo::File("layout3".to_owned()), ], plugins, + tab_history, }; let session_info_2 = SessionInfo { name: "session 2".to_owned(), @@ -1948,6 +1976,7 @@ fn serialize_session_update_event_with_non_default_values() { LayoutInfo::File("layout3".to_owned()), ], plugins: Default::default(), + tab_history: Default::default(), }; let session_infos = vec![session_info_1, session_info_2]; let resurrectable_sessions = vec![];