refactor(screen): more multiple-users groundwork (#788)

* refactor(screen): support multiple mirrored clients

* style(fmt): make rustfmt happy

* style(clippy): make clippy happy

* whitespace

* github, y u no update CI?!

* is this a cache issue?

* is it the checkout cache?

* no cache at all?

* Debug

* fix gototab

* decoment

* gototab none in wasm_vm

* gototab none in wasm_vm

* the fun never ends

* style(fmt): make rustfmt happy
This commit is contained in:
Aram Drevekenin 2021-10-20 10:37:38 +02:00 committed by GitHub
parent 4fa55dbfde
commit a99354a155
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 680 additions and 513 deletions

View file

@ -338,7 +338,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
let mode = mode_info.mode;
session_data
.senders
.send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone()))
.send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone(), client_id))
.unwrap();
session_data
.senders

View file

@ -29,13 +29,19 @@ use zellij_utils::{
pub type VteBytes = Vec<u8>;
#[derive(Clone, Copy, Debug)]
pub enum ClientOrTabIndex {
ClientId(ClientId),
TabIndex(usize),
}
/// Instructions related to PTYs (pseudoterminals).
#[derive(Clone, Debug)]
pub(crate) enum PtyInstruction {
SpawnTerminal(Option<TerminalAction>),
SpawnTerminalVertically(Option<TerminalAction>),
SpawnTerminalHorizontally(Option<TerminalAction>),
UpdateActivePane(Option<PaneId>),
SpawnTerminal(Option<TerminalAction>, ClientOrTabIndex),
SpawnTerminalVertically(Option<TerminalAction>, ClientId),
SpawnTerminalHorizontally(Option<TerminalAction>, ClientId),
UpdateActivePane(Option<PaneId>, ClientId),
NewTab(Option<TerminalAction>, Option<TabLayout>, ClientId),
ClosePane(PaneId),
CloseTab(Vec<PaneId>),
@ -45,10 +51,10 @@ pub(crate) enum PtyInstruction {
impl From<&PtyInstruction> for PtyContext {
fn from(pty_instruction: &PtyInstruction) -> Self {
match *pty_instruction {
PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
PtyInstruction::UpdateActivePane(_) => PtyContext::UpdateActivePane,
PtyInstruction::SpawnTerminal(..) => PtyContext::SpawnTerminal,
PtyInstruction::SpawnTerminalVertically(..) => PtyContext::SpawnTerminalVertically,
PtyInstruction::SpawnTerminalHorizontally(..) => PtyContext::SpawnTerminalHorizontally,
PtyInstruction::UpdateActivePane(..) => PtyContext::UpdateActivePane,
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
PtyInstruction::NewTab(..) => PtyContext::NewTab,
@ -58,7 +64,7 @@ impl From<&PtyInstruction> for PtyContext {
}
pub(crate) struct Pty {
pub active_pane: Option<PaneId>,
pub active_panes: HashMap<ClientId, PaneId>,
pub bus: Bus<PtyInstruction>,
pub id_to_child_pid: HashMap<RawFd, ChildId>,
debug_to_file: bool,
@ -72,29 +78,40 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty((&event).into()));
match event {
PtyInstruction::SpawnTerminal(terminal_action) => {
let pid = pty.spawn_terminal(terminal_action);
PtyInstruction::SpawnTerminal(terminal_action, client_or_tab_index) => {
let pid = pty.spawn_terminal(terminal_action, client_or_tab_index);
pty.bus
.senders
.send_to_screen(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
.send_to_screen(ScreenInstruction::NewPane(
PaneId::Terminal(pid),
client_or_tab_index,
))
.unwrap();
}
PtyInstruction::SpawnTerminalVertically(terminal_action) => {
let pid = pty.spawn_terminal(terminal_action);
PtyInstruction::SpawnTerminalVertically(terminal_action, client_id) => {
let pid =
pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id));
pty.bus
.senders
.send_to_screen(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
.send_to_screen(ScreenInstruction::VerticalSplit(
PaneId::Terminal(pid),
client_id,
))
.unwrap();
}
PtyInstruction::SpawnTerminalHorizontally(terminal_action) => {
let pid = pty.spawn_terminal(terminal_action);
PtyInstruction::SpawnTerminalHorizontally(terminal_action, client_id) => {
let pid =
pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id));
pty.bus
.senders
.send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
.send_to_screen(ScreenInstruction::HorizontalSplit(
PaneId::Terminal(pid),
client_id,
))
.unwrap();
}
PtyInstruction::UpdateActivePane(pane_id) => {
pty.set_active_pane(pane_id);
PtyInstruction::UpdateActivePane(pane_id, client_id) => {
pty.set_active_pane(pane_id, client_id);
}
PtyInstruction::NewTab(terminal_action, tab_layout, client_id) => {
let tab_name = tab_layout.as_ref().and_then(|layout| {
@ -115,11 +132,14 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
// clear current name at first
pty.bus
.senders
.send_to_screen(ScreenInstruction::UpdateTabName(vec![0]))
.send_to_screen(ScreenInstruction::UpdateTabName(vec![0], client_id))
.unwrap();
pty.bus
.senders
.send_to_screen(ScreenInstruction::UpdateTabName(tab_name.into_bytes()))
.send_to_screen(ScreenInstruction::UpdateTabName(
tab_name.into_bytes(),
client_id,
))
.unwrap();
}
}
@ -229,11 +249,14 @@ fn stream_terminal_bytes(
}
async_send_to_screen(senders.clone(), ScreenInstruction::Render).await;
// this is a little hacky, and is because the tests end the file as soon as
// we read everything, rather than hanging until there is new data
// a better solution would be to fix the test fakes, but this will do for now
async_send_to_screen(senders, ScreenInstruction::ClosePane(PaneId::Terminal(pid)))
.await;
// we send ClosePane here so that the screen knows to close this tab if the process
// inside the terminal exited on its own (eg. the user typed "exit<ENTER>" inside a
// bash shell)
async_send_to_screen(
senders,
ScreenInstruction::ClosePane(PaneId::Terminal(pid), None),
)
.await;
}
})
}
@ -241,29 +264,40 @@ fn stream_terminal_bytes(
impl Pty {
pub fn new(bus: Bus<PtyInstruction>, debug_to_file: bool) -> Self {
Pty {
active_pane: None,
active_panes: HashMap::new(),
bus,
id_to_child_pid: HashMap::new(),
debug_to_file,
task_handles: HashMap::new(),
}
}
pub fn get_default_terminal(&self) -> TerminalAction {
pub fn get_default_terminal(&self, client_id: Option<ClientId>) -> TerminalAction {
TerminalAction::RunCommand(RunCommand {
args: vec![],
command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")),
cwd: self
.active_pane
cwd: client_id
.and_then(|client_id| self.active_panes.get(&client_id))
.and_then(|pane| match pane {
PaneId::Plugin(..) => None,
PaneId::Terminal(id) => self.id_to_child_pid.get(&id).and_then(|id| id.shell),
PaneId::Terminal(id) => self.id_to_child_pid.get(id).and_then(|id| id.shell),
})
.and_then(|id| self.bus.os_input.as_ref().map(|input| input.get_cwd(id)))
.flatten(),
})
}
pub fn spawn_terminal(&mut self, terminal_action: Option<TerminalAction>) -> RawFd {
let terminal_action = terminal_action.unwrap_or_else(|| self.get_default_terminal());
pub fn spawn_terminal(
&mut self,
terminal_action: Option<TerminalAction>,
client_or_tab_index: ClientOrTabIndex,
) -> RawFd {
let terminal_action = match client_or_tab_index {
ClientOrTabIndex::ClientId(client_id) => {
terminal_action.unwrap_or_else(|| self.get_default_terminal(Some(client_id)))
}
ClientOrTabIndex::TabIndex(_) => {
terminal_action.unwrap_or_else(|| self.get_default_terminal(None))
}
};
let (pid_primary, child_id): (RawFd, ChildId) = self
.bus
.os_input
@ -286,7 +320,8 @@ impl Pty {
default_shell: Option<TerminalAction>,
client_id: ClientId,
) {
let default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal());
let default_shell =
default_shell.unwrap_or_else(|| self.get_default_terminal(Some(client_id)));
let extracted_run_instructions = layout.extract_run_instructions();
let mut new_pane_pids = vec![];
for run_instruction in extracted_run_instructions {
@ -368,8 +403,10 @@ impl Pty {
self.close_pane(id);
});
}
pub fn set_active_pane(&mut self, pane_id: Option<PaneId>) {
self.active_pane = pane_id;
pub fn set_active_pane(&mut self, pane_id: Option<PaneId>, client_id: ClientId) {
if let Some(pane_id) = pane_id {
self.active_panes.insert(client_id, pane_id);
}
}
}

View file

@ -3,8 +3,11 @@ use std::sync::{Arc, RwLock};
use zellij_utils::zellij_tile::data::Event;
use crate::{
os_input_output::ServerOsApi, pty::PtyInstruction, screen::ScreenInstruction,
wasm_vm::PluginInstruction, ServerInstruction, SessionMetaData, SessionState,
os_input_output::ServerOsApi,
pty::{ClientOrTabIndex, PtyInstruction},
screen::ScreenInstruction,
wasm_vm::PluginInstruction,
ServerInstruction, SessionMetaData, SessionState,
};
use zellij_utils::{
channels::SenderWithContext,
@ -34,17 +37,17 @@ fn route_action(
Action::ToggleTab => {
session
.senders
.send_to_screen(ScreenInstruction::ToggleTab)
.send_to_screen(ScreenInstruction::ToggleTab(client_id))
.unwrap();
}
Action::Write(val) => {
session
.senders
.send_to_screen(ScreenInstruction::ClearScroll)
.send_to_screen(ScreenInstruction::ClearScroll(client_id))
.unwrap();
session
.senders
.send_to_screen(ScreenInstruction::WriteCharacter(val))
.send_to_screen(ScreenInstruction::WriteCharacter(val, client_id))
.unwrap();
}
Action::SwitchToMode(mode) => {
@ -61,11 +64,10 @@ fn route_action(
.unwrap();
session
.senders
.send_to_screen(ScreenInstruction::ChangeMode(get_mode_info(
mode,
palette,
session.capabilities,
)))
.send_to_screen(ScreenInstruction::ChangeMode(
get_mode_info(mode, palette, session.capabilities),
client_id,
))
.unwrap();
session
.senders
@ -74,103 +76,103 @@ fn route_action(
}
Action::Resize(direction) => {
let screen_instr = match direction {
Direction::Left => ScreenInstruction::ResizeLeft,
Direction::Right => ScreenInstruction::ResizeRight,
Direction::Up => ScreenInstruction::ResizeUp,
Direction::Down => ScreenInstruction::ResizeDown,
Direction::Left => ScreenInstruction::ResizeLeft(client_id),
Direction::Right => ScreenInstruction::ResizeRight(client_id),
Direction::Up => ScreenInstruction::ResizeUp(client_id),
Direction::Down => ScreenInstruction::ResizeDown(client_id),
};
session.senders.send_to_screen(screen_instr).unwrap();
}
Action::SwitchFocus => {
session
.senders
.send_to_screen(ScreenInstruction::SwitchFocus)
.send_to_screen(ScreenInstruction::SwitchFocus(client_id))
.unwrap();
}
Action::FocusNextPane => {
session
.senders
.send_to_screen(ScreenInstruction::FocusNextPane)
.send_to_screen(ScreenInstruction::FocusNextPane(client_id))
.unwrap();
}
Action::FocusPreviousPane => {
session
.senders
.send_to_screen(ScreenInstruction::FocusPreviousPane)
.send_to_screen(ScreenInstruction::FocusPreviousPane(client_id))
.unwrap();
}
Action::MoveFocus(direction) => {
let screen_instr = match direction {
Direction::Left => ScreenInstruction::MoveFocusLeft,
Direction::Right => ScreenInstruction::MoveFocusRight,
Direction::Up => ScreenInstruction::MoveFocusUp,
Direction::Down => ScreenInstruction::MoveFocusDown,
Direction::Left => ScreenInstruction::MoveFocusLeft(client_id),
Direction::Right => ScreenInstruction::MoveFocusRight(client_id),
Direction::Up => ScreenInstruction::MoveFocusUp(client_id),
Direction::Down => ScreenInstruction::MoveFocusDown(client_id),
};
session.senders.send_to_screen(screen_instr).unwrap();
}
Action::MoveFocusOrTab(direction) => {
let screen_instr = match direction {
Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab,
Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab,
Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id),
Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab(client_id),
_ => unreachable!(),
};
session.senders.send_to_screen(screen_instr).unwrap();
}
Action::MovePane(direction) => {
let screen_instr = match direction {
Direction::Left => ScreenInstruction::MovePaneLeft,
Direction::Right => ScreenInstruction::MovePaneRight,
Direction::Up => ScreenInstruction::MovePaneUp,
Direction::Down => ScreenInstruction::MovePaneDown,
Direction::Left => ScreenInstruction::MovePaneLeft(client_id),
Direction::Right => ScreenInstruction::MovePaneRight(client_id),
Direction::Up => ScreenInstruction::MovePaneUp(client_id),
Direction::Down => ScreenInstruction::MovePaneDown(client_id),
};
session.senders.send_to_screen(screen_instr).unwrap();
}
Action::ScrollUp => {
session
.senders
.send_to_screen(ScreenInstruction::ScrollUp)
.send_to_screen(ScreenInstruction::ScrollUp(client_id))
.unwrap();
}
Action::ScrollUpAt(point) => {
session
.senders
.send_to_screen(ScreenInstruction::ScrollUpAt(point))
.send_to_screen(ScreenInstruction::ScrollUpAt(point, client_id))
.unwrap();
}
Action::ScrollDown => {
session
.senders
.send_to_screen(ScreenInstruction::ScrollDown)
.send_to_screen(ScreenInstruction::ScrollDown(client_id))
.unwrap();
}
Action::ScrollDownAt(point) => {
session
.senders
.send_to_screen(ScreenInstruction::ScrollDownAt(point))
.send_to_screen(ScreenInstruction::ScrollDownAt(point, client_id))
.unwrap();
}
Action::ScrollToBottom => {
session
.senders
.send_to_screen(ScreenInstruction::ScrollToBottom)
.send_to_screen(ScreenInstruction::ScrollToBottom(client_id))
.unwrap();
}
Action::PageScrollUp => {
session
.senders
.send_to_screen(ScreenInstruction::PageScrollUp)
.send_to_screen(ScreenInstruction::PageScrollUp(client_id))
.unwrap();
}
Action::PageScrollDown => {
session
.senders
.send_to_screen(ScreenInstruction::PageScrollDown)
.send_to_screen(ScreenInstruction::PageScrollDown(client_id))
.unwrap();
}
Action::ToggleFocusFullscreen => {
session
.senders
.send_to_screen(ScreenInstruction::ToggleActiveTerminalFullscreen)
.send_to_screen(ScreenInstruction::ToggleActiveTerminalFullscreen(client_id))
.unwrap();
}
Action::TogglePaneFrames => {
@ -182,31 +184,43 @@ fn route_action(
Action::NewPane(direction) => {
let shell = session.default_shell.clone();
let pty_instr = match direction {
Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(shell),
Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(shell),
Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(shell),
Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(shell),
Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(shell, client_id),
Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(shell, client_id),
Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(shell, client_id),
Some(Direction::Down) => {
PtyInstruction::SpawnTerminalHorizontally(shell, client_id)
}
// No direction specified - try to put it in the biggest available spot
None => PtyInstruction::SpawnTerminal(shell),
None => PtyInstruction::SpawnTerminal(shell, ClientOrTabIndex::ClientId(client_id)),
};
session.senders.send_to_pty(pty_instr).unwrap();
}
Action::Run(command) => {
let run_cmd = Some(TerminalAction::RunCommand(command.clone().into()));
let pty_instr = match command.direction {
Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(run_cmd),
Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(run_cmd),
Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(run_cmd),
Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(run_cmd),
Some(Direction::Left) => {
PtyInstruction::SpawnTerminalVertically(run_cmd, client_id)
}
Some(Direction::Right) => {
PtyInstruction::SpawnTerminalVertically(run_cmd, client_id)
}
Some(Direction::Up) => {
PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id)
}
Some(Direction::Down) => {
PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id)
}
// No direction specified - try to put it in the biggest available spot
None => PtyInstruction::SpawnTerminal(run_cmd),
None => {
PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id))
}
};
session.senders.send_to_pty(pty_instr).unwrap();
}
Action::CloseFocus => {
session
.senders
.send_to_screen(ScreenInstruction::CloseFocusedPane)
.send_to_screen(ScreenInstruction::CloseFocusedPane(client_id))
.unwrap();
}
Action::NewTab(tab_layout) => {
@ -219,37 +233,37 @@ fn route_action(
Action::GoToNextTab => {
session
.senders
.send_to_screen(ScreenInstruction::SwitchTabNext)
.send_to_screen(ScreenInstruction::SwitchTabNext(client_id))
.unwrap();
}
Action::GoToPreviousTab => {
session
.senders
.send_to_screen(ScreenInstruction::SwitchTabPrev)
.send_to_screen(ScreenInstruction::SwitchTabPrev(client_id))
.unwrap();
}
Action::ToggleActiveSyncTab => {
session
.senders
.send_to_screen(ScreenInstruction::ToggleActiveSyncTab)
.send_to_screen(ScreenInstruction::ToggleActiveSyncTab(client_id))
.unwrap();
}
Action::CloseTab => {
session
.senders
.send_to_screen(ScreenInstruction::CloseTab)
.send_to_screen(ScreenInstruction::CloseTab(client_id))
.unwrap();
}
Action::GoToTab(i) => {
session
.senders
.send_to_screen(ScreenInstruction::GoToTab(i))
.send_to_screen(ScreenInstruction::GoToTab(i, Some(client_id)))
.unwrap();
}
Action::TabNameInput(c) => {
session
.senders
.send_to_screen(ScreenInstruction::UpdateTabName(c))
.send_to_screen(ScreenInstruction::UpdateTabName(c, client_id))
.unwrap();
}
Action::Quit => {
@ -267,25 +281,25 @@ fn route_action(
Action::LeftClick(point) => {
session
.senders
.send_to_screen(ScreenInstruction::LeftClick(point))
.send_to_screen(ScreenInstruction::LeftClick(point, client_id))
.unwrap();
}
Action::MouseRelease(point) => {
session
.senders
.send_to_screen(ScreenInstruction::MouseRelease(point))
.send_to_screen(ScreenInstruction::MouseRelease(point, client_id))
.unwrap();
}
Action::MouseHold(point) => {
session
.senders
.send_to_screen(ScreenInstruction::MouseHold(point))
.send_to_screen(ScreenInstruction::MouseHold(point, client_id))
.unwrap();
}
Action::Copy => {
session
.senders
.send_to_screen(ScreenInstruction::Copy)
.send_to_screen(ScreenInstruction::Copy(client_id))
.unwrap();
}
Action::NoOp => {}

File diff suppressed because it is too large Load diff

View file

@ -95,19 +95,16 @@ fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (us
(columns_offset, rows_offset)
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct Output {
pub client_render_instructions: HashMap<ClientId, String>,
}
impl Output {
pub fn new(client_ids: &HashSet<ClientId>) -> Self {
let mut client_render_instructions = HashMap::new();
pub fn add_clients(&mut self, client_ids: &HashSet<ClientId>) {
for client_id in client_ids {
client_render_instructions.insert(*client_id, String::new());
}
Output {
client_render_instructions,
self.client_render_instructions
.insert(*client_id, String::new());
}
}
pub fn push_str_to_all_clients(&mut self, to_push: &str) {
@ -134,7 +131,6 @@ pub(crate) struct Tab {
should_clear_display_before_rendering: bool,
pub mode_info: ModeInfo,
pub colors: Palette,
pub is_active: bool,
connected_clients: HashSet<ClientId>,
draw_pane_frames: bool,
pending_vte_events: HashMap<RawFd, Vec<VteBytes>>,
@ -335,7 +331,6 @@ impl Tab {
colors,
draw_pane_frames,
pending_vte_events: HashMap::new(),
is_active: true,
connected_clients,
}
}
@ -800,14 +795,20 @@ impl Tab {
resize_pty!(pane, self.os_api);
}
}
pub fn render(&mut self) -> Option<Output> {
pub fn render(&mut self, output: &mut Output) {
if self.connected_clients.is_empty() || self.active_terminal.is_none() {
return None;
return;
}
self.senders
.send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal))
.unwrap();
let mut output = Output::new(&self.connected_clients);
for connected_client in self.connected_clients.iter() {
// TODO: move this out of the render function
self.senders
.send_to_pty(PtyInstruction::UpdateActivePane(
self.active_terminal,
*connected_client,
))
.unwrap();
}
output.add_clients(&self.connected_clients);
let mut boundaries = Boundaries::new(self.viewport);
let hide_cursor = "\u{1b}[?25l";
output.push_str_to_all_clients(hide_cursor);
@ -878,7 +879,6 @@ impl Tab {
output.push_str_to_all_clients(hide_cursor);
}
}
Some(output)
}
fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
self.panes.iter()
@ -2409,6 +2409,7 @@ impl Tab {
// if we reached here, this is either the last pane or there's some sort of
// configuration error (eg. we're trying to close a pane surrounded by fixed panes)
self.panes.remove(&id);
self.active_terminal = None;
self.resize_whole_tab(self.display_area);
}
}
@ -2587,7 +2588,8 @@ impl Tab {
}
fn write_selection_to_clipboard(&self, selection: &str) {
let mut output = Output::new(&self.connected_clients);
let mut output = Output::default();
output.add_clients(&self.connected_clients);
output.push_str_to_all_clients(&format!(
"\u{1b}]52;c;{}\u{1b}\\",
base64::encode(selection)

View file

@ -116,7 +116,7 @@ fn open_new_tab() {
assert_eq!(screen.tabs.len(), 2, "Screen now has two tabs");
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab switched to new tab"
);
@ -132,10 +132,10 @@ pub fn switch_to_prev_tab() {
new_tab(&mut screen, 1);
new_tab(&mut screen, 2);
screen.switch_tab_prev();
screen.switch_tab_prev(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
0,
"Active tab switched to previous tab"
);
@ -151,11 +151,11 @@ pub fn switch_to_next_tab() {
new_tab(&mut screen, 1);
new_tab(&mut screen, 2);
screen.switch_tab_prev();
screen.switch_tab_next();
screen.switch_tab_prev(1);
screen.switch_tab_next(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab switched to next tab"
);
@ -171,11 +171,11 @@ pub fn close_tab() {
new_tab(&mut screen, 1);
new_tab(&mut screen, 2);
screen.close_tab();
screen.close_tab(1);
assert_eq!(screen.tabs.len(), 1, "Only one tab left");
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
0,
"Active tab switched to previous tab"
);
@ -197,13 +197,13 @@ pub fn close_the_middle_tab() {
.values()
.map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids()))
.collect::<Vec<_>>());
screen.switch_tab_prev();
screen.switch_tab_prev(1);
dbg!(screen
.tabs
.values()
.map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids()))
.collect::<Vec<_>>());
screen.close_tab();
screen.close_tab(1);
dbg!(screen
.tabs
.values()
@ -212,7 +212,7 @@ pub fn close_the_middle_tab() {
assert_eq!(screen.tabs.len(), 2, "Two tabs left");
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab switched to previous tab"
);
@ -229,11 +229,11 @@ fn move_focus_left_at_left_screen_edge_changes_tab() {
new_tab(&mut screen, 1);
new_tab(&mut screen, 2);
new_tab(&mut screen, 3);
screen.switch_tab_prev();
screen.move_focus_left_or_previous_tab();
screen.switch_tab_prev(1);
screen.move_focus_left_or_previous_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
0,
"Active tab switched to previous"
);
@ -250,11 +250,11 @@ fn move_focus_right_at_right_screen_edge_changes_tab() {
new_tab(&mut screen, 1);
new_tab(&mut screen, 2);
new_tab(&mut screen, 3);
screen.switch_tab_prev();
screen.move_focus_right_or_next_tab();
screen.switch_tab_prev(1);
screen.move_focus_right_or_next_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
2,
"Active tab switched to next"
);
@ -270,19 +270,19 @@ pub fn toggle_to_previous_tab_simple() {
new_tab(&mut screen, 1);
new_tab(&mut screen, 2);
screen.go_to_tab(1);
screen.go_to_tab(2);
screen.go_to_tab(1, 1);
screen.go_to_tab(2, 1);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
0,
"Active tab toggler to previous tab"
);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab toggler to previous tab"
);
@ -301,38 +301,38 @@ pub fn toggle_to_previous_tab_create_tabs_only() {
new_tab(&mut screen, 3);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(1)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 1],
"Tab history is invalid"
);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab toggler to previous tab"
);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(2)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 2],
"Tab history is invalid"
);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
2,
"Active tab toggler to previous tab"
);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(1)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 1],
"Tab history is invalid"
);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab toggler to previous tab"
);
@ -352,84 +352,84 @@ pub fn toggle_to_previous_tab_delete() {
new_tab(&mut screen, 4); // 3
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(1), Some(2)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 1, 2],
"Tab history is invalid"
);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
3,
"Active tab toggler to previous tab"
);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(1), Some(3)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 1, 3],
"Tab history is invalid"
);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
2,
"Active tab toggler to previous tab"
);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(1), Some(2)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 1, 2],
"Tab history is invalid"
);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
3,
"Active tab toggler to previous tab"
);
screen.switch_tab_prev();
screen.switch_tab_prev(1);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(1), Some(3)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 1, 3],
"Tab history is invalid"
);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
2,
"Active tab toggler to previous tab"
);
screen.switch_tab_prev();
screen.switch_tab_prev(1);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(3), Some(2)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 3, 2],
"Tab history is invalid"
);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab toggler to previous tab"
);
screen.close_tab();
screen.close_tab(1);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(3)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 3],
"Tab history is invalid"
);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
1,
"Active tab toggler to previous tab"
);
screen.toggle_tab();
screen.toggle_tab(1);
assert_eq!(
screen.get_active_tab().unwrap().position,
screen.get_active_tab(1).unwrap().position,
2,
"Active tab toggler to previous tab"
);
assert_eq!(
screen.tab_history,
vec![None, Some(0), Some(2)],
screen.tab_history.get(&1).unwrap(),
&vec![0, 2],
"Tab history is invalid"
);
}

View file

@ -20,7 +20,7 @@ use zellij_tile::data::{Event, EventType, PluginIds};
use crate::{
logging_pipe::LoggingPipe,
panes::PaneId,
pty::PtyInstruction,
pty::{ClientOrTabIndex, PtyInstruction},
screen::ScreenInstruction,
thread_bus::{Bus, ThreadSenders},
};
@ -60,6 +60,7 @@ pub(crate) struct PluginEnv {
pub senders: ThreadSenders,
pub wasi_env: WasiEnv,
pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
pub tab_index: usize,
plugin_own_data_dir: PathBuf,
}
@ -208,6 +209,7 @@ fn start_plugin(
wasi_env,
subscriptions: Arc::new(Mutex::new(HashSet::new())),
plugin_own_data_dir,
tab_index,
};
let zellij = zellij_exports(store, &plugin_env);
@ -293,16 +295,17 @@ fn host_open_file(plugin_env: &PluginEnv) {
let path: PathBuf = wasi_read_object(&plugin_env.wasi_env);
plugin_env
.senders
.send_to_pty(PtyInstruction::SpawnTerminal(Some(
TerminalAction::OpenFile(path),
)))
.send_to_pty(PtyInstruction::SpawnTerminal(
Some(TerminalAction::OpenFile(path)),
ClientOrTabIndex::TabIndex(plugin_env.tab_index),
))
.unwrap();
}
fn host_switch_tab_to(plugin_env: &PluginEnv, tab_idx: u32) {
plugin_env
.senders
.send_to_screen(ScreenInstruction::GoToTab(tab_idx))
.send_to_screen(ScreenInstruction::GoToTab(tab_idx, None)) // this is a hack, we should be able to return the client id here
.unwrap();
}