Merge pull request #622 from sagittarius-a/feature/go-to-last-tab
feat(tab): add keybind to go to last tab visited
This commit is contained in:
commit
b1906c893a
8 changed files with 238 additions and 4 deletions
|
|
@ -190,6 +190,7 @@ impl InputHandler {
|
||||||
| Action::GoToPreviousTab
|
| Action::GoToPreviousTab
|
||||||
| Action::CloseTab
|
| Action::CloseTab
|
||||||
| Action::GoToTab(_)
|
| Action::GoToTab(_)
|
||||||
|
| Action::ToggleTab
|
||||||
| Action::MoveFocusOrTab(_) => {
|
| Action::MoveFocusOrTab(_) => {
|
||||||
self.command_is_executing.blocking_input_thread();
|
self.command_is_executing.blocking_input_thread();
|
||||||
self.os_input
|
self.os_input
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,12 @@ fn route_action(
|
||||||
.send_to_plugin(PluginInstruction::Update(None, Event::InputReceived))
|
.send_to_plugin(PluginInstruction::Update(None, Event::InputReceived))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match action {
|
match action {
|
||||||
|
Action::ToggleTab => {
|
||||||
|
session
|
||||||
|
.senders
|
||||||
|
.send_to_screen(ScreenInstruction::ToggleTab)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
Action::Write(val) => {
|
Action::Write(val) => {
|
||||||
session
|
session
|
||||||
.senders
|
.senders
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ pub(crate) enum ScreenInstruction {
|
||||||
ToggleActiveSyncTab,
|
ToggleActiveSyncTab,
|
||||||
CloseTab,
|
CloseTab,
|
||||||
GoToTab(u32),
|
GoToTab(u32),
|
||||||
|
ToggleTab,
|
||||||
UpdateTabName(Vec<u8>),
|
UpdateTabName(Vec<u8>),
|
||||||
TerminalResize(PositionAndSize),
|
TerminalResize(PositionAndSize),
|
||||||
ChangeMode(ModeInfo),
|
ChangeMode(ModeInfo),
|
||||||
|
|
@ -135,6 +136,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||||
ScreenInstruction::MouseRelease(_) => ScreenContext::MouseRelease,
|
ScreenInstruction::MouseRelease(_) => ScreenContext::MouseRelease,
|
||||||
ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold,
|
ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold,
|
||||||
ScreenInstruction::Copy => ScreenContext::Copy,
|
ScreenInstruction::Copy => ScreenContext::Copy,
|
||||||
|
ScreenInstruction::ToggleTab => ScreenContext::ToggleTab,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,6 +154,7 @@ pub(crate) struct Screen {
|
||||||
position_and_size: PositionAndSize,
|
position_and_size: PositionAndSize,
|
||||||
/// The index of this [`Screen`]'s active [`Tab`].
|
/// The index of this [`Screen`]'s active [`Tab`].
|
||||||
active_tab_index: Option<usize>,
|
active_tab_index: Option<usize>,
|
||||||
|
tab_history: Vec<Option<usize>>,
|
||||||
mode_info: ModeInfo,
|
mode_info: ModeInfo,
|
||||||
colors: Palette,
|
colors: Palette,
|
||||||
session_state: Arc<RwLock<SessionState>>,
|
session_state: Arc<RwLock<SessionState>>,
|
||||||
|
|
@ -175,6 +178,7 @@ impl Screen {
|
||||||
colors: client_attributes.palette,
|
colors: client_attributes.palette,
|
||||||
active_tab_index: None,
|
active_tab_index: None,
|
||||||
tabs: BTreeMap::new(),
|
tabs: BTreeMap::new(),
|
||||||
|
tab_history: Vec::with_capacity(32),
|
||||||
mode_info,
|
mode_info,
|
||||||
session_state,
|
session_state,
|
||||||
draw_pane_frames,
|
draw_pane_frames,
|
||||||
|
|
@ -200,6 +204,7 @@ impl Screen {
|
||||||
self.session_state.clone(),
|
self.session_state.clone(),
|
||||||
self.draw_pane_frames,
|
self.draw_pane_frames,
|
||||||
);
|
);
|
||||||
|
self.tab_history.push(self.active_tab_index);
|
||||||
self.active_tab_index = Some(tab_index);
|
self.active_tab_index = Some(tab_index);
|
||||||
self.tabs.insert(tab_index, tab);
|
self.tabs.insert(tab_index, tab);
|
||||||
self.update_tabs();
|
self.update_tabs();
|
||||||
|
|
@ -225,6 +230,8 @@ impl Screen {
|
||||||
for tab in self.tabs.values_mut() {
|
for tab in self.tabs.values_mut() {
|
||||||
if tab.position == new_tab_pos {
|
if tab.position == new_tab_pos {
|
||||||
tab.set_force_render();
|
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);
|
self.active_tab_index = Some(tab.index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -244,6 +251,8 @@ impl Screen {
|
||||||
for tab in self.tabs.values_mut() {
|
for tab in self.tabs.values_mut() {
|
||||||
if tab.position == new_tab_pos {
|
if tab.position == new_tab_pos {
|
||||||
tab.set_force_render();
|
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);
|
self.active_tab_index = Some(tab.index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -258,6 +267,8 @@ impl Screen {
|
||||||
if let Some(t) = self.tabs.values_mut().find(|t| t.position == tab_index) {
|
if let Some(t) = self.tabs.values_mut().find(|t| t.position == tab_index) {
|
||||||
if t.index != active_tab_index {
|
if t.index != active_tab_index {
|
||||||
t.set_force_render();
|
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.active_tab_index = Some(t.index);
|
||||||
self.update_tabs();
|
self.update_tabs();
|
||||||
self.render();
|
self.render();
|
||||||
|
|
@ -269,9 +280,6 @@ impl Screen {
|
||||||
/// to be the last tab.
|
/// to be the last tab.
|
||||||
pub fn close_tab(&mut self) {
|
pub fn close_tab(&mut self) {
|
||||||
let active_tab_index = self.active_tab_index.unwrap();
|
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 active_tab = self.tabs.remove(&active_tab_index).unwrap();
|
||||||
let pane_ids = active_tab.get_pane_ids();
|
let pane_ids = active_tab.get_pane_ids();
|
||||||
// below we don't check the result of sending the CloseTab instruction to the pty thread
|
// below we don't check the result of sending the CloseTab instruction to the pty thread
|
||||||
|
|
@ -290,6 +298,7 @@ impl Screen {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
self.active_tab_index = self.tab_history.pop().unwrap();
|
||||||
for t in self.tabs.values_mut() {
|
for t in self.tabs.values_mut() {
|
||||||
if t.position > active_tab.position {
|
if t.position > active_tab.position {
|
||||||
t.position -= 1;
|
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`].
|
/// Returns a mutable reference to this [`Screen`]'s active [`Tab`].
|
||||||
pub fn get_active_tab_mut(&mut self) -> Option<&mut Tab> {
|
pub fn get_active_tab_mut(&mut self) -> Option<&mut Tab> {
|
||||||
match self.active_tab_index {
|
match self.active_tab_index {
|
||||||
|
|
@ -368,6 +388,7 @@ impl Screen {
|
||||||
self.draw_pane_frames,
|
self.draw_pane_frames,
|
||||||
);
|
);
|
||||||
tab.apply_layout(layout, new_pids, tab_index);
|
tab.apply_layout(layout, new_pids, tab_index);
|
||||||
|
self.tab_history.push(self.active_tab_index);
|
||||||
self.active_tab_index = Some(tab_index);
|
self.active_tab_index = Some(tab_index);
|
||||||
self.tabs.insert(tab_index, tab);
|
self.tabs.insert(tab_index, tab);
|
||||||
self.update_tabs();
|
self.update_tabs();
|
||||||
|
|
@ -425,6 +446,16 @@ impl Screen {
|
||||||
self.switch_tab_next();
|
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
|
// The box is here in order to make the
|
||||||
|
|
@ -768,6 +799,14 @@ pub(crate) fn screen_thread_main(
|
||||||
ScreenInstruction::Exit => {
|
ScreenInstruction::Exit => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ScreenInstruction::ToggleTab => {
|
||||||
|
screen.toggle_tab();
|
||||||
|
screen
|
||||||
|
.bus
|
||||||
|
.senders
|
||||||
|
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ pub fn close_the_middle_tab() {
|
||||||
assert_eq!(screen.tabs.len(), 2, "Two tabs left");
|
assert_eq!(screen.tabs.len(), 2, "Two tabs left");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
screen.get_active_tab().unwrap().position,
|
screen.get_active_tab().unwrap().position,
|
||||||
0,
|
1,
|
||||||
"Active tab switched to previous tab"
|
"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"
|
"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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,8 @@ keybinds:
|
||||||
key: [ Char: '8',]
|
key: [ Char: '8',]
|
||||||
- action: [GoToTab: 9,]
|
- action: [GoToTab: 9,]
|
||||||
key: [ Char: '9',]
|
key: [ Char: '9',]
|
||||||
|
- action: [ToggleTab]
|
||||||
|
key: [ Char: "\t" ]
|
||||||
scroll:
|
scroll:
|
||||||
- action: [SwitchToMode: Normal,]
|
- action: [SwitchToMode: Normal,]
|
||||||
key: [Ctrl: 'r', Ctrl: 's', Char: ' ',
|
key: [Ctrl: 'r', Ctrl: 's', Char: ' ',
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,7 @@ pub enum ScreenContext {
|
||||||
MouseRelease,
|
MouseRelease,
|
||||||
MouseHold,
|
MouseHold,
|
||||||
Copy,
|
Copy,
|
||||||
|
ToggleTab,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ pub enum Action {
|
||||||
/// Close the current tab.
|
/// Close the current tab.
|
||||||
CloseTab,
|
CloseTab,
|
||||||
GoToTab(u32),
|
GoToTab(u32),
|
||||||
|
ToggleTab,
|
||||||
TabNameInput(Vec<u8>),
|
TabNameInput(Vec<u8>),
|
||||||
/// Run speficied command in new pane.
|
/// Run speficied command in new pane.
|
||||||
Run(RunCommandAction),
|
Run(RunCommandAction),
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ pub fn get_mode_info(
|
||||||
("x".to_string(), "Close".to_string()),
|
("x".to_string(), "Close".to_string()),
|
||||||
("r".to_string(), "Rename".to_string()),
|
("r".to_string(), "Rename".to_string()),
|
||||||
("s".to_string(), "Sync".to_string()),
|
("s".to_string(), "Sync".to_string()),
|
||||||
|
("Tab".to_string(), "Toggle".to_string()),
|
||||||
],
|
],
|
||||||
InputMode::Scroll => vec![
|
InputMode::Scroll => vec![
|
||||||
("↓↑".to_string(), "Scroll".to_string()),
|
("↓↑".to_string(), "Scroll".to_string()),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue