refactor(tabs): lay down infrastructure for multiple users (#864)
* 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 * tests(e2e): update infra and add multiple user mirroring test * refactor(tab): change structs in tabs and terminal panes to support multiple users * style(fmt): make rustfmt happy * style(fmt): make clippy happy
This commit is contained in:
parent
3e07040808
commit
bd795a3e9f
11 changed files with 2274 additions and 1579 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use zellij_tile::data::Palette;
|
use zellij_tile::data::Palette;
|
||||||
|
|
||||||
use zellij_server::panes::TerminalPane;
|
use zellij_server::panes::TerminalPane;
|
||||||
|
|
@ -16,6 +17,7 @@ const CONNECTION_STRING: &str = "127.0.0.1:2222";
|
||||||
const CONNECTION_USERNAME: &str = "test";
|
const CONNECTION_USERNAME: &str = "test";
|
||||||
const CONNECTION_PASSWORD: &str = "test";
|
const CONNECTION_PASSWORD: &str = "test";
|
||||||
const SESSION_NAME: &str = "e2e-test";
|
const SESSION_NAME: &str = "e2e-test";
|
||||||
|
const RETRIES: usize = 10;
|
||||||
|
|
||||||
fn ssh_connect() -> ssh2::Session {
|
fn ssh_connect() -> ssh2::Session {
|
||||||
let tcp = TcpStream::connect(CONNECTION_STRING).unwrap();
|
let tcp = TcpStream::connect(CONNECTION_STRING).unwrap();
|
||||||
|
|
@ -24,7 +26,16 @@ fn ssh_connect() -> ssh2::Session {
|
||||||
sess.handshake().unwrap();
|
sess.handshake().unwrap();
|
||||||
sess.userauth_password(CONNECTION_USERNAME, CONNECTION_PASSWORD)
|
sess.userauth_password(CONNECTION_USERNAME, CONNECTION_PASSWORD)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
sess.set_timeout(3000);
|
sess
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssh_connect_without_timeout() -> ssh2::Session {
|
||||||
|
let tcp = TcpStream::connect(CONNECTION_STRING).unwrap();
|
||||||
|
let mut sess = Session::new().unwrap();
|
||||||
|
sess.set_tcp_stream(tcp);
|
||||||
|
sess.handshake().unwrap();
|
||||||
|
sess.userauth_password(CONNECTION_USERNAME, CONNECTION_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
sess
|
sess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,6 +118,76 @@ fn start_zellij_with_layout(channel: &mut ssh2::Channel, layout_path: &str) {
|
||||||
channel.flush().unwrap();
|
channel.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_from_channel(
|
||||||
|
channel: &Arc<Mutex<ssh2::Channel>>,
|
||||||
|
last_snapshot: &Arc<Mutex<String>>,
|
||||||
|
cursor_coordinates: &Arc<Mutex<(usize, usize)>>,
|
||||||
|
pane_geom: &PaneGeom,
|
||||||
|
) -> (Arc<Mutex<bool>>, std::thread::JoinHandle<()>) {
|
||||||
|
let should_keep_running = Arc::new(Mutex::new(true));
|
||||||
|
let thread = std::thread::Builder::new()
|
||||||
|
.name("read_thread".into())
|
||||||
|
.spawn({
|
||||||
|
let should_keep_running = should_keep_running.clone();
|
||||||
|
let channel = channel.clone();
|
||||||
|
let last_snapshot = last_snapshot.clone();
|
||||||
|
let cursor_coordinates = cursor_coordinates.clone();
|
||||||
|
let mut vte_parser = vte::Parser::new();
|
||||||
|
let mut terminal_output = TerminalPane::new(0, *pane_geom, Palette::default(), 0); // 0 is the pane index
|
||||||
|
let mut retries_left = 3;
|
||||||
|
move || {
|
||||||
|
let mut should_sleep = false;
|
||||||
|
loop {
|
||||||
|
if !*should_keep_running.lock().unwrap() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if should_sleep {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
should_sleep = false;
|
||||||
|
}
|
||||||
|
let mut buf = [0u8; 1280000];
|
||||||
|
match channel.lock().unwrap().read(&mut buf) {
|
||||||
|
Ok(0) => {
|
||||||
|
let current_snapshot = take_snapshot(&mut terminal_output);
|
||||||
|
let mut last_snapshot = last_snapshot.lock().unwrap();
|
||||||
|
*cursor_coordinates.lock().unwrap() =
|
||||||
|
terminal_output.cursor_coordinates().unwrap_or((0, 0));
|
||||||
|
*last_snapshot = current_snapshot;
|
||||||
|
should_sleep = true;
|
||||||
|
}
|
||||||
|
Ok(count) => {
|
||||||
|
for byte in buf.iter().take(count) {
|
||||||
|
vte_parser.advance(&mut terminal_output.grid, *byte);
|
||||||
|
}
|
||||||
|
let current_snapshot = take_snapshot(&mut terminal_output);
|
||||||
|
let mut last_snapshot = last_snapshot.lock().unwrap();
|
||||||
|
*cursor_coordinates.lock().unwrap() =
|
||||||
|
terminal_output.grid.cursor_coordinates().unwrap_or((0, 0));
|
||||||
|
*last_snapshot = current_snapshot;
|
||||||
|
should_sleep = true;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||||
|
let current_snapshot = take_snapshot(&mut terminal_output);
|
||||||
|
let mut last_snapshot = last_snapshot.lock().unwrap();
|
||||||
|
*cursor_coordinates.lock().unwrap() =
|
||||||
|
terminal_output.cursor_coordinates().unwrap_or((0, 0));
|
||||||
|
*last_snapshot = current_snapshot;
|
||||||
|
should_sleep = true;
|
||||||
|
} else if retries_left > 0 {
|
||||||
|
retries_left -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
(should_keep_running, thread)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn take_snapshot(terminal_output: &mut TerminalPane) -> String {
|
pub fn take_snapshot(terminal_output: &mut TerminalPane) -> String {
|
||||||
let output_lines = terminal_output.read_buffer_as_lines();
|
let output_lines = terminal_output.read_buffer_as_lines();
|
||||||
let cursor_coordinates = terminal_output.cursor_coordinates();
|
let cursor_coordinates = terminal_output.cursor_coordinates();
|
||||||
|
|
@ -128,42 +209,44 @@ pub fn take_snapshot(terminal_output: &mut TerminalPane) -> String {
|
||||||
snapshot
|
snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RemoteTerminal<'a> {
|
pub struct RemoteTerminal {
|
||||||
channel: &'a mut ssh2::Channel,
|
channel: Arc<Mutex<ssh2::Channel>>,
|
||||||
cursor_x: usize,
|
cursor_x: usize,
|
||||||
cursor_y: usize,
|
cursor_y: usize,
|
||||||
current_snapshot: String,
|
last_snapshot: Arc<Mutex<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::fmt::Debug for RemoteTerminal<'a> {
|
impl std::fmt::Debug for RemoteTerminal {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"cursor x: {}\ncursor_y: {}\ncurrent_snapshot:\n{}",
|
"cursor x: {}\ncursor_y: {}\ncurrent_snapshot:\n{}",
|
||||||
self.cursor_x, self.cursor_y, self.current_snapshot
|
self.cursor_x,
|
||||||
|
self.cursor_y,
|
||||||
|
*self.last_snapshot.lock().unwrap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RemoteTerminal<'a> {
|
impl RemoteTerminal {
|
||||||
pub fn cursor_position_is(&self, x: usize, y: usize) -> bool {
|
pub fn cursor_position_is(&self, x: usize, y: usize) -> bool {
|
||||||
x == self.cursor_x && y == self.cursor_y
|
x == self.cursor_x && y == self.cursor_y
|
||||||
}
|
}
|
||||||
pub fn tip_appears(&self) -> bool {
|
pub fn tip_appears(&self) -> bool {
|
||||||
self.current_snapshot.contains("Tip:")
|
self.last_snapshot.lock().unwrap().contains("Tip:")
|
||||||
}
|
}
|
||||||
pub fn status_bar_appears(&self) -> bool {
|
pub fn status_bar_appears(&self) -> bool {
|
||||||
self.current_snapshot.contains("Ctrl +")
|
self.last_snapshot.lock().unwrap().contains("Ctrl +")
|
||||||
}
|
}
|
||||||
pub fn snapshot_contains(&self, text: &str) -> bool {
|
pub fn snapshot_contains(&self, text: &str) -> bool {
|
||||||
self.current_snapshot.contains(text)
|
self.last_snapshot.lock().unwrap().contains(text)
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn current_snapshot(&self) -> String {
|
pub fn current_snapshot(&self) -> String {
|
||||||
// convenience method for writing tests,
|
// convenience method for writing tests,
|
||||||
// this should only be used when developing,
|
// this should only be used when developing,
|
||||||
// please prefer "snapsht_contains" instead
|
// please prefer "snapsht_contains" instead
|
||||||
self.current_snapshot.clone()
|
self.last_snapshot.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn current_cursor_position(&self) -> String {
|
pub fn current_cursor_position(&self) -> String {
|
||||||
|
|
@ -173,21 +256,25 @@ impl<'a> RemoteTerminal<'a> {
|
||||||
format!("x: {}, y: {}", self.cursor_x, self.cursor_y)
|
format!("x: {}, y: {}", self.cursor_x, self.cursor_y)
|
||||||
}
|
}
|
||||||
pub fn send_key(&mut self, key: &[u8]) {
|
pub fn send_key(&mut self, key: &[u8]) {
|
||||||
self.channel.write_all(key).unwrap();
|
let mut channel = self.channel.lock().unwrap();
|
||||||
self.channel.flush().unwrap();
|
channel.write_all(key).unwrap();
|
||||||
|
channel.flush().unwrap();
|
||||||
}
|
}
|
||||||
pub fn change_size(&mut self, cols: u32, rows: u32) {
|
pub fn change_size(&mut self, cols: u32, rows: u32) {
|
||||||
self.channel
|
self.channel
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.request_pty_size(cols, rows, Some(cols), Some(rows))
|
.request_pty_size(cols, rows, Some(cols), Some(rows))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
pub fn attach_to_original_session(&mut self) {
|
pub fn attach_to_original_session(&mut self) {
|
||||||
self.channel
|
let mut channel = self.channel.lock().unwrap();
|
||||||
|
channel
|
||||||
.write_all(
|
.write_all(
|
||||||
format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME).as_bytes(),
|
format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME).as_bytes(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.channel.flush().unwrap();
|
channel.flush().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,26 +287,21 @@ pub struct Step {
|
||||||
pub struct RemoteRunner {
|
pub struct RemoteRunner {
|
||||||
steps: Vec<Step>,
|
steps: Vec<Step>,
|
||||||
current_step_index: usize,
|
current_step_index: usize,
|
||||||
vte_parser: vte::Parser,
|
channel: Arc<Mutex<ssh2::Channel>>,
|
||||||
terminal_output: TerminalPane,
|
|
||||||
channel: ssh2::Channel,
|
|
||||||
test_name: &'static str,
|
|
||||||
currently_running_step: Option<String>,
|
currently_running_step: Option<String>,
|
||||||
retries_left: usize,
|
retries_left: usize,
|
||||||
win_size: Size,
|
retry_pause_ms: usize,
|
||||||
layout_file_name: Option<&'static str>,
|
|
||||||
without_frames: bool,
|
|
||||||
session_name: Option<String>,
|
|
||||||
attach_to_existing: bool,
|
|
||||||
panic_on_no_retries_left: bool,
|
panic_on_no_retries_left: bool,
|
||||||
|
last_snapshot: Arc<Mutex<String>>,
|
||||||
|
cursor_coordinates: Arc<Mutex<(usize, usize)>>, // x, y
|
||||||
|
reader_thread: (Arc<Mutex<bool>>, std::thread::JoinHandle<()>),
|
||||||
pub test_timed_out: bool,
|
pub test_timed_out: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteRunner {
|
impl RemoteRunner {
|
||||||
pub fn new(test_name: &'static str, win_size: Size) -> Self {
|
pub fn new(win_size: Size) -> Self {
|
||||||
let sess = ssh_connect();
|
let sess = ssh_connect();
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
|
||||||
let mut rows = Dimension::fixed(win_size.rows);
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
let mut cols = Dimension::fixed(win_size.cols);
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
rows.set_inner(win_size.rows);
|
rows.set_inner(win_size.rows);
|
||||||
|
|
@ -230,35 +312,38 @@ impl RemoteRunner {
|
||||||
rows,
|
rows,
|
||||||
cols,
|
cols,
|
||||||
};
|
};
|
||||||
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
start_zellij(&mut channel);
|
start_zellij(&mut channel);
|
||||||
|
let channel = Arc::new(Mutex::new(channel));
|
||||||
|
let last_snapshot = Arc::new(Mutex::new(String::new()));
|
||||||
|
let cursor_coordinates = Arc::new(Mutex::new((0, 0)));
|
||||||
|
sess.set_blocking(false);
|
||||||
|
let reader_thread =
|
||||||
|
read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom);
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
channel,
|
channel,
|
||||||
terminal_output,
|
|
||||||
vte_parser,
|
|
||||||
test_name,
|
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
retries_left: 10,
|
retries_left: RETRIES,
|
||||||
win_size,
|
retry_pause_ms: 100,
|
||||||
layout_file_name: None,
|
|
||||||
without_frames: false,
|
|
||||||
session_name: None,
|
|
||||||
attach_to_existing: false,
|
|
||||||
test_timed_out: false,
|
test_timed_out: false,
|
||||||
panic_on_no_retries_left: true,
|
panic_on_no_retries_left: true,
|
||||||
|
last_snapshot,
|
||||||
|
cursor_coordinates,
|
||||||
|
reader_thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_with_session_name(
|
pub fn kill_running_sessions(win_size: Size) {
|
||||||
test_name: &'static str,
|
|
||||||
win_size: Size,
|
|
||||||
session_name: &str,
|
|
||||||
) -> Self {
|
|
||||||
let sess = ssh_connect();
|
let sess = ssh_connect();
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
setup_remote_environment(&mut channel, win_size);
|
||||||
|
start_zellij(&mut channel);
|
||||||
|
}
|
||||||
|
pub fn new_with_session_name(win_size: Size, session_name: &str) -> Self {
|
||||||
|
// notice that this method does not have a timeout, so use with caution!
|
||||||
|
let sess = ssh_connect_without_timeout();
|
||||||
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let mut rows = Dimension::fixed(win_size.rows);
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
let mut cols = Dimension::fixed(win_size.cols);
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
rows.set_inner(win_size.rows);
|
rows.set_inner(win_size.rows);
|
||||||
|
|
@ -269,35 +354,31 @@ impl RemoteRunner {
|
||||||
rows,
|
rows,
|
||||||
cols,
|
cols,
|
||||||
};
|
};
|
||||||
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
start_zellij_in_session(&mut channel, session_name);
|
start_zellij_in_session(&mut channel, session_name);
|
||||||
|
let channel = Arc::new(Mutex::new(channel));
|
||||||
|
let last_snapshot = Arc::new(Mutex::new(String::new()));
|
||||||
|
let cursor_coordinates = Arc::new(Mutex::new((0, 0)));
|
||||||
|
sess.set_blocking(false);
|
||||||
|
let reader_thread =
|
||||||
|
read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom);
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
channel,
|
channel,
|
||||||
terminal_output,
|
|
||||||
vte_parser,
|
|
||||||
test_name,
|
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
retries_left: 10,
|
retries_left: RETRIES,
|
||||||
win_size,
|
retry_pause_ms: 100,
|
||||||
layout_file_name: None,
|
|
||||||
without_frames: false,
|
|
||||||
session_name: Some(String::from(session_name)),
|
|
||||||
attach_to_existing: false,
|
|
||||||
test_timed_out: false,
|
test_timed_out: false,
|
||||||
panic_on_no_retries_left: true,
|
panic_on_no_retries_left: true,
|
||||||
|
last_snapshot,
|
||||||
|
cursor_coordinates,
|
||||||
|
reader_thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_existing_session(
|
pub fn new_existing_session(win_size: Size, session_name: &str) -> Self {
|
||||||
test_name: &'static str,
|
let sess = ssh_connect_without_timeout();
|
||||||
win_size: Size,
|
|
||||||
session_name: &str,
|
|
||||||
) -> Self {
|
|
||||||
let sess = ssh_connect();
|
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
|
||||||
let mut rows = Dimension::fixed(win_size.rows);
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
let mut cols = Dimension::fixed(win_size.cols);
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
rows.set_inner(win_size.rows);
|
rows.set_inner(win_size.rows);
|
||||||
|
|
@ -308,31 +389,31 @@ impl RemoteRunner {
|
||||||
rows,
|
rows,
|
||||||
cols,
|
cols,
|
||||||
};
|
};
|
||||||
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
attach_to_existing_session(&mut channel, session_name);
|
attach_to_existing_session(&mut channel, session_name);
|
||||||
|
let channel = Arc::new(Mutex::new(channel));
|
||||||
|
let last_snapshot = Arc::new(Mutex::new(String::new()));
|
||||||
|
let cursor_coordinates = Arc::new(Mutex::new((0, 0)));
|
||||||
|
sess.set_blocking(false);
|
||||||
|
let reader_thread =
|
||||||
|
read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom);
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
channel,
|
channel,
|
||||||
terminal_output,
|
|
||||||
vte_parser,
|
|
||||||
test_name,
|
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
retries_left: 10,
|
retries_left: RETRIES,
|
||||||
win_size,
|
retry_pause_ms: 100,
|
||||||
layout_file_name: None,
|
|
||||||
without_frames: false,
|
|
||||||
session_name: Some(String::from(session_name)),
|
|
||||||
attach_to_existing: true,
|
|
||||||
test_timed_out: false,
|
test_timed_out: false,
|
||||||
panic_on_no_retries_left: true,
|
panic_on_no_retries_left: true,
|
||||||
|
last_snapshot,
|
||||||
|
cursor_coordinates,
|
||||||
|
reader_thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_without_frames(test_name: &'static str, win_size: Size) -> Self {
|
pub fn new_without_frames(win_size: Size) -> Self {
|
||||||
let sess = ssh_connect();
|
let sess = ssh_connect();
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
|
||||||
let mut rows = Dimension::fixed(win_size.rows);
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
let mut cols = Dimension::fixed(win_size.cols);
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
rows.set_inner(win_size.rows);
|
rows.set_inner(win_size.rows);
|
||||||
|
|
@ -343,36 +424,32 @@ impl RemoteRunner {
|
||||||
rows,
|
rows,
|
||||||
cols,
|
cols,
|
||||||
};
|
};
|
||||||
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
start_zellij_without_frames(&mut channel);
|
start_zellij_without_frames(&mut channel);
|
||||||
|
let channel = Arc::new(Mutex::new(channel));
|
||||||
|
let last_snapshot = Arc::new(Mutex::new(String::new()));
|
||||||
|
let cursor_coordinates = Arc::new(Mutex::new((0, 0)));
|
||||||
|
sess.set_blocking(false);
|
||||||
|
let reader_thread =
|
||||||
|
read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom);
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
channel,
|
channel,
|
||||||
terminal_output,
|
|
||||||
vte_parser,
|
|
||||||
test_name,
|
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
retries_left: 10,
|
retries_left: RETRIES,
|
||||||
win_size,
|
retry_pause_ms: 100,
|
||||||
layout_file_name: None,
|
|
||||||
without_frames: true,
|
|
||||||
session_name: None,
|
|
||||||
attach_to_existing: false,
|
|
||||||
test_timed_out: false,
|
test_timed_out: false,
|
||||||
panic_on_no_retries_left: true,
|
panic_on_no_retries_left: true,
|
||||||
|
last_snapshot,
|
||||||
|
cursor_coordinates,
|
||||||
|
reader_thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_with_layout(
|
pub fn new_with_layout(win_size: Size, layout_file_name: &'static str) -> Self {
|
||||||
test_name: &'static str,
|
|
||||||
win_size: Size,
|
|
||||||
layout_file_name: &'static str,
|
|
||||||
) -> Self {
|
|
||||||
let remote_path = Path::new(ZELLIJ_LAYOUT_PATH).join(layout_file_name);
|
let remote_path = Path::new(ZELLIJ_LAYOUT_PATH).join(layout_file_name);
|
||||||
let sess = ssh_connect();
|
let sess = ssh_connect();
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
|
||||||
let mut rows = Dimension::fixed(win_size.rows);
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
let mut cols = Dimension::fixed(win_size.cols);
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
rows.set_inner(win_size.rows);
|
rows.set_inner(win_size.rows);
|
||||||
|
|
@ -383,159 +460,104 @@ impl RemoteRunner {
|
||||||
rows,
|
rows,
|
||||||
cols,
|
cols,
|
||||||
};
|
};
|
||||||
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
start_zellij_with_layout(&mut channel, &remote_path.to_string_lossy());
|
start_zellij_with_layout(&mut channel, &remote_path.to_string_lossy());
|
||||||
|
let channel = Arc::new(Mutex::new(channel));
|
||||||
|
let last_snapshot = Arc::new(Mutex::new(String::new()));
|
||||||
|
let cursor_coordinates = Arc::new(Mutex::new((0, 0)));
|
||||||
|
sess.set_blocking(false);
|
||||||
|
let reader_thread =
|
||||||
|
read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom);
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
channel,
|
channel,
|
||||||
terminal_output,
|
|
||||||
vte_parser,
|
|
||||||
test_name,
|
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
retries_left: 10,
|
retries_left: RETRIES,
|
||||||
win_size,
|
retry_pause_ms: 100,
|
||||||
layout_file_name: Some(layout_file_name),
|
|
||||||
without_frames: false,
|
|
||||||
session_name: None,
|
|
||||||
attach_to_existing: false,
|
|
||||||
test_timed_out: false,
|
test_timed_out: false,
|
||||||
panic_on_no_retries_left: true,
|
panic_on_no_retries_left: true,
|
||||||
|
last_snapshot,
|
||||||
|
cursor_coordinates,
|
||||||
|
reader_thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn dont_panic(mut self) -> Self {
|
pub fn dont_panic(mut self) -> Self {
|
||||||
self.panic_on_no_retries_left = false;
|
self.panic_on_no_retries_left = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
pub fn retry_pause_ms(mut self, retry_pause_ms: usize) -> Self {
|
||||||
|
self.retry_pause_ms = retry_pause_ms;
|
||||||
|
self
|
||||||
|
}
|
||||||
pub fn add_step(mut self, step: Step) -> Self {
|
pub fn add_step(mut self, step: Step) -> Self {
|
||||||
self.steps.push(step);
|
self.steps.push(step);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn replace_steps(&mut self, steps: Vec<Step>) {
|
|
||||||
self.steps = steps;
|
|
||||||
}
|
|
||||||
fn display_informative_error(&mut self) {
|
|
||||||
let test_name = self.test_name;
|
|
||||||
let current_step_name = self.currently_running_step.as_ref().cloned();
|
|
||||||
match current_step_name {
|
|
||||||
Some(current_step) => {
|
|
||||||
let remote_terminal = self.current_remote_terminal_state();
|
|
||||||
eprintln!("Timed out waiting for data on the SSH channel for test {}. Was waiting for step: {}", test_name, current_step);
|
|
||||||
eprintln!("{:?}", remote_terminal);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let remote_terminal = self.current_remote_terminal_state();
|
|
||||||
eprintln!("Timed out waiting for data on the SSH channel for test {}. Haven't begun running steps yet.", test_name);
|
|
||||||
eprintln!("{:?}", remote_terminal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_current_snapshot(&mut self) -> String {
|
|
||||||
take_snapshot(&mut self.terminal_output)
|
|
||||||
}
|
|
||||||
fn current_remote_terminal_state(&mut self) -> RemoteTerminal {
|
|
||||||
let current_snapshot = self.get_current_snapshot();
|
|
||||||
let (cursor_x, cursor_y) = self.terminal_output.cursor_coordinates().unwrap_or((0, 0));
|
|
||||||
RemoteTerminal {
|
|
||||||
cursor_x,
|
|
||||||
cursor_y,
|
|
||||||
current_snapshot,
|
|
||||||
channel: &mut self.channel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn run_next_step(&mut self) {
|
pub fn run_next_step(&mut self) {
|
||||||
if let Some(next_step) = self.steps.get(self.current_step_index) {
|
if let Some(next_step) = self.steps.get(self.current_step_index) {
|
||||||
let current_snapshot = take_snapshot(&mut self.terminal_output);
|
let (cursor_x, cursor_y) = *self.cursor_coordinates.lock().unwrap();
|
||||||
let (cursor_x, cursor_y) = self.terminal_output.cursor_coordinates().unwrap_or((0, 0));
|
|
||||||
let remote_terminal = RemoteTerminal {
|
let remote_terminal = RemoteTerminal {
|
||||||
cursor_x,
|
cursor_x,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
current_snapshot,
|
last_snapshot: self.last_snapshot.clone(),
|
||||||
channel: &mut self.channel,
|
channel: self.channel.clone(),
|
||||||
};
|
};
|
||||||
let instruction = next_step.instruction;
|
let instruction = next_step.instruction;
|
||||||
self.currently_running_step = Some(String::from(next_step.name));
|
self.currently_running_step = Some(String::from(next_step.name));
|
||||||
if instruction(remote_terminal) {
|
if instruction(remote_terminal) {
|
||||||
|
self.retries_left = RETRIES;
|
||||||
self.current_step_index += 1;
|
self.current_step_index += 1;
|
||||||
|
} else {
|
||||||
|
self.retries_left -= 1;
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(self.retry_pause_ms as u64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn steps_left(&self) -> bool {
|
pub fn steps_left(&self) -> bool {
|
||||||
self.steps.get(self.current_step_index).is_some()
|
self.steps.get(self.current_step_index).is_some()
|
||||||
}
|
}
|
||||||
fn restart_test(&mut self) -> String {
|
pub fn take_snapshot_after(&mut self, step: Step) -> String {
|
||||||
if let Some(layout_file_name) = self.layout_file_name.as_ref() {
|
let mut retries_left = RETRIES;
|
||||||
// let mut new_runner = RemoteRunner::new_with_layout(self.test_name, self.win_size, Path::new(&local_layout_path), session_name);
|
let instruction = step.instruction;
|
||||||
let mut new_runner =
|
|
||||||
RemoteRunner::new_with_layout(self.test_name, self.win_size, layout_file_name);
|
|
||||||
new_runner.retries_left = self.retries_left - 1;
|
|
||||||
new_runner.replace_steps(self.steps.clone());
|
|
||||||
drop(std::mem::replace(self, new_runner));
|
|
||||||
} else if self.without_frames {
|
|
||||||
let mut new_runner = RemoteRunner::new_without_frames(self.test_name, self.win_size);
|
|
||||||
new_runner.retries_left = self.retries_left - 1;
|
|
||||||
new_runner.replace_steps(self.steps.clone());
|
|
||||||
drop(std::mem::replace(self, new_runner));
|
|
||||||
} else if self.session_name.is_some() {
|
|
||||||
let mut new_runner = if self.attach_to_existing {
|
|
||||||
RemoteRunner::new_existing_session(
|
|
||||||
self.test_name,
|
|
||||||
self.win_size,
|
|
||||||
self.session_name.as_ref().unwrap(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
RemoteRunner::new_with_session_name(
|
|
||||||
self.test_name,
|
|
||||||
self.win_size,
|
|
||||||
self.session_name.as_ref().unwrap(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
new_runner.retries_left = self.retries_left - 1;
|
|
||||||
new_runner.replace_steps(self.steps.clone());
|
|
||||||
drop(std::mem::replace(self, new_runner));
|
|
||||||
} else {
|
|
||||||
let mut new_runner = RemoteRunner::new(self.test_name, self.win_size);
|
|
||||||
new_runner.retries_left = self.retries_left - 1;
|
|
||||||
new_runner.replace_steps(self.steps.clone());
|
|
||||||
drop(std::mem::replace(self, new_runner));
|
|
||||||
}
|
|
||||||
self.run_all_steps()
|
|
||||||
}
|
|
||||||
pub fn run_all_steps(&mut self) -> String {
|
|
||||||
// returns the last snapshot
|
|
||||||
loop {
|
loop {
|
||||||
let mut buf = [0u8; 1024];
|
if retries_left == 0 {
|
||||||
match self.channel.read(&mut buf) {
|
self.test_timed_out = true;
|
||||||
Ok(0) => break,
|
return self.last_snapshot.lock().unwrap().clone();
|
||||||
Ok(_count) => {
|
|
||||||
for byte in buf.iter() {
|
|
||||||
self.vte_parser
|
|
||||||
.advance(&mut self.terminal_output.grid, *byte);
|
|
||||||
}
|
}
|
||||||
|
let (cursor_x, cursor_y) = *self.cursor_coordinates.lock().unwrap();
|
||||||
|
let remote_terminal = RemoteTerminal {
|
||||||
|
cursor_x,
|
||||||
|
cursor_y,
|
||||||
|
last_snapshot: self.last_snapshot.clone(),
|
||||||
|
channel: self.channel.clone(),
|
||||||
|
};
|
||||||
|
if instruction(remote_terminal) {
|
||||||
|
return self.last_snapshot.lock().unwrap().clone();
|
||||||
|
} else {
|
||||||
|
retries_left -= 1;
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn run_all_steps(&mut self) {
|
||||||
|
loop {
|
||||||
self.run_next_step();
|
self.run_next_step();
|
||||||
if !self.steps_left() {
|
if !self.steps_left() {
|
||||||
break;
|
break;
|
||||||
}
|
} else if self.retries_left == 0 {
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if self.retries_left > 0 {
|
|
||||||
return self.restart_test();
|
|
||||||
}
|
|
||||||
self.test_timed_out = true;
|
self.test_timed_out = true;
|
||||||
if self.panic_on_no_retries_left {
|
break;
|
||||||
self.display_informative_error();
|
|
||||||
panic!("timed out waiting for test: {:?}", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
take_snapshot(&mut self.terminal_output)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for RemoteRunner {
|
impl Drop for RemoteRunner {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let _ = self.channel.close();
|
let _ = self.channel.lock().unwrap().close();
|
||||||
|
let reader_thread_running = &mut self.reader_thread.0;
|
||||||
|
*reader_thread_running.lock().unwrap() = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,4 @@ expression: last_snapshot
|
||||||
│ │
|
│ │
|
||||||
└──────┘
|
└──────┘
|
||||||
Ctrl +
|
Ctrl +
|
||||||
...
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
---
|
---
|
||||||
source: src/tests/integration/e2e.rs
|
source: src/tests/e2e/cases.rs
|
||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
||||||
$ │$ █
|
│$ █ ││$ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
│
|
│ ││ │
|
||||||
|
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
<←↓↑→> Move focus / <n> New / <x> Close / <r> Rename / <s> Sync / <Tab> Toggle / <ENTER> Select pane
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
source: src/tests/e2e/cases.rs
|
||||||
|
expression: second_runner_snapshot
|
||||||
|
|
||||||
|
---
|
||||||
|
Zellij (mirrored_sessions) Tab #1 Tab #2
|
||||||
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
||||||
|
│$ █ ││$ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||||
|
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
|
<←↓↑→> Move focus / <n> New / <x> Close / <r> Rename / <s> Sync / <Tab> Toggle / <ENTER> Select pane
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
source: src/tests/e2e/cases.rs
|
source: src/tests/e2e/cases.rs
|
||||||
expression: last_snapshot
|
expression: first_runner_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij (mirrored_sessions) Tab #1
|
Zellij (mirrored_sessions) Tab #1 Tab #2
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
||||||
│$ ││$ █ │
|
│$ █ ││$ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
|
|
@ -26,4 +26,4 @@ expression: last_snapshot
|
||||||
│ ││ │
|
│ ││ │
|
||||||
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT
|
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.
|
<←↓↑→> Move focus / <n> New / <x> Close / <r> Rename / <s> Sync / <Tab> Toggle / <ENTER> Select pane
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
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.
|
||||||
|
|
@ -348,7 +348,7 @@ impl Screen {
|
||||||
let mut output = Output::default();
|
let mut output = Output::default();
|
||||||
let mut tabs_to_close = vec![];
|
let mut tabs_to_close = vec![];
|
||||||
for (tab_index, tab) in self.tabs.iter_mut() {
|
for (tab_index, tab) in self.tabs.iter_mut() {
|
||||||
if tab.get_active_pane().is_some() {
|
if tab.has_active_panes() {
|
||||||
tab.render(&mut output);
|
tab.render(&mut output);
|
||||||
} else {
|
} else {
|
||||||
tabs_to_close.push(*tab_index);
|
tabs_to_close.push(*tab_index);
|
||||||
|
|
@ -511,20 +511,20 @@ impl Screen {
|
||||||
{
|
{
|
||||||
self.get_active_tab_mut(client_id)
|
self.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clear_active_terminal_scroll();
|
.clear_active_terminal_scroll(client_id);
|
||||||
}
|
}
|
||||||
self.colors = mode_info.palette;
|
self.colors = mode_info.palette;
|
||||||
self.mode_info = mode_info;
|
self.mode_info = mode_info;
|
||||||
for tab in self.tabs.values_mut() {
|
for tab in self.tabs.values_mut() {
|
||||||
tab.mode_info = self.mode_info.clone();
|
tab.mode_info = self.mode_info.clone();
|
||||||
tab.mark_active_pane_for_rerender();
|
tab.mark_active_pane_for_rerender(client_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) {
|
pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) {
|
||||||
if !self
|
if !self
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_focus_left()
|
.move_focus_left(client_id)
|
||||||
{
|
{
|
||||||
self.switch_tab_prev(client_id);
|
self.switch_tab_prev(client_id);
|
||||||
}
|
}
|
||||||
|
|
@ -533,7 +533,7 @@ impl Screen {
|
||||||
if !self
|
if !self
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_focus_right()
|
.move_focus_right(client_id)
|
||||||
{
|
{
|
||||||
self.switch_tab_next(client_id);
|
self.switch_tab_next(client_id);
|
||||||
}
|
}
|
||||||
|
|
@ -597,10 +597,13 @@ pub(crate) fn screen_thread_main(
|
||||||
ScreenInstruction::NewPane(pid, client_or_tab_index) => {
|
ScreenInstruction::NewPane(pid, client_or_tab_index) => {
|
||||||
match client_or_tab_index {
|
match client_or_tab_index {
|
||||||
ClientOrTabIndex::ClientId(client_id) => {
|
ClientOrTabIndex::ClientId(client_id) => {
|
||||||
screen.get_active_tab_mut(client_id).unwrap().new_pane(pid);
|
screen
|
||||||
|
.get_active_tab_mut(client_id)
|
||||||
|
.unwrap()
|
||||||
|
.new_pane(pid, Some(client_id));
|
||||||
}
|
}
|
||||||
ClientOrTabIndex::TabIndex(tab_index) => {
|
ClientOrTabIndex::TabIndex(tab_index) => {
|
||||||
screen.tabs.get_mut(&tab_index).unwrap().new_pane(pid);
|
screen.tabs.get_mut(&tab_index).unwrap().new_pane(pid, None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen
|
screen
|
||||||
|
|
@ -616,7 +619,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.horizontal_split(pid);
|
.horizontal_split(pid, client_id);
|
||||||
screen
|
screen
|
||||||
.bus
|
.bus
|
||||||
.senders
|
.senders
|
||||||
|
|
@ -630,7 +633,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.vertical_split(pid);
|
.vertical_split(pid, client_id);
|
||||||
screen
|
screen
|
||||||
.bus
|
.bus
|
||||||
.senders
|
.senders
|
||||||
|
|
@ -644,26 +647,38 @@ pub(crate) fn screen_thread_main(
|
||||||
let active_tab = screen.get_active_tab_mut(client_id).unwrap();
|
let active_tab = screen.get_active_tab_mut(client_id).unwrap();
|
||||||
match active_tab.is_sync_panes_active() {
|
match active_tab.is_sync_panes_active() {
|
||||||
true => active_tab.write_to_terminals_on_current_tab(bytes),
|
true => active_tab.write_to_terminals_on_current_tab(bytes),
|
||||||
false => active_tab.write_to_active_terminal(bytes),
|
false => active_tab.write_to_active_terminal(bytes, client_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScreenInstruction::ResizeLeft(client_id) => {
|
ScreenInstruction::ResizeLeft(client_id) => {
|
||||||
screen.get_active_tab_mut(client_id).unwrap().resize_left();
|
screen
|
||||||
|
.get_active_tab_mut(client_id)
|
||||||
|
.unwrap()
|
||||||
|
.resize_left(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
ScreenInstruction::ResizeRight(client_id) => {
|
ScreenInstruction::ResizeRight(client_id) => {
|
||||||
screen.get_active_tab_mut(client_id).unwrap().resize_right();
|
screen
|
||||||
|
.get_active_tab_mut(client_id)
|
||||||
|
.unwrap()
|
||||||
|
.resize_right(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
ScreenInstruction::ResizeDown(client_id) => {
|
ScreenInstruction::ResizeDown(client_id) => {
|
||||||
screen.get_active_tab_mut(client_id).unwrap().resize_down();
|
screen
|
||||||
|
.get_active_tab_mut(client_id)
|
||||||
|
.unwrap()
|
||||||
|
.resize_down(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
ScreenInstruction::ResizeUp(client_id) => {
|
ScreenInstruction::ResizeUp(client_id) => {
|
||||||
screen.get_active_tab_mut(client_id).unwrap().resize_up();
|
screen
|
||||||
|
.get_active_tab_mut(client_id)
|
||||||
|
.unwrap()
|
||||||
|
.resize_up(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -671,7 +686,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.resize_increase();
|
.resize_increase(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -679,12 +694,15 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.resize_decrease();
|
.resize_decrease(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
ScreenInstruction::SwitchFocus(client_id) => {
|
ScreenInstruction::SwitchFocus(client_id) => {
|
||||||
screen.get_active_tab_mut(client_id).unwrap().move_focus();
|
screen
|
||||||
|
.get_active_tab_mut(client_id)
|
||||||
|
.unwrap()
|
||||||
|
.move_focus(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -692,7 +710,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.focus_next_pane();
|
.focus_next_pane(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -700,7 +718,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.focus_previous_pane();
|
.focus_previous_pane(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -708,7 +726,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_focus_left();
|
.move_focus_left(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -726,7 +744,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_focus_down();
|
.move_focus_down(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -734,7 +752,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_focus_right();
|
.move_focus_right(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -752,7 +770,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_focus_up();
|
.move_focus_up(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -760,7 +778,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.scroll_active_terminal_up();
|
.scroll_active_terminal_up(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -768,7 +786,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_active_pane();
|
.move_active_pane(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -776,7 +794,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_active_pane_down();
|
.move_active_pane_down(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -784,7 +802,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_active_pane_up();
|
.move_active_pane_up(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -792,7 +810,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_active_pane_right();
|
.move_active_pane_right(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -800,7 +818,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.move_active_pane_left();
|
.move_active_pane_left(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -816,7 +834,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.scroll_active_terminal_down();
|
.scroll_active_terminal_down(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -832,7 +850,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.scroll_active_terminal_to_bottom();
|
.scroll_active_terminal_to_bottom(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -840,7 +858,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.scroll_active_terminal_up_page();
|
.scroll_active_terminal_up_page(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -848,7 +866,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.scroll_active_terminal_down_page();
|
.scroll_active_terminal_down_page(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -856,7 +874,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.scroll_active_terminal_up_half_page();
|
.scroll_active_terminal_up_half_page(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -864,7 +882,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.scroll_active_terminal_down_half_page();
|
.scroll_active_terminal_down_half_page(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -872,7 +890,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clear_active_terminal_scroll();
|
.clear_active_terminal_scroll(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -880,7 +898,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.close_focused_pane();
|
.close_focused_pane(client_id);
|
||||||
screen.update_tabs(); // update_tabs eventually calls render through the plugin thread
|
screen.update_tabs(); // update_tabs eventually calls render through the plugin thread
|
||||||
}
|
}
|
||||||
ScreenInstruction::SetSelectable(id, selectable, tab_index) => {
|
ScreenInstruction::SetSelectable(id, selectable, tab_index) => {
|
||||||
|
|
@ -917,7 +935,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.toggle_active_pane_fullscreen();
|
.toggle_active_pane_fullscreen(client_id);
|
||||||
screen.update_tabs();
|
screen.update_tabs();
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
|
|
@ -1011,7 +1029,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.handle_left_click(&point);
|
.handle_left_click(&point, client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -1019,7 +1037,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.handle_right_click(&point);
|
.handle_right_click(&point, client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -1027,7 +1045,7 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.handle_mouse_release(&point);
|
.handle_mouse_release(&point, client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
@ -1035,12 +1053,15 @@ pub(crate) fn screen_thread_main(
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.handle_mouse_hold(&point);
|
.handle_mouse_hold(&point, client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
ScreenInstruction::Copy(client_id) => {
|
ScreenInstruction::Copy(client_id) => {
|
||||||
screen.get_active_tab(client_id).unwrap().copy_selection();
|
screen
|
||||||
|
.get_active_tab(client_id)
|
||||||
|
.unwrap()
|
||||||
|
.copy_selection(client_id);
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,3 @@
|
||||||
use crate::{
|
|
||||||
logging_pipe::LoggingPipe,
|
|
||||||
panes::PaneId,
|
|
||||||
pty::{ClientOrTabIndex, PtyInstruction},
|
|
||||||
screen::ScreenInstruction,
|
|
||||||
thread_bus::{Bus, ThreadSenders},
|
|
||||||
};
|
|
||||||
use highway::{HighwayHash, PortableHash};
|
use highway::{HighwayHash, PortableHash};
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
@ -25,6 +18,15 @@ use wasmer::{
|
||||||
};
|
};
|
||||||
use wasmer_wasi::{Pipe, WasiEnv, WasiState};
|
use wasmer_wasi::{Pipe, WasiEnv, WasiState};
|
||||||
use zellij_tile::data::{Event, EventType, PluginIds};
|
use zellij_tile::data::{Event, EventType, PluginIds};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
logging_pipe::LoggingPipe,
|
||||||
|
panes::PaneId,
|
||||||
|
pty::{ClientOrTabIndex, PtyInstruction},
|
||||||
|
screen::ScreenInstruction,
|
||||||
|
thread_bus::{Bus, ThreadSenders},
|
||||||
|
};
|
||||||
|
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
consts::ZELLIJ_PROJ_DIR,
|
consts::ZELLIJ_PROJ_DIR,
|
||||||
errors::{ContextType, PluginContext},
|
errors::{ContextType, PluginContext},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue