Skip to content

Commit

Permalink
Add Switch to Next/Prev Split or Tab command (#995)
Browse files Browse the repository at this point in the history
* Add Switch to Next/Prev Split or Tab command

* Update docs for Select Next/Prev SplitOrTab commands

* Add docs for MoveCurrentTabToNext

* Add switch tab/split unit tests
  • Loading branch information
vlabo authored Mar 2, 2025
1 parent 11b0afe commit 0e33b2f
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 14 deletions.
31 changes: 17 additions & 14 deletions docs/docs/key-bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,25 +74,28 @@ Execute a predefined action in Rio terminal.

### [Split Actions](#split-actions)

| Action | Description |
| :-------------- | :------------------------------------------------------------------------- |
| SplitRight | Create a split by right side |
| SplitDown | Create a split by under current pane |
| SelectNextSplit | Select next split |
| SelectPrevSplit | Select previous split |
| CloseSplitOrTab | Close split, if split is the last then will close the tab |
| Action | Description |
| :------------------- | :------------------------------------------------------------------------- |
| SplitRight | Create a split by right side |
| SplitDown | Create a split by under current pane |
| SelectNextSplit | Select next split |
| SelectPrevSplit | Select previous split |
| CloseSplitOrTab | Close split, if split is the last then will close the tab |
| SelectNextSplitOrTab | Select next split if available if not next tab |
| SelectPrevSplitOrTab | Select previous split if available if not previous tab |

### [Tab Actions](#tab-actions)

| Action | Description |
| :------------------- | :---------------------------------------------------------------------- |
| CreateTab | |
| CloseTab | |
| CloseUnfocusedTabs | |
| SelectPrevTab | |
| SelectNextTab | |
| SelectLastTab | |
| MoveCurrentTabToPrev | Move the current focused tab to the previous slot if any is available |
| CreateTab | Create new tab |
| CloseTab | Close current tab |
| CloseUnfocusedTabs | Close all tabs that are not currently focused |
| SelectNextTab | Select next tab |
| SelectPrevTab | Select pervious tab |
| SelectLastTab | Select last tab |
| MoveCurrentTabToNext | Move the current focused tab to the next slot, or first when last |
| MoveCurrentTabToPrev | Move the current focused tab to the previous slot, or last when first |
| SelectTab(tab_index) | Example: Select first tab `SelectTab(0)`, second tab `SelectTab(1)` |

### [Scroll Actions](#scroll-actions)
Expand Down
11 changes: 11 additions & 0 deletions frontends/rioterm/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ impl From<String> for Action {
"splitdown" => Some(Action::SplitDown),
"selectnextsplit" => Some(Action::SelectNextSplit),
"selectprevsplit" => Some(Action::SelectPrevSplit),
"selectnextsplitortab" => Some(Action::SelectNextSplitOrTab),
"selectprevsplitortab" => Some(Action::SelectPrevSplitOrTab),
"togglevimode" => Some(Action::ToggleViMode),
"togglefullscreen" => Some(Action::ToggleFullscreen),
"none" => Some(Action::None),
Expand Down Expand Up @@ -465,9 +467,18 @@ pub enum Action {
/// Split vertically
SplitDown,

/// Select next split
SelectNextSplit,

/// Select previous split
SelectPrevSplit,

/// Select next split if available if not next tab
SelectNextSplitOrTab,

/// Select previous split if available if not previous tab
SelectPrevSplitOrTab,

/// Allow receiving char input.
ReceiveChar,

Expand Down
29 changes: 29 additions & 0 deletions frontends/rioterm/src/context/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,21 @@ impl<T: rio_backend::event::EventListener> ContextGrid<T> {
}
}

#[inline]
pub fn select_next_split_no_loop(&mut self) -> bool {
if self.inner.len() == 1 {
return false;
}

if self.current >= self.inner.len() - 1 {
return false;
} else {
self.current += 1;
}

return true;
}

#[inline]
pub fn select_prev_split(&mut self) {
if self.inner.len() == 1 {
Expand All @@ -153,6 +168,20 @@ impl<T: rio_backend::event::EventListener> ContextGrid<T> {
}
}

#[inline]
pub fn select_prev_split_no_loop(&mut self) -> bool {
if self.inner.len() == 1 {
return false;
}

if self.current == 0 {
return false;
} else {
self.current -= 1;
}
return true;
}

#[inline]
#[allow(unused)]
pub fn current_index(&self) -> usize {
Expand Down
214 changes: 214 additions & 0 deletions frontends/rioterm/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,32 @@ impl<T: EventListener + Clone + std::marker::Send + 'static> ContextManager<T> {
self.current_route = self.current().route_id;
}

#[inline]
pub fn switch_to_next_split_or_tab(&mut self) {
if self.contexts[self.current_index].select_next_split_no_loop() {
self.current_route = self.current().route_id;
return;
}
self.switch_to_next();
// Make sure first split is selected
let current_tab = &mut self.contexts[self.current_index];
current_tab.current = 0;
self.current_route = self.current().route_id;
}

#[inline]
pub fn switch_to_prev_split_or_tab(&mut self) {
if self.contexts[self.current_index].select_prev_split_no_loop() {
self.current_route = self.current().route_id;
return;
}
self.switch_to_prev();
// Make sure last split is selected
let current_tab = &mut self.contexts[self.current_index];
current_tab.current = current_tab.len() - 1;
self.current_route = self.current().route_id;
}

#[inline]
pub fn select_tab(&mut self, tab_index: usize) {
if self.config.is_native {
Expand Down Expand Up @@ -1229,6 +1255,194 @@ pub mod test {
assert_eq!(context_manager.current_index, 1);
}

#[test]
fn test_switch_to_next_split_or_tab() {
let window_id: WindowId = WindowId::from(0);

let mut context_manager =
ContextManager::start_with_capacity(5, VoidListener {}, window_id).unwrap();
let should_redirect = true;
let split_down = false;

context_manager.add_context(should_redirect, 0);
context_manager.split(0, split_down);
context_manager.split(0, split_down);
context_manager.add_context(should_redirect, 0);
context_manager.add_context(should_redirect, 0);
context_manager.split(0, split_down);
context_manager.add_context(should_redirect, 0);
context_manager.set_current(0);
assert_eq!(context_manager.len(), 5);
assert_eq!(context_manager.current_index, 0);

let mut current_index;

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 1);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 2);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 2);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 3);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 3);
assert_eq!(context_manager.contexts[current_index].current, 1);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 4);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 0);
assert_eq!(context_manager.contexts[current_index].current, 0);
}

