feat(cli): add GoToTabName action to switch tab by name (#2120)

* Add `GoToTabName` action to switch tab by name

* rm blank file

* add --create option

* format

* add some doc

* add test case

* format

* add test case

* change variable name
This commit is contained in:
哇呜哇呜呀咦耶 2023-02-07 22:45:59 +08:00 committed by GitHub
parent 601eee8bb3
commit 99e8d56adb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 0 deletions

View file

@ -307,6 +307,7 @@ impl InputHandler {
| Action::GoToPreviousTab
| Action::CloseTab
| Action::GoToTab(_)
| Action::GoToTabName(_, _)
| Action::ToggleTab
| Action::MoveFocusOrTab(_) => {
self.command_is_executing.blocking_input_thread();

View file

@ -479,6 +479,16 @@ pub(crate) fn route_action(
.send_to_screen(ScreenInstruction::GoToTab(i, Some(client_id)))
.with_context(err_context)?;
},
Action::GoToTabName(name, create) => {
session
.senders
.send_to_screen(ScreenInstruction::GoToTabName(
name,
create,
Some(client_id),
))
.with_context(err_context)?;
},
Action::TabNameInput(c) => {
session
.senders

View file

@ -196,6 +196,7 @@ pub enum ScreenInstruction {
ToggleActiveSyncTab(ClientId),
CloseTab(ClientId),
GoToTab(u32, Option<ClientId>), // this Option is a hacky workaround, please do not copy this behaviour
GoToTabName(String, bool, Option<ClientId>),
ToggleTab(ClientId),
UpdateTabName(Vec<u8>, ClientId),
UndoRenameTab(ClientId),
@ -317,6 +318,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::SwitchTabPrev(..) => ScreenContext::SwitchTabPrev,
ScreenInstruction::CloseTab(..) => ScreenContext::CloseTab,
ScreenInstruction::GoToTab(..) => ScreenContext::GoToTab,
ScreenInstruction::GoToTabName(..) => ScreenContext::GoToTabName,
ScreenInstruction::UpdateTabName(..) => ScreenContext::UpdateTabName,
ScreenInstruction::UndoRenameTab(..) => ScreenContext::UndoRenameTab,
ScreenInstruction::TerminalResize(..) => ScreenContext::TerminalResize,
@ -623,6 +625,18 @@ impl Screen {
Ok(())
}
/// A helper function to switch to a new tab with specified name. Return true if tab [name] has
/// been created, else false.
fn switch_active_tab_name(&mut self, name: String, client_id: ClientId) -> Result<bool> {
match self.tabs.values().find(|t| t.name == name) {
Some(new_tab) => {
self.switch_active_tab(new_tab.position, client_id)?;
Ok(true)
},
None => Ok(false),
}
}
/// Sets this [`Screen`]'s active [`Tab`] to the next tab.
pub fn switch_tab_next(&mut self, client_id: ClientId) -> Result<()> {
let err_context = || format!("failed to switch to next tab for client {client_id}");
@ -678,6 +692,10 @@ impl Screen {
self.switch_active_tab(tab_index.saturating_sub(1), client_id)
}
pub fn go_to_tab_name(&mut self, name: String, client_id: ClientId) -> Result<bool> {
self.switch_active_tab_name(name, client_id)
}
fn close_tab_at_index(&mut self, tab_index: usize) -> Result<()> {
let err_context = || format!("failed to close tab at index {tab_index:?}");
@ -1981,6 +1999,39 @@ pub(crate) fn screen_thread_main(
screen.render()?;
}
},
ScreenInstruction::GoToTabName(tab_name, create, client_id) => {
let client_id = if client_id.is_none() {
None
} else if screen
.active_tab_indices
.contains_key(&client_id.expect("This is checked above"))
{
client_id
} else {
screen.active_tab_indices.keys().next().copied()
};
if let Some(client_id) = client_id {
if let Ok(tab_exists) = screen.go_to_tab_name(tab_name.clone(), client_id) {
screen.unblock_input()?;
screen.render()?;
if create && !tab_exists {
let tab_index = screen.get_new_tab_index();
screen.new_tab(tab_index, client_id)?;
screen
.bus
.senders
.send_to_plugin(PluginInstruction::NewTab(
None,
None,
vec![],
Some(tab_name),
tab_index,
client_id,
))?;
}
}
}
},
ScreenInstruction::UpdateTabName(c, client_id) => {
screen.update_active_tab_name(c, client_id)?;
screen.unblock_input()?;

View file

@ -546,6 +546,40 @@ pub fn switch_to_next_tab() {
);
}
#[test]
pub fn switch_to_tab_name() {
let size = Size {
cols: 121,
rows: 20,
};
let mut screen = create_new_screen(size);
new_tab(&mut screen, 1, 1);
new_tab(&mut screen, 2, 2);
assert_eq!(
screen
.switch_active_tab_name("Tab #1".to_string(), 1)
.expect("TEST"),
false,
"Active tab switched to tab by name"
);
assert_eq!(
screen
.switch_active_tab_name("Tab #2".to_string(), 1)
.expect("TEST"),
true,
"Active tab switched to tab by name"
);
assert_eq!(
screen
.switch_active_tab_name("Tab #3".to_string(), 1)
.expect("TEST"),
true,
"Active tab switched to tab by name"
);
}
#[test]
pub fn close_tab() {
let size = Size {

View file

@ -315,6 +315,13 @@ pub enum CliAction {
CloseTab,
/// Go to tab with index [index]
GoToTab { index: u32 },
/// Go to tab with name [name]
GoToTabName {
name: String,
/// Create a tab if one does not exist.
#[clap(short, long, value_parser)]
create: bool,
},
/// Renames the focused pane
RenameTab { name: String },
/// Remove a previously set tab name

View file

@ -285,6 +285,7 @@ pub enum ScreenContext {
SwitchTabPrev,
CloseTab,
GoToTab,
GoToTabName,
UpdateTabName,
UndoRenameTab,
TerminalResize,

View file

@ -174,6 +174,7 @@ pub enum Action {
/// Close the current tab.
CloseTab,
GoToTab(u32),
GoToTabName(String, bool),
ToggleTab,
TabNameInput(Vec<u8>),
UndoRenameTab,
@ -321,6 +322,7 @@ impl Action {
CliAction::GoToPreviousTab => Ok(vec![Action::GoToPreviousTab]),
CliAction::CloseTab => Ok(vec![Action::CloseTab]),
CliAction::GoToTab { index } => Ok(vec![Action::GoToTab(index)]),
CliAction::GoToTabName { name, create } => Ok(vec![Action::GoToTabName(name, create)]),
CliAction::RenameTab { name } => Ok(vec![
Action::TabNameInput(vec![0]),
Action::TabNameInput(name.as_bytes().to_vec()),