summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--zellij-client/src/input_handler.rs1
-rw-r--r--zellij-server/src/route.rs6
-rw-r--r--zellij-server/src/screen.rs45
-rw-r--r--zellij-server/src/unit/screen_tests.rs185
-rw-r--r--zellij-utils/assets/config/default.yaml2
-rw-r--r--zellij-utils/src/errors.rs1
-rw-r--r--zellij-utils/src/input/actions.rs1
-rw-r--r--zellij-utils/src/input/mod.rs1
8 files changed, 238 insertions, 4 deletions
diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs
index 8e65df49c..03d223b2e 100644
--- a/zellij-client/src/input_handler.rs
+++ b/zellij-client/src/input_handler.rs
@@ -190,6 +190,7 @@ impl InputHandler {
| Action::GoToPreviousTab
| Action::CloseTab
| Action::GoToTab(_)
+ | Action::ToggleTab
| Action::MoveFocusOrTab(_) => {
self.command_is_executing.blocking_input_thread();
self.os_input
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 20f14c3bd..54ddfe1b1 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -28,6 +28,12 @@ fn route_action(
.send_to_plugin(PluginInstruction::Update(None, Event::InputReceived))
.unwrap();
match action {
+ Action::ToggleTab => {
+ session
+ .senders
+ .send_to_screen(ScreenInstruction::ToggleTab)
+ .unwrap();
+ }
Action::Write(val) => {
session
.senders
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index 4a62dad09..d0e02e80e 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -69,6 +69,7 @@ pub(crate) enum ScreenInstruction {
ToggleActiveSyncTab,
CloseTab,
GoToTab(u32),
+ ToggleTab,
UpdateTabName(Vec<u8>),
TerminalResize(PositionAndSize),
ChangeMode(ModeInfo),
@@ -135,6 +136,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::MouseRelease(_) => ScreenContext::MouseRelease,
ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold,
ScreenInstruction::Copy => ScreenContext::Copy,
+ ScreenInstruction::ToggleTab => ScreenContext::ToggleTab,
}
}
}
@@ -152,6 +154,7 @@ pub(crate) struct Screen {
position_and_size: PositionAndSize,
/// The index of this [`Screen`]'s active [`Tab`].
active_tab_index: Option<usize>,
+ tab_history: Vec<Option<usize>>,
mode_info: ModeInfo,
colors: Palette,
session_state: Arc<RwLock<SessionState>>,
@@ -175,6 +178,7 @@ impl Screen {
colors: client_attributes.palette,
active_tab_index: None,
tabs: BTreeMap::new(),
+ tab_history: Vec::with_capacity(32),
mode_info,
session_state,
draw_pane_frames,
@@ -200,6 +204,7 @@ impl Screen {
self.session_state.clone(),
self.draw_pane_frames,
);
+ self.tab_history.push(self.active_tab_index);
self.active_tab_index = Some(tab_index);
self.tabs.insert(tab_index, tab);
self.update_tabs();
@@ -225,6 +230,8 @@ impl Screen {
for tab in self.tabs.values_mut() {
if tab.position == new_tab_pos {
tab.set_force_render();
+ self.tab_history.retain(|&e| e != Some(tab.index));
+ self.tab_history.push(self.active_tab_index);
self.active_tab_index = Some(tab.index);
break;
}
@@ -244,6 +251,8 @@ impl Screen {
for tab in self.tabs.values_mut() {
if tab.position == new_tab_pos {
tab.set_force_render();
+ self.tab_history.retain(|&e| e != Some(tab.index));
+ self.tab_history.push(self.active_tab_index);
self.active_tab_index = Some(tab.index);
break;
}
@@ -258,6 +267,8 @@ impl Screen {
if let Some(t) = self.tabs.values_mut().find(|t| t.position == tab_index) {
if t.index != active_tab_index {
t.set_force_render();
+ self.tab_history.retain(|&e| e != Some(t.index));
+ self.tab_history.push(self.active_tab_index);
self.active_tab_index = Some(t.index);
self.update_tabs();
self.render();
@@ -269,9 +280,6 @@ impl Screen {
/// to be the last tab.
pub fn close_tab(&mut self) {
let active_tab_index = self.active_tab_index.unwrap();
- if self.tabs.len() > 1 {
- self.switch_tab_prev();
- }
let active_tab = self.tabs.remove(&active_tab_index).unwrap();
let pane_ids = active_tab.get_pane_ids();
// below we don't check the result of sending the CloseTab instruction to the pty thread
@@ -290,6 +298,7 @@ impl Screen {
.unwrap();
}
} else {
+ self.active_tab_index = self.tab_history.pop().unwrap();
for t in self.tabs.values_mut() {
if t.position > active_tab.position {
t.position -= 1;
@@ -335,6 +344,17 @@ impl Screen {
}
}
+ /// Returns an immutable reference to this [`Screen`]'s previous active [`Tab`].
+ /// Consumes the last entry in tab history.
+ pub fn get_previous_tab(&mut self) -> Option<&Tab> {
+ let last = self.tab_history.pop();
+ last?;
+ match last.unwrap() {
+ Some(tab) => self.tabs.get(&tab),
+ None => None,
+ }
+ }
+
/// Returns a mutable reference to this [`Screen`]'s active [`Tab`].
pub fn get_active_tab_mut(&mut self) -> Option<&mut Tab> {
match self.active_tab_index {
@@ -368,6 +388,7 @@ impl Screen {
self.draw_pane_frames,
);
tab.apply_layout(layout, new_pids, tab_index);
+ self.tab_history.push(self.active_tab_index);
self.active_tab_index = Some(tab_index);
self.tabs.insert(tab_index, tab);
self.update_tabs();
@@ -425,6 +446,16 @@ impl Screen {
self.switch_tab_next();
}
}
+ pub fn toggle_tab(&mut self) {
+ let tab = self.get_previous_tab();
+ if let Some(t) = tab {
+ let position = t.position;
+ self.go_to_tab(position + 1);
+ };
+
+ self.update_tabs();
+ self.render();
+ }
}
// The box is here in order to make the
@@ -768,6 +799,14 @@ pub(crate) fn screen_thread_main(
ScreenInstruction::Exit => {
break;
}
+ ScreenInstruction::ToggleTab => {
+ screen.toggle_tab();
+ screen
+ .bus
+ .senders
+ .send_to_server(ServerInstruction::UnblockInputThread)
+ .unwrap();
+ }
}
}
}
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 0ce5cff81..6248ded27 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -202,7 +202,7 @@ pub fn close_the_middle_tab() {
assert_eq!(screen.tabs.len(), 2, "Two tabs left");
assert_eq!(
screen.get_active_tab().unwrap().position,
- 0,
+ 1,
"Active tab switched to previous tab"
);
}
@@ -254,3 +254,186 @@ fn move_focus_right_at_right_screen_edge_changes_tab() {
"Active tab switched to next"
);
}
+
+#[test]
+pub fn toggle_to_previous_tab_simple() {
+ let position_and_size = PositionAndSize {
+ cols: 121,
+ rows: 20,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+ let mut screen = create_new_screen(position_and_size);
+
+ screen.new_tab(1);
+ screen.new_tab(2);
+ screen.go_to_tab(1);
+ screen.go_to_tab(2);
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 0,
+ "Active tab toggler to previous tab"
+ );
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 1,
+ "Active tab toggler to previous tab"
+ );
+}
+
+#[test]
+pub fn toggle_to_previous_tab_create_tabs_only() {
+ let position_and_size = PositionAndSize {
+ cols: 121,
+ rows: 20,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+ let mut screen = create_new_screen(position_and_size);
+
+ screen.new_tab(1);
+ screen.new_tab(2);
+ screen.new_tab(3);
+
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(1)],
+ "Tab history is invalid"
+ );
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 1,
+ "Active tab toggler to previous tab"
+ );
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(2)],
+ "Tab history is invalid"
+ );
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 2,
+ "Active tab toggler to previous tab"
+ );
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(1)],
+ "Tab history is invalid"
+ );
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 1,
+ "Active tab toggler to previous tab"
+ );
+}
+
+#[test]
+pub fn toggle_to_previous_tab_delete() {
+ let position_and_size = PositionAndSize {
+ cols: 121,
+ rows: 20,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+ let mut screen = create_new_screen(position_and_size);
+
+ screen.new_tab(1); // 0
+ screen.new_tab(2); // 1
+ screen.new_tab(3); // 2
+ screen.new_tab(4); // 3
+
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(1), Some(2)],
+ "Tab history is invalid"
+ );
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 3,
+ "Active tab toggler to previous tab"
+ );
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(1), Some(3)],
+ "Tab history is invalid"
+ );
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 2,
+ "Active tab toggler to previous tab"
+ );
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(1), Some(2)],
+ "Tab history is invalid"
+ );
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 3,
+ "Active tab toggler to previous tab"
+ );
+
+ screen.switch_tab_prev();
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(1), Some(3)],
+ "Tab history is invalid"
+ );
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 2,
+ "Active tab toggler to previous tab"
+ );
+ screen.switch_tab_prev();
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(3), Some(2)],
+ "Tab history is invalid"
+ );
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 1,
+ "Active tab toggler to previous tab"
+ );
+
+ screen.close_tab();
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(3)],
+ "Tab history is invalid"
+ );
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 1,
+ "Active tab toggler to previous tab"
+ );
+
+ screen.toggle_tab();
+ assert_eq!(
+ screen.get_active_tab().unwrap().position,
+ 2,
+ "Active tab toggler to previous tab"
+ );
+ assert_eq!(
+ screen.tab_history,
+ vec![None, Some(0), Some(2)],
+ "Tab history is invalid"
+ );
+}
diff --git a/zellij-utils/assets/config/default.yaml b/zellij-utils/assets/config/default.yaml
index 4654cee90..94f12f33c 100644
--- a/zellij-utils/assets/config/default.yaml
+++ b/zellij-utils/assets/config/default.yaml
@@ -168,6 +168,8 @@ keybinds:
key: [ Char: '8',]
- action: [GoToTab: 9,]
key: [ Char: '9',]
+ - action: [ToggleTab]
+ key: [ Char: "\t" ]
scroll:
- action: [SwitchToMode: Normal,]
key: [Ctrl: 'r', Ctrl: 's', Char: ' ',
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index c0891d52d..65a54c560 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -228,6 +228,7 @@ pub enum ScreenContext {
MouseRelease,
MouseHold,
Copy,
+ ToggleTab,
}
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index 3e86409bc..23c01eefe 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -76,6 +76,7 @@ pub enum Action {
/// Close the current tab.
CloseTab,
GoToTab(u32),
+ ToggleTab,
TabNameInput(Vec<u8>),
/// Run speficied command in new pane.
Run(RunCommandAction),
diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs
index 87d72db8a..68aa9dd79 100644
--- a/zellij-utils/src/input/mod.rs
+++ b/zellij-utils/src/input/mod.rs
@@ -38,6 +38,7 @@ pub fn get_mode_info(
("x".to_string(), "Close".to_string()),
("r".to_string(), "Rename".to_string()),
("s".to_string(), "Sync".to_string()),
+ ("Tab".to_string(), "Toggle".to_string()),
],
InputMode::Scroll => vec![
("↓↑".to_string(), "Scroll".to_string()),