#[test]
fn test_switch_to_prev_split_or_tab() {
let window_id: WindowId = WindowId::from(0);

let mut context_manager =
ContextManager::start_with_capacity(5, VoidListener {}, window_id).unwrap();
let should_redirect = true;
let split_down = false;

context_manager.add_context(should_redirect, 0);
context_manager.split(0, split_down);
context_manager.split(0, split_down);
context_manager.add_context(should_redirect, 0);
context_manager.add_context(should_redirect, 0);
context_manager.split(0, split_down);
context_manager.add_context(should_redirect, 0);
context_manager.set_current(0);
assert_eq!(context_manager.len(), 5);
assert_eq!(context_manager.current_index, 0);

let mut current_index;

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 4);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 3);
assert_eq!(context_manager.contexts[current_index].current, 1);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 3);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 2);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 2);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 1);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 0);
assert_eq!(context_manager.contexts[current_index].current, 0);
}

#[test]
fn test_switch_to_next_and_prev_split_or_tab() {
let window_id: WindowId = WindowId::from(0);

let mut context_manager =
ContextManager::start_with_capacity(5, VoidListener {}, window_id).unwrap();
let should_redirect = true;
let split_down = false;

context_manager.add_context(should_redirect, 0);
context_manager.split(0, split_down);
context_manager.split(0, split_down);
context_manager.add_context(should_redirect, 0);
context_manager.set_current(0);
assert_eq!(context_manager.len(), 3);
assert_eq!(context_manager.current_index, 0);

let mut current_index;

// Next
context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 1);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 2);

context_manager.switch_to_next_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 2);
assert_eq!(context_manager.contexts[current_index].current, 0);

// Prev
context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 2);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 1);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 1);
assert_eq!(context_manager.contexts[current_index].current, 0);

context_manager.switch_to_prev_split_or_tab();
current_index = context_manager.current_index;
assert_eq!(current_index, 0);
assert_eq!(context_manager.contexts[current_index].current, 0);
}

#[test]
fn test_move_current_to_next() {
let window_id = WindowId::from(0);
Expand Down
10 changes: 10 additions & 0 deletions frontends/rioterm/src/screen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,16 @@ impl Screen<'_> {
self.context_manager.select_prev_split();
self.render();
}
Act::SelectNextSplitOrTab => {
self.cancel_search();
self.context_manager.switch_to_next_split_or_tab();
self.render();
}
Act::SelectPrevSplitOrTab => {
self.cancel_search();
self.context_manager.switch_to_prev_split_or_tab();
self.render();
}
Act::SelectTab(tab_index) => {
self.context_manager.select_tab(*tab_index);
self.cancel_search();
Expand Down

0 comments on commit 0e33b2f

Please sign in to comment.