feat: add action to undo rename (#1513)

This commit is contained in:
Jae-Heon Ji 2022-06-18 09:58:42 +09:00 committed by GitHub
parent 8da6207849
commit 8e2be2c61e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 265 additions and 5 deletions

View File

@ -32,6 +32,7 @@ pub const MOVE_FOCUS_DOWN_IN_PANE_MODE: [u8; 1] = [106]; // j
pub const MOVE_FOCUS_UP_IN_PANE_MODE: [u8; 1] = [107]; // k
pub const MOVE_FOCUS_LEFT_IN_PANE_MODE: [u8; 1] = [104]; // h
pub const MOVE_FOCUS_RIGHT_IN_PANE_MODE: [u8; 1] = [108]; // l
pub const RENAME_PANE_MODE: [u8; 1] = [99]; // c
pub const SCROLL_MODE: [u8; 1] = [19]; // ctrl-s
pub const SCROLL_UP_IN_SCROLL_MODE: [u8; 1] = [107]; // k
@ -51,6 +52,7 @@ pub const NEW_TAB_IN_TAB_MODE: [u8; 1] = [110]; // n
pub const SWITCH_NEXT_TAB_IN_TAB_MODE: [u8; 1] = [108]; // l
pub const SWITCH_PREV_TAB_IN_TAB_MODE: [u8; 1] = [104]; // h
pub const CLOSE_TAB_IN_TAB_MODE: [u8; 1] = [120]; // x
pub const RENAME_TAB_MODE: [u8; 1] = [114]; // r
pub const SESSION_MODE: [u8; 1] = [15]; // ctrl-o
pub const DETACH_IN_SESSION_MODE: [u8; 1] = [100]; // d
@ -1853,3 +1855,102 @@ pub fn edit_scrollback() {
};
assert!(last_snapshot.contains(".dump"));
}
#[test]
#[ignore]
pub fn undo_rename_tab() {
let fake_win_size = Size {
cols: 120,
rows: 24,
};
let mut test_attempts = 10;
let last_snapshot = loop {
RemoteRunner::kill_running_sessions(fake_win_size);
let mut runner = RemoteRunner::new(fake_win_size).add_step(Step {
name: "Undo tab name change",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.status_bar_appears()
&& remote_terminal.snapshot_contains("Tab #1")
{
remote_terminal.send_key(&TAB_MODE);
remote_terminal.send_key(&RENAME_TAB_MODE);
remote_terminal.send_key(&[97, 97]);
remote_terminal.send_key(&ESC);
step_is_complete = true;
}
step_is_complete
},
});
runner.run_all_steps();
let last_snapshot = runner.take_snapshot_after(Step {
name: "Wait for tab name to apper on screen",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("Tab #1") {
step_is_complete = true
}
step_is_complete
},
});
if runner.test_timed_out && test_attempts > 0 {
test_attempts -= 1;
continue;
} else {
break last_snapshot;
}
};
assert_snapshot!(last_snapshot);
}
#[test]
#[ignore]
pub fn undo_rename_pane() {
let fake_win_size = Size {
cols: 120,
rows: 24,
};
let mut test_attempts = 10;
let last_snapshot = loop {
RemoteRunner::kill_running_sessions(fake_win_size);
let mut runner = RemoteRunner::new(fake_win_size).add_step(Step {
name: "Undo pane name change",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
remote_terminal.send_key(&RENAME_PANE_MODE);
remote_terminal.send_key(&[97, 97]);
remote_terminal.send_key(&ESC);
step_is_complete = true;
}
step_is_complete
},
});
runner.run_all_steps();
let last_snapshot = runner.take_snapshot_after(Step {
name: "Wait for pane name to apper on screen",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("Pane #1") {
step_is_complete = true
}
step_is_complete
},
});
if runner.test_timed_out && test_attempts > 0 {
test_attempts -= 1;
continue;
} else {
break last_snapshot;
}
};
assert_snapshot!(last_snapshot);
}

View File

@ -0,0 +1,28 @@
---
source: src/tests/e2e/cases.rs
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│$ █ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SCROLL  <o> SESSION  <q> QUIT 
Tip: Alt + <n> => new pane. Alt + <←↓↑→ or hjkl> => navigate. Alt + <+-> => resize pane.

View File

@ -0,0 +1,28 @@
---
source: src/tests/e2e/cases.rs
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│$ █ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SCROLL  <o> SESSION  <q> QUIT 
Tip: Alt + <n> => new pane. Alt + <←↓↑→ or hjkl> => navigate. Alt + <+-> => resize pane.

View File

@ -30,6 +30,7 @@ pub(crate) struct PluginPane {
pub active_at: Instant,
pub pane_title: String,
pub pane_name: String,
prev_pane_name: String,
frame: bool,
borderless: bool,
}
@ -54,7 +55,8 @@ impl PluginPane {
content_offset: Offset::default(),
pane_title: title,
borderless: false,
pane_name,
pane_name: pane_name.clone(),
prev_pane_name: pane_name,
}
}
}
@ -386,6 +388,18 @@ impl Pane for PluginPane {
fn set_content_offset(&mut self, offset: Offset) {
self.content_offset = offset;
}
fn store_pane_name(&mut self) {
if self.pane_name != self.prev_pane_name {
self.prev_pane_name = self.pane_name.clone()
}
}
fn load_pane_name(&mut self) {
if self.pane_name != self.prev_pane_name {
self.pane_name = self.prev_pane_name.clone()
}
}
fn set_borderless(&mut self, borderless: bool) {
self.borderless = borderless;
}

View File

@ -50,6 +50,7 @@ pub struct TerminalPane {
content_offset: Offset,
pane_title: String,
pane_name: String,
prev_pane_name: String,
frame: HashMap<ClientId, PaneFrame>,
borderless: bool,
fake_cursor_locations: HashSet<(usize, usize)>, // (x, y) - these hold a record of previous fake cursors which we need to clear on render
@ -472,6 +473,17 @@ impl Pane for TerminalPane {
self.reflow_lines();
}
fn store_pane_name(&mut self) {
if self.pane_name != self.prev_pane_name {
self.prev_pane_name = self.pane_name.clone()
}
}
fn load_pane_name(&mut self) {
if self.pane_name != self.prev_pane_name {
self.pane_name = self.prev_pane_name.clone()
}
}
fn set_borderless(&mut self, borderless: bool) {
self.borderless = borderless;
}
@ -521,7 +533,8 @@ impl TerminalPane {
style,
selection_scrolled_at: time::Instant::now(),
pane_title: initial_pane_title,
pane_name,
pane_name: pane_name.clone(),
prev_pane_name: pane_name,
borderless: false,
fake_cursor_locations: HashSet::new(),
}

View File

@ -259,6 +259,12 @@ fn route_action(
.send_to_screen(ScreenInstruction::UpdatePaneName(c, client_id))
.unwrap();
},
Action::UndoRenamePane => {
session
.senders
.send_to_screen(ScreenInstruction::UndoRenamePane(client_id))
.unwrap();
},
Action::Run(command) => {
let run_cmd = Some(TerminalAction::RunCommand(command.clone().into()));
let pty_instr = match command.direction {
@ -330,6 +336,12 @@ fn route_action(
.send_to_screen(ScreenInstruction::UpdateTabName(c, client_id))
.unwrap();
},
Action::UndoRenameTab => {
session
.senders
.send_to_screen(ScreenInstruction::UndoRenameTab(client_id))
.unwrap();
},
Action::Quit => {
to_server
.send(ServerInstruction::ClientExit(client_id))

View File

@ -85,6 +85,7 @@ pub enum ScreenInstruction {
SetSelectable(PaneId, bool, usize),
ClosePane(PaneId, Option<ClientId>),
UpdatePaneName(Vec<u8>, ClientId),
UndoRenamePane(ClientId),
NewTab(Layout, Vec<RawFd>, ClientId),
SwitchTabNext(ClientId),
SwitchTabPrev(ClientId),
@ -93,6 +94,7 @@ pub enum ScreenInstruction {
GoToTab(u32, Option<ClientId>), // this Option is a hacky workaround, please do not copy thie behaviour
ToggleTab(ClientId),
UpdateTabName(Vec<u8>, ClientId),
UndoRenameTab(ClientId),
TerminalResize(Size),
TerminalPixelDimensions(PixelDimensions),
TerminalBackgroundColor(String),
@ -168,12 +170,14 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
ScreenInstruction::ClosePane(..) => ScreenContext::ClosePane,
ScreenInstruction::UpdatePaneName(..) => ScreenContext::UpdatePaneName,
ScreenInstruction::UndoRenamePane(..) => ScreenContext::UndoRenamePane,
ScreenInstruction::NewTab(..) => ScreenContext::NewTab,
ScreenInstruction::SwitchTabNext(..) => ScreenContext::SwitchTabNext,
ScreenInstruction::SwitchTabPrev(..) => ScreenContext::SwitchTabPrev,
ScreenInstruction::CloseTab(..) => ScreenContext::CloseTab,
ScreenInstruction::GoToTab(..) => ScreenContext::GoToTab,
ScreenInstruction::UpdateTabName(..) => ScreenContext::UpdateTabName,
ScreenInstruction::UndoRenameTab(..) => ScreenContext::UndoRenameTab,
ScreenInstruction::TerminalResize(..) => ScreenContext::TerminalResize,
ScreenInstruction::TerminalPixelDimensions(..) => {
ScreenContext::TerminalPixelDimensions
@ -742,12 +746,23 @@ impl Screen {
log::error!("Active tab not found for client id: {:?}", client_id);
}
}
pub fn undo_active_rename_tab(&mut self, client_id: ClientId) {
if let Some(active_tab) = self.get_active_tab_mut(client_id) {
if active_tab.name != active_tab.prev_name {
active_tab.name = active_tab.prev_name.clone();
self.update_tabs();
}
} else {
log::error!("Active tab not found for client id: {:?}", client_id);
}
}
pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) {
let previous_mode = self
.mode_info
.get(&client_id)
.unwrap_or(&self.default_mode_info)
.mode;
if previous_mode == InputMode::Scroll
&& (mode_info.mode == InputMode::Normal || mode_info.mode == InputMode::Locked)
{
@ -755,6 +770,23 @@ impl Screen {
active_tab.clear_active_terminal_scroll(client_id);
}
}
if mode_info.mode == InputMode::RenameTab {
if let Some(active_tab) = self.get_active_tab_mut(client_id) {
active_tab.prev_name = active_tab.name.clone();
}
}
if mode_info.mode == InputMode::RenamePane {
if let Some(active_tab) = self.get_active_tab_mut(client_id) {
if let Some(active_pane) =
active_tab.get_active_pane_or_floating_pane_mut(client_id)
{
active_pane.store_pane_name();
}
}
}
self.style = mode_info.style;
self.mode_info.insert(client_id, mode_info.clone());
for tab in self.tabs.values_mut() {
@ -1140,6 +1172,11 @@ pub(crate) fn screen_thread_main(
.update_active_pane_name(c, client_id));
screen.render();
},
ScreenInstruction::UndoRenamePane(client_id) => {
active_tab!(screen, client_id, |tab: &mut Tab| tab
.undo_active_rename_pane(client_id));
screen.render();
},
ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => {
active_tab!(screen, client_id, |tab: &mut Tab| tab
.toggle_active_pane_fullscreen(client_id));
@ -1186,6 +1223,10 @@ pub(crate) fn screen_thread_main(
screen.update_active_tab_name(c, client_id);
screen.render();
},
ScreenInstruction::UndoRenameTab(client_id) => {
screen.undo_active_rename_tab(client_id);
screen.render();
},
ScreenInstruction::TerminalResize(new_size) => {
screen.resize_to_screen(new_size);
screen.render();

View File

@ -70,6 +70,7 @@ pub(crate) struct Tab {
pub index: usize,
pub position: usize,
pub name: String,
pub prev_name: String,
tiled_panes: TiledPanes,
floating_panes: FloatingPanes,
suppressed_panes: HashMap<PaneId, Box<dyn Pane>>,
@ -267,6 +268,8 @@ pub trait Pane {
|| position_on_screen.column() == self.x()
|| position_on_screen.column() == (self.x() + self.cols()).saturating_sub(1)
}
fn store_pane_name(&mut self);
fn load_pane_name(&mut self);
fn set_borderless(&mut self, borderless: bool);
fn borderless(&self) -> bool;
fn handle_right_click(&mut self, _to: &Position, _client_id: ClientId) {}
@ -346,7 +349,8 @@ impl Tab {
tiled_panes,
floating_panes,
suppressed_panes: HashMap::new(),
name,
name: name.clone(),
prev_name: name,
max_panes,
viewport,
display_area,
@ -1932,6 +1936,21 @@ impl Tab {
}
}
pub fn undo_active_rename_pane(&mut self, client_id: ClientId) {
if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) {
let active_terminal = if self.are_floating_panes_visible() {
self.floating_panes
.get_pane_mut(PaneId::Terminal(active_terminal_id))
} else {
self.tiled_panes
.get_pane_mut(PaneId::Terminal(active_terminal_id))
}
.unwrap();
active_terminal.load_pane_name();
}
}
pub fn is_position_inside_viewport(&self, point: &Position) -> bool {
let Position {
line: Line(line),

View File

@ -327,7 +327,7 @@ keybinds:
renametab:
- action: [SwitchToMode: Normal,]
key: [Char: "\n", Ctrl: 'c', Esc]
- action: [TabNameInput: [27] , SwitchToMode: Tab,]
- action: [UndoRenameTab , SwitchToMode: Tab,]
key: [Esc,]
- action: [NewPane: ,]
key: [ Alt: 'n',]
@ -348,7 +348,7 @@ keybinds:
renamepane:
- action: [SwitchToMode: Normal,]
key: [Char: "\n", Ctrl: 'c', Esc]
- action: [PaneNameInput: [27] , SwitchToMode: Pane,]
- action: [UndoRenamePane , SwitchToMode: Pane,]
key: [Esc,]
- action: [NewPane: ,]
key: [ Alt: 'n',]

View File

@ -265,12 +265,14 @@ pub enum ScreenContext {
SetFixedWidth,
ClosePane,
UpdatePaneName,
UndoRenamePane,
NewTab,
SwitchTabNext,
SwitchTabPrev,
CloseTab,
GoToTab,
UpdateTabName,
UndoRenameTab,
TerminalResize,
TerminalPixelDimensions,
TerminalBackgroundColor,

View File

@ -91,6 +91,7 @@ pub enum Action {
/// Close the focus pane.
CloseFocus,
PaneNameInput(Vec<u8>),
UndoRenamePane,
/// Create a new tab, optionally with a specified tab layout.
NewTab(Option<TabLayout>),
/// Do nothing.
@ -104,6 +105,7 @@ pub enum Action {
GoToTab(u32),
ToggleTab,
TabNameInput(Vec<u8>),
UndoRenameTab,
/// Run specified command in new pane.
Run(RunCommandAction),
/// Detach session and exit