feat(scroll): edit scrollback with default editor (#1456)
* initial commit for opening the current buffer in an editor * fix(editor): take hidden panes into consideration when manipulating tiled grid * when closing an edit buffer, take the geometry of the replaced buffer from the closed buffer * if the floating panels are displayed, don't add to hidden panels the current buffer * strategy changing - put the panels inside a suppressed_panels HashMap instead of hidden_panels * Revert "strategy changing - put the panels inside a suppressed_panels HashMap instead of hidden_panels" This reverts commit c52a203a20cf4c87c147be8b9c193ed6458c1038. * remove the floating panes by moving them to the tiled_panes in hidden_panels * feat(edit): open editor to correct line and don't crash when none is set * formatting * feat(edit): use suppressed panes * style(fmt): rustfmt and logs * style(fmt): clean up unused code * test(editor): integration test for suppressing/closing suppressed pane * test(e2e): editor e2e test * style(fmt): rustfmt * feat(edit): update ui and setup * style(fmt): rustfmt * feat(config): allow configuring scrollback_editor explicitly * style(fmt): rustfmt * chore(repo): build after merging Co-authored-by: Aram Drevekenin <aram@poor.dev>
This commit is contained in:
parent
58cc8fb2e1
commit
e1fcf3a6db
36 changed files with 978 additions and 59 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -2726,6 +2726,16 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.6",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "value-bag"
|
name = "value-bag"
|
||||||
version = "1.0.0-alpha.9"
|
version = "1.0.0-alpha.9"
|
||||||
|
|
@ -3262,6 +3272,7 @@ dependencies = [
|
||||||
"typetag",
|
"typetag",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"url",
|
"url",
|
||||||
|
"uuid",
|
||||||
"wasmer",
|
"wasmer",
|
||||||
"wasmer-wasi",
|
"wasmer-wasi",
|
||||||
"zellij-tile",
|
"zellij-tile",
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ ACTIONS
|
||||||
* __MoveFocus: <Direction\>__ - moves focus in the specified direction (Left,
|
* __MoveFocus: <Direction\>__ - moves focus in the specified direction (Left,
|
||||||
Right, Up, Down).
|
Right, Up, Down).
|
||||||
* __DumpScreen: <File\>__ - dumps the screen in the specified file.
|
* __DumpScreen: <File\>__ - dumps the screen in the specified file.
|
||||||
|
* __EditScrollback__ - replaces the current pane with the scrollback buffer.
|
||||||
* __ScrollUp__ - scrolls up 1 line in the focused pane.
|
* __ScrollUp__ - scrolls up 1 line in the focused pane.
|
||||||
* __ScrollDown__ - scrolls down 1 line in the focused pane.
|
* __ScrollDown__ - scrolls down 1 line in the focused pane.
|
||||||
* __PageScrollUp__ - scrolls up 1 page in the focused pane.
|
* __PageScrollUp__ - scrolls up 1 page in the focused pane.
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ pub const SCROLL_UP_IN_SCROLL_MODE: [u8; 1] = [107]; // k
|
||||||
pub const SCROLL_DOWN_IN_SCROLL_MODE: [u8; 1] = [106]; // j
|
pub const SCROLL_DOWN_IN_SCROLL_MODE: [u8; 1] = [106]; // j
|
||||||
pub const SCROLL_PAGE_UP_IN_SCROLL_MODE: [u8; 1] = [2]; // ctrl-b
|
pub const SCROLL_PAGE_UP_IN_SCROLL_MODE: [u8; 1] = [2]; // ctrl-b
|
||||||
pub const SCROLL_PAGE_DOWN_IN_SCROLL_MODE: [u8; 1] = [6]; // ctrl-f
|
pub const SCROLL_PAGE_DOWN_IN_SCROLL_MODE: [u8; 1] = [6]; // ctrl-f
|
||||||
|
pub const EDIT_SCROLLBACK: [u8; 1] = [101]; // e
|
||||||
|
|
||||||
pub const RESIZE_MODE: [u8; 1] = [14]; // ctrl-n
|
pub const RESIZE_MODE: [u8; 1] = [14]; // ctrl-n
|
||||||
pub const RESIZE_DOWN_IN_RESIZE_MODE: [u8; 1] = [106]; // j
|
pub const RESIZE_DOWN_IN_RESIZE_MODE: [u8; 1] = [106]; // j
|
||||||
|
|
@ -1804,3 +1805,51 @@ pub fn tmux_mode() {
|
||||||
};
|
};
|
||||||
assert_snapshot!(last_snapshot);
|
assert_snapshot!(last_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
pub fn edit_scrollback() {
|
||||||
|
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: "Split pane to the right",
|
||||||
|
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(&SCROLL_MODE);
|
||||||
|
remote_terminal.send_key(&EDIT_SCROLLBACK);
|
||||||
|
step_is_complete = true;
|
||||||
|
}
|
||||||
|
step_is_complete
|
||||||
|
},
|
||||||
|
});
|
||||||
|
runner.run_all_steps();
|
||||||
|
let last_snapshot = runner.take_snapshot_after(Step {
|
||||||
|
name: "Wait for editor to appear",
|
||||||
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
|
let mut step_is_complete = false;
|
||||||
|
if remote_terminal.snapshot_contains(".dump") {
|
||||||
|
// the .dump is an indication we get on the bottom line of vi when editing a
|
||||||
|
// file
|
||||||
|
// the temp file name is randomly generated, so we don't assert the whole snapshot
|
||||||
|
step_is_complete = true;
|
||||||
|
}
|
||||||
|
step_is_complete
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if runner.test_timed_out && test_attempts > 0 {
|
||||||
|
test_attempts -= 1;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
break last_snapshot;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert!(last_snapshot.contains(".dump"));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
const ZELLIJ_EXECUTABLE_LOCATION: &str = "/usr/src/zellij/x86_64-unknown-linux-musl/release/zellij";
|
const ZELLIJ_EXECUTABLE_LOCATION: &str = "/usr/src/zellij/x86_64-unknown-linux-musl/release/zellij";
|
||||||
|
const SET_ENV_VARIABLES: &str = "EDITOR=/usr/bin/vi";
|
||||||
const ZELLIJ_LAYOUT_PATH: &str = "/usr/src/zellij/fixtures/layouts";
|
const ZELLIJ_LAYOUT_PATH: &str = "/usr/src/zellij/fixtures/layouts";
|
||||||
const ZELLIJ_DATA_DIR: &str = "/usr/src/zellij/e2e-data";
|
const ZELLIJ_DATA_DIR: &str = "/usr/src/zellij/e2e-data";
|
||||||
const CONNECTION_STRING: &str = "127.0.0.1:2222";
|
const CONNECTION_STRING: &str = "127.0.0.1:2222";
|
||||||
|
|
@ -64,8 +65,8 @@ fn start_zellij(channel: &mut ssh2::Channel) {
|
||||||
channel
|
channel
|
||||||
.write_all(
|
.write_all(
|
||||||
format!(
|
format!(
|
||||||
"{} --session {} --data-dir {}\n",
|
"{} {} --session {} --data-dir {}\n",
|
||||||
ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR
|
SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
|
|
@ -78,8 +79,8 @@ fn start_zellij_mirrored_session(channel: &mut ssh2::Channel) {
|
||||||
channel
|
channel
|
||||||
.write_all(
|
.write_all(
|
||||||
format!(
|
format!(
|
||||||
"{} --session {} --data-dir {} options --mirror-session true\n",
|
"{} {} --session {} --data-dir {} options --mirror-session true\n",
|
||||||
ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR
|
SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
|
|
@ -92,8 +93,12 @@ fn start_zellij_in_session(channel: &mut ssh2::Channel, session_name: &str, mirr
|
||||||
channel
|
channel
|
||||||
.write_all(
|
.write_all(
|
||||||
format!(
|
format!(
|
||||||
"{} --session {} --data-dir {} options --mirror-session {}\n",
|
"{} {} --session {} --data-dir {} options --mirror-session {}\n",
|
||||||
ZELLIJ_EXECUTABLE_LOCATION, session_name, ZELLIJ_DATA_DIR, mirrored
|
SET_ENV_VARIABLES,
|
||||||
|
ZELLIJ_EXECUTABLE_LOCATION,
|
||||||
|
session_name,
|
||||||
|
ZELLIJ_DATA_DIR,
|
||||||
|
mirrored
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
|
|
@ -103,7 +108,13 @@ fn start_zellij_in_session(channel: &mut ssh2::Channel, session_name: &str, mirr
|
||||||
|
|
||||||
fn attach_to_existing_session(channel: &mut ssh2::Channel, session_name: &str) {
|
fn attach_to_existing_session(channel: &mut ssh2::Channel, session_name: &str) {
|
||||||
channel
|
channel
|
||||||
.write_all(format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, session_name).as_bytes())
|
.write_all(
|
||||||
|
format!(
|
||||||
|
"{} {} attach {}\n",
|
||||||
|
SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, session_name
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
channel.flush().unwrap();
|
channel.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -113,8 +124,8 @@ fn start_zellij_without_frames(channel: &mut ssh2::Channel) {
|
||||||
channel
|
channel
|
||||||
.write_all(
|
.write_all(
|
||||||
format!(
|
format!(
|
||||||
"{} --session {} --data-dir {} options --no-pane-frames\n",
|
"{} {} --session {} --data-dir {} options --no-pane-frames\n",
|
||||||
ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR
|
SET_ENV_VARIABLES, ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME, ZELLIJ_DATA_DIR
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
|
|
@ -127,8 +138,12 @@ fn start_zellij_with_layout(channel: &mut ssh2::Channel, layout_path: &str) {
|
||||||
channel
|
channel
|
||||||
.write_all(
|
.write_all(
|
||||||
format!(
|
format!(
|
||||||
"{} --layout {} --session {} --data-dir {}\n",
|
"{} {} --layout {} --session {} --data-dir {}\n",
|
||||||
ZELLIJ_EXECUTABLE_LOCATION, layout_path, SESSION_NAME, ZELLIJ_DATA_DIR
|
SET_ENV_VARIABLES,
|
||||||
|
ZELLIJ_EXECUTABLE_LOCATION,
|
||||||
|
layout_path,
|
||||||
|
SESSION_NAME,
|
||||||
|
ZELLIJ_DATA_DIR
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
source: src/tests/e2e/cases.rs
|
source: src/tests/e2e/cases.rs
|
||||||
|
assertion_line: 295
|
||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij (e2e-test) Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/1 ┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/1 ┐
|
||||||
|
|
@ -26,4 +26,4 @@ expression: last_snapshot
|
||||||
│ ││line19 00000000000000000000000000000000000000000000000000█│
|
│ ││line19 00000000000000000000000000000000000000000000000000█│
|
||||||
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||||
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
|
||||||
<↓↑> Scroll / <PgUp/PgDn> Scroll Page / <u/d> Scroll Half Page / <ENTER> Select pane
|
<↓↑> Scroll / <PgUp/PgDn> Scroll / <u/d> Scroll / <e> Edit / <ENTER> Select pane
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ typetag = "0.1.7"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
close_fds = "0.3.2"
|
close_fds = "0.3.2"
|
||||||
sysinfo = "0.22.5"
|
sysinfo = "0.22.5"
|
||||||
|
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = "1.6.0"
|
insta = "1.6.0"
|
||||||
|
|
|
||||||
|
|
@ -620,6 +620,7 @@ fn init_session(
|
||||||
Some(os_input.clone()),
|
Some(os_input.clone()),
|
||||||
),
|
),
|
||||||
opts.debug,
|
opts.debug,
|
||||||
|
config_options.scrollback_editor.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
move || pty_thread_main(pty, layout)
|
move || pty_thread_main(pty, layout)
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ fn handle_openpty(
|
||||||
///
|
///
|
||||||
fn handle_terminal(
|
fn handle_terminal(
|
||||||
cmd: RunCommand,
|
cmd: RunCommand,
|
||||||
|
failover_cmd: Option<RunCommand>,
|
||||||
orig_termios: termios::Termios,
|
orig_termios: termios::Termios,
|
||||||
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
||||||
) -> (RawFd, RawFd) {
|
) -> (RawFd, RawFd) {
|
||||||
|
|
@ -152,9 +153,12 @@ fn handle_terminal(
|
||||||
// parent.
|
// parent.
|
||||||
match openpty(None, Some(&orig_termios)) {
|
match openpty(None, Some(&orig_termios)) {
|
||||||
Ok(open_pty_res) => handle_openpty(open_pty_res, cmd, quit_cb),
|
Ok(open_pty_res) => handle_openpty(open_pty_res, cmd, quit_cb),
|
||||||
Err(e) => {
|
Err(e) => match failover_cmd {
|
||||||
panic!("failed to start pty{:?}", e);
|
Some(failover_cmd) => handle_terminal(failover_cmd, None, orig_termios, quit_cb),
|
||||||
}
|
None => {
|
||||||
|
panic!("failed to start pty{:?}", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,19 +178,40 @@ pub fn spawn_terminal(
|
||||||
terminal_action: TerminalAction,
|
terminal_action: TerminalAction,
|
||||||
orig_termios: termios::Termios,
|
orig_termios: termios::Termios,
|
||||||
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
||||||
) -> (RawFd, RawFd) {
|
default_editor: Option<PathBuf>,
|
||||||
|
) -> Result<(RawFd, RawFd), &'static str> {
|
||||||
|
let mut failover_cmd_args = None;
|
||||||
let cmd = match terminal_action {
|
let cmd = match terminal_action {
|
||||||
TerminalAction::OpenFile(file_to_open) => {
|
TerminalAction::OpenFile(file_to_open, line_number) => {
|
||||||
if env::var("EDITOR").is_err() && env::var("VISUAL").is_err() {
|
if default_editor.is_none()
|
||||||
panic!("Can't edit files if an editor is not defined. To fix: define the EDITOR or VISUAL environment variables with the path to your editor (eg. /usr/bin/vim)");
|
&& env::var("EDITOR").is_err()
|
||||||
|
&& env::var("VISUAL").is_err()
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
"No Editor found, consider setting a path to one in $EDITOR or $VISUAL",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let command =
|
let command = default_editor.unwrap_or_else(|| {
|
||||||
PathBuf::from(env::var("EDITOR").unwrap_or_else(|_| env::var("VISUAL").unwrap()));
|
PathBuf::from(env::var("EDITOR").unwrap_or_else(|_| env::var("VISUAL").unwrap()))
|
||||||
|
});
|
||||||
|
|
||||||
let args = vec![file_to_open
|
let mut args = vec![];
|
||||||
|
let file_to_open = file_to_open
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
.expect("Not valid Utf8 Encoding")];
|
.expect("Not valid Utf8 Encoding");
|
||||||
|
if let Some(line_number) = line_number {
|
||||||
|
if command.ends_with("vim")
|
||||||
|
|| command.ends_with("nvim")
|
||||||
|
|| command.ends_with("emacs")
|
||||||
|
|| command.ends_with("nano")
|
||||||
|
|| command.ends_with("kak")
|
||||||
|
{
|
||||||
|
failover_cmd_args = Some(vec![file_to_open.clone()]);
|
||||||
|
args.push(format!("+{}", line_number));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args.push(file_to_open);
|
||||||
RunCommand {
|
RunCommand {
|
||||||
command,
|
command,
|
||||||
args,
|
args,
|
||||||
|
|
@ -195,8 +220,15 @@ pub fn spawn_terminal(
|
||||||
}
|
}
|
||||||
TerminalAction::RunCommand(command) => command,
|
TerminalAction::RunCommand(command) => command,
|
||||||
};
|
};
|
||||||
|
let failover_cmd = if let Some(failover_cmd_args) = failover_cmd_args {
|
||||||
|
let mut cmd = cmd.clone();
|
||||||
|
cmd.args = failover_cmd_args;
|
||||||
|
Some(cmd)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
handle_terminal(cmd, orig_termios, quit_cb)
|
Ok(handle_terminal(cmd, failover_cmd, orig_termios, quit_cb))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -245,7 +277,8 @@ pub trait ServerOsApi: Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
terminal_action: TerminalAction,
|
terminal_action: TerminalAction,
|
||||||
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
||||||
) -> (RawFd, RawFd);
|
default_editor: Option<PathBuf>,
|
||||||
|
) -> Result<(RawFd, RawFd), &'static str>;
|
||||||
/// Read bytes from the standard output of the virtual terminal referred to by `fd`.
|
/// Read bytes from the standard output of the virtual terminal referred to by `fd`.
|
||||||
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
||||||
/// Creates an `AsyncReader` that can be used to read from `fd` in an async context
|
/// Creates an `AsyncReader` that can be used to read from `fd` in an async context
|
||||||
|
|
@ -284,9 +317,15 @@ impl ServerOsApi for ServerOsInputOutput {
|
||||||
&self,
|
&self,
|
||||||
terminal_action: TerminalAction,
|
terminal_action: TerminalAction,
|
||||||
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
quit_cb: Box<dyn Fn(PaneId) + Send>,
|
||||||
) -> (RawFd, RawFd) {
|
default_editor: Option<PathBuf>,
|
||||||
|
) -> Result<(RawFd, RawFd), &'static str> {
|
||||||
let orig_termios = self.orig_termios.lock().unwrap();
|
let orig_termios = self.orig_termios.lock().unwrap();
|
||||||
spawn_terminal(terminal_action, orig_termios.clone(), quit_cb)
|
spawn_terminal(
|
||||||
|
terminal_action,
|
||||||
|
orig_termios.clone(),
|
||||||
|
quit_cb,
|
||||||
|
default_editor,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
|
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
unistd::read(fd, buf)
|
unistd::read(fd, buf)
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,49 @@ impl FloatingPanes {
|
||||||
self.panes.insert(pane_id, pane);
|
self.panes.insert(pane_id, pane);
|
||||||
self.z_indices.push(pane_id);
|
self.z_indices.push(pane_id);
|
||||||
}
|
}
|
||||||
|
pub fn replace_active_pane(
|
||||||
|
&mut self,
|
||||||
|
pane: Box<dyn Pane>,
|
||||||
|
client_id: ClientId,
|
||||||
|
) -> Option<Box<dyn Pane>> {
|
||||||
|
self.active_panes
|
||||||
|
.get(&client_id)
|
||||||
|
.copied()
|
||||||
|
.and_then(|active_pane_id| self.replace_pane(active_pane_id, pane))
|
||||||
|
}
|
||||||
|
pub fn replace_pane(
|
||||||
|
&mut self,
|
||||||
|
pane_id: PaneId,
|
||||||
|
mut with_pane: Box<dyn Pane>,
|
||||||
|
) -> Option<Box<dyn Pane>> {
|
||||||
|
let with_pane_id = with_pane.pid();
|
||||||
|
with_pane.set_content_offset(Offset::frame(1));
|
||||||
|
let removed_pane = self.panes.remove(&pane_id).map(|removed_pane| {
|
||||||
|
let removed_pane_id = removed_pane.pid();
|
||||||
|
let with_pane_id = with_pane.pid();
|
||||||
|
let removed_pane_geom = removed_pane.current_geom();
|
||||||
|
with_pane.set_geom(removed_pane_geom);
|
||||||
|
self.panes.insert(with_pane_id, with_pane);
|
||||||
|
let z_index = self
|
||||||
|
.z_indices
|
||||||
|
.iter()
|
||||||
|
.position(|pane_id| pane_id == &removed_pane_id)
|
||||||
|
.unwrap();
|
||||||
|
self.z_indices.remove(z_index);
|
||||||
|
self.z_indices.insert(z_index, with_pane_id);
|
||||||
|
removed_pane
|
||||||
|
});
|
||||||
|
|
||||||
|
// update the desired_pane_positions to relate to the new pane
|
||||||
|
if let Some(desired_pane_position) = self.desired_pane_positions.remove(&pane_id) {
|
||||||
|
self.desired_pane_positions
|
||||||
|
.insert(with_pane_id, desired_pane_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move clients from the previously active pane to the new pane we just inserted
|
||||||
|
self.move_clients_between_panes(pane_id, with_pane_id);
|
||||||
|
removed_pane
|
||||||
|
}
|
||||||
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
||||||
self.z_indices.retain(|p_id| *p_id != pane_id);
|
self.z_indices.retain(|p_id| *p_id != pane_id);
|
||||||
self.desired_pane_positions.remove(&pane_id);
|
self.desired_pane_positions.remove(&pane_id);
|
||||||
|
|
@ -241,6 +284,11 @@ impl FloatingPanes {
|
||||||
floating_pane_grid.resize(new_screen_size);
|
floating_pane_grid.resize(new_screen_size);
|
||||||
self.set_force_render();
|
self.set_force_render();
|
||||||
}
|
}
|
||||||
|
pub fn resize_pty_all_panes(&mut self, os_api: &mut Box<dyn ServerOsApi>) {
|
||||||
|
for pane in self.panes.values_mut() {
|
||||||
|
resize_pty!(pane, os_api);
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn resize_active_pane_left(
|
pub fn resize_active_pane_left(
|
||||||
&mut self,
|
&mut self,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
|
|
@ -875,4 +923,16 @@ impl FloatingPanes {
|
||||||
pub fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
pub fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
||||||
self.panes.iter()
|
self.panes.iter()
|
||||||
}
|
}
|
||||||
|
fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) {
|
||||||
|
let clients_in_pane: Vec<ClientId> = self
|
||||||
|
.active_panes
|
||||||
|
.iter()
|
||||||
|
.filter(|(_cid, pid)| **pid == from_pane_id)
|
||||||
|
.map(|(cid, _pid)| *cid)
|
||||||
|
.collect();
|
||||||
|
for client_id in clients_in_pane {
|
||||||
|
self.active_panes.remove(&client_id);
|
||||||
|
self.active_panes.insert(client_id, to_pane_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1460,6 +1460,9 @@ impl Grid {
|
||||||
Some(selection.join("\n"))
|
Some(selection.join("\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn absolute_position_in_scrollback(&self) -> usize {
|
||||||
|
self.lines_above.len() + self.cursor.y
|
||||||
|
}
|
||||||
|
|
||||||
fn update_selected_lines(&mut self, old_selection: &Selection, new_selection: &Selection) {
|
fn update_selected_lines(&mut self, old_selection: &Selection, new_selection: &Selection) {
|
||||||
for l in old_selection.diff(new_selection, self.height) {
|
for l in old_selection.diff(new_selection, self.height) {
|
||||||
|
|
|
||||||
|
|
@ -481,6 +481,10 @@ impl Pane for TerminalPane {
|
||||||
fn mouse_mode(&self) -> bool {
|
fn mouse_mode(&self) -> bool {
|
||||||
self.grid.mouse_mode
|
self.grid.mouse_mode
|
||||||
}
|
}
|
||||||
|
fn get_line_number(&self) -> Option<usize> {
|
||||||
|
// + 1 because the absolute position in the scrollback is 0 indexed and this should be 1 indexed
|
||||||
|
Some(self.grid.absolute_position_in_scrollback() + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalPane {
|
impl TerminalPane {
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,58 @@ impl TiledPanes {
|
||||||
os_api,
|
os_api,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, pane: Box<dyn Pane>) {
|
pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
|
||||||
|
if self.draw_pane_frames {
|
||||||
|
pane.set_content_offset(Offset::frame(1));
|
||||||
|
}
|
||||||
self.panes.insert(pane_id, pane);
|
self.panes.insert(pane_id, pane);
|
||||||
}
|
}
|
||||||
|
pub fn replace_active_pane(
|
||||||
|
&mut self,
|
||||||
|
pane: Box<dyn Pane>,
|
||||||
|
client_id: ClientId,
|
||||||
|
) -> Option<Box<dyn Pane>> {
|
||||||
|
let pane_id = pane.pid();
|
||||||
|
// remove the currently active pane
|
||||||
|
let previously_active_pane = self
|
||||||
|
.active_panes
|
||||||
|
.get(&client_id)
|
||||||
|
.copied()
|
||||||
|
.and_then(|active_pane_id| self.replace_pane(active_pane_id, pane));
|
||||||
|
|
||||||
|
// move clients from the previously active pane to the new pane we just inserted
|
||||||
|
if let Some(previously_active_pane) = previously_active_pane.as_ref() {
|
||||||
|
let previously_active_pane_id = previously_active_pane.pid();
|
||||||
|
self.move_clients_between_panes(previously_active_pane_id, pane_id);
|
||||||
|
}
|
||||||
|
previously_active_pane
|
||||||
|
}
|
||||||
|
pub fn replace_pane(
|
||||||
|
&mut self,
|
||||||
|
pane_id: PaneId,
|
||||||
|
mut with_pane: Box<dyn Pane>,
|
||||||
|
) -> Option<Box<dyn Pane>> {
|
||||||
|
let with_pane_id = with_pane.pid();
|
||||||
|
if self.draw_pane_frames {
|
||||||
|
with_pane.set_content_offset(Offset::frame(1));
|
||||||
|
}
|
||||||
|
let removed_pane = self.panes.remove(&pane_id).map(|removed_pane| {
|
||||||
|
let with_pane_id = with_pane.pid();
|
||||||
|
let removed_pane_geom = removed_pane.current_geom();
|
||||||
|
with_pane.set_geom(removed_pane_geom);
|
||||||
|
self.panes.insert(with_pane_id, with_pane);
|
||||||
|
removed_pane
|
||||||
|
});
|
||||||
|
|
||||||
|
// move clients from the previously active pane to the new pane we just inserted
|
||||||
|
self.move_clients_between_panes(pane_id, with_pane_id);
|
||||||
|
removed_pane
|
||||||
|
}
|
||||||
pub fn insert_pane(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
|
pub fn insert_pane(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
|
||||||
let cursor_height_width_ratio = self.cursor_height_width_ratio();
|
let cursor_height_width_ratio = self.cursor_height_width_ratio();
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -133,6 +178,7 @@ impl TiledPanes {
|
||||||
let cursor_height_width_ratio = self.cursor_height_width_ratio();
|
let cursor_height_width_ratio = self.cursor_height_width_ratio();
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -166,6 +212,7 @@ impl TiledPanes {
|
||||||
pub fn relayout(&mut self, direction: Direction) {
|
pub fn relayout(&mut self, direction: Direction) {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -399,12 +446,13 @@ impl TiledPanes {
|
||||||
{
|
{
|
||||||
let mut display_area = self.display_area.borrow_mut();
|
let mut display_area = self.display_area.borrow_mut();
|
||||||
let mut viewport = self.viewport.borrow_mut();
|
let mut viewport = self.viewport.borrow_mut();
|
||||||
let panes = self
|
|
||||||
.panes
|
|
||||||
.iter_mut()
|
|
||||||
.filter(|(pid, _)| !self.panes_to_hide.contains(pid));
|
|
||||||
let Size { rows, cols } = new_screen_size;
|
let Size { rows, cols } = new_screen_size;
|
||||||
let mut pane_grid = TiledPaneGrid::new(panes, *display_area, *viewport);
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
|
*display_area,
|
||||||
|
*viewport,
|
||||||
|
);
|
||||||
if pane_grid.layout(Direction::Horizontal, cols).is_ok() {
|
if pane_grid.layout(Direction::Horizontal, cols).is_ok() {
|
||||||
let column_difference = cols as isize - display_area.cols as isize;
|
let column_difference = cols as isize - display_area.cols as isize;
|
||||||
// FIXME: Should the viewport be an Offset?
|
// FIXME: Should the viewport be an Offset?
|
||||||
|
|
@ -427,6 +475,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -440,6 +489,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -453,6 +503,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -466,6 +517,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -479,6 +531,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -492,6 +545,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -507,6 +561,7 @@ impl TiledPanes {
|
||||||
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -522,6 +577,7 @@ impl TiledPanes {
|
||||||
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -547,6 +603,7 @@ impl TiledPanes {
|
||||||
Some(active_pane_id) => {
|
Some(active_pane_id) => {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -587,6 +644,7 @@ impl TiledPanes {
|
||||||
Some(active_pane_id) => {
|
Some(active_pane_id) => {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -627,6 +685,7 @@ impl TiledPanes {
|
||||||
Some(active_pane_id) => {
|
Some(active_pane_id) => {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -667,6 +726,7 @@ impl TiledPanes {
|
||||||
Some(active_pane_id) => {
|
Some(active_pane_id) => {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -706,6 +766,7 @@ impl TiledPanes {
|
||||||
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -736,6 +797,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -770,6 +832,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -804,6 +867,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -838,6 +902,7 @@ impl TiledPanes {
|
||||||
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
|
||||||
let pane_grid = TiledPaneGrid::new(
|
let pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -877,6 +942,7 @@ impl TiledPanes {
|
||||||
match self
|
match self
|
||||||
.panes
|
.panes
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|(p_id, _)| !self.panes_to_hide.contains(p_id))
|
||||||
.find(|(p_id, p)| **p_id != pane_id && p.selectable())
|
.find(|(p_id, p)| **p_id != pane_id && p.selectable())
|
||||||
.map(|(p_id, _p)| p_id)
|
.map(|(p_id, _p)| p_id)
|
||||||
{
|
{
|
||||||
|
|
@ -890,9 +956,13 @@ impl TiledPanes {
|
||||||
None => self.active_panes.clear(),
|
None => self.active_panes.clear(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn extract_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
||||||
|
self.panes.remove(&pane_id)
|
||||||
|
}
|
||||||
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
||||||
let mut pane_grid = TiledPaneGrid::new(
|
let mut pane_grid = TiledPaneGrid::new(
|
||||||
&mut self.panes,
|
&mut self.panes,
|
||||||
|
&self.panes_to_hide,
|
||||||
*self.display_area.borrow(),
|
*self.display_area.borrow(),
|
||||||
*self.viewport.borrow(),
|
*self.viewport.borrow(),
|
||||||
);
|
);
|
||||||
|
|
@ -1015,6 +1085,24 @@ impl TiledPanes {
|
||||||
pub fn panes_to_hide_count(&self) -> usize {
|
pub fn panes_to_hide_count(&self) -> usize {
|
||||||
self.panes_to_hide.len()
|
self.panes_to_hide.len()
|
||||||
}
|
}
|
||||||
|
pub fn add_to_hidden_panels(&mut self, pid: PaneId) {
|
||||||
|
self.panes_to_hide.insert(pid);
|
||||||
|
}
|
||||||
|
pub fn remove_from_hidden_panels(&mut self, pid: PaneId) {
|
||||||
|
self.panes_to_hide.remove(&pid);
|
||||||
|
}
|
||||||
|
fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) {
|
||||||
|
let clients_in_pane: Vec<ClientId> = self
|
||||||
|
.active_panes
|
||||||
|
.iter()
|
||||||
|
.filter(|(_cid, pid)| **pid == from_pane_id)
|
||||||
|
.map(|(cid, _pid)| *cid)
|
||||||
|
.collect();
|
||||||
|
for client_id in clients_in_pane {
|
||||||
|
self.active_panes.remove(&client_id);
|
||||||
|
self.active_panes.insert(client_id, to_pane_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::borrowed_box)]
|
#[allow(clippy::borrowed_box)]
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,15 @@ pub struct TiledPaneGrid<'a> {
|
||||||
impl<'a> TiledPaneGrid<'a> {
|
impl<'a> TiledPaneGrid<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
panes: impl IntoIterator<Item = (&'a PaneId, &'a mut Box<dyn Pane>)>,
|
panes: impl IntoIterator<Item = (&'a PaneId, &'a mut Box<dyn Pane>)>,
|
||||||
|
panes_to_hide: &HashSet<PaneId>,
|
||||||
display_area: Size,
|
display_area: Size,
|
||||||
viewport: Viewport,
|
viewport: Viewport,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let panes: HashMap<_, _> = panes.into_iter().map(|(p_id, p)| (*p_id, p)).collect();
|
let panes: HashMap<_, _> = panes
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(p_id, _)| !panes_to_hide.contains(p_id))
|
||||||
|
.map(|(p_id, p)| (*p_id, p))
|
||||||
|
.collect();
|
||||||
TiledPaneGrid {
|
TiledPaneGrid {
|
||||||
panes: Rc::new(RefCell::new(panes)),
|
panes: Rc::new(RefCell::new(panes)),
|
||||||
display_area,
|
display_area,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ pub enum ClientOrTabIndex {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum PtyInstruction {
|
pub(crate) enum PtyInstruction {
|
||||||
SpawnTerminal(Option<TerminalAction>, ClientOrTabIndex),
|
SpawnTerminal(Option<TerminalAction>, ClientOrTabIndex),
|
||||||
|
OpenInPlaceEditor(PathBuf, Option<usize>, ClientId), // Option<usize> is the optional line number
|
||||||
SpawnTerminalVertically(Option<TerminalAction>, ClientId),
|
SpawnTerminalVertically(Option<TerminalAction>, ClientId),
|
||||||
SpawnTerminalHorizontally(Option<TerminalAction>, ClientId),
|
SpawnTerminalHorizontally(Option<TerminalAction>, ClientId),
|
||||||
UpdateActivePane(Option<PaneId>, ClientId),
|
UpdateActivePane(Option<PaneId>, ClientId),
|
||||||
|
|
@ -55,6 +56,7 @@ impl From<&PtyInstruction> for PtyContext {
|
||||||
fn from(pty_instruction: &PtyInstruction) -> Self {
|
fn from(pty_instruction: &PtyInstruction) -> Self {
|
||||||
match *pty_instruction {
|
match *pty_instruction {
|
||||||
PtyInstruction::SpawnTerminal(..) => PtyContext::SpawnTerminal,
|
PtyInstruction::SpawnTerminal(..) => PtyContext::SpawnTerminal,
|
||||||
|
PtyInstruction::OpenInPlaceEditor(..) => PtyContext::OpenInPlaceEditor,
|
||||||
PtyInstruction::SpawnTerminalVertically(..) => PtyContext::SpawnTerminalVertically,
|
PtyInstruction::SpawnTerminalVertically(..) => PtyContext::SpawnTerminalVertically,
|
||||||
PtyInstruction::SpawnTerminalHorizontally(..) => PtyContext::SpawnTerminalHorizontally,
|
PtyInstruction::SpawnTerminalHorizontally(..) => PtyContext::SpawnTerminalHorizontally,
|
||||||
PtyInstruction::UpdateActivePane(..) => PtyContext::UpdateActivePane,
|
PtyInstruction::UpdateActivePane(..) => PtyContext::UpdateActivePane,
|
||||||
|
|
@ -73,6 +75,7 @@ pub(crate) struct Pty {
|
||||||
pub id_to_child_pid: HashMap<RawFd, RawFd>, // pty_primary => child raw fd
|
pub id_to_child_pid: HashMap<RawFd, RawFd>, // pty_primary => child raw fd
|
||||||
debug_to_file: bool,
|
debug_to_file: bool,
|
||||||
task_handles: HashMap<RawFd, JoinHandle<()>>,
|
task_handles: HashMap<RawFd, JoinHandle<()>>,
|
||||||
|
default_editor: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
@ -83,7 +86,9 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<LayoutFromYaml>) {
|
||||||
err_ctx.add_call(ContextType::Pty((&event).into()));
|
err_ctx.add_call(ContextType::Pty((&event).into()));
|
||||||
match event {
|
match event {
|
||||||
PtyInstruction::SpawnTerminal(terminal_action, client_or_tab_index) => {
|
PtyInstruction::SpawnTerminal(terminal_action, client_or_tab_index) => {
|
||||||
let pid = pty.spawn_terminal(terminal_action, client_or_tab_index);
|
let pid = pty
|
||||||
|
.spawn_terminal(terminal_action, client_or_tab_index)
|
||||||
|
.unwrap(); // TODO: handle error here
|
||||||
pty.bus
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::NewPane(
|
.send_to_screen(ScreenInstruction::NewPane(
|
||||||
|
|
@ -92,9 +97,29 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<LayoutFromYaml>) {
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
PtyInstruction::OpenInPlaceEditor(temp_file, line_number, client_id) => {
|
||||||
|
match pty.spawn_terminal(
|
||||||
|
Some(TerminalAction::OpenFile(temp_file, line_number)),
|
||||||
|
ClientOrTabIndex::ClientId(client_id),
|
||||||
|
) {
|
||||||
|
Ok(pid) => {
|
||||||
|
pty.bus
|
||||||
|
.senders
|
||||||
|
.send_to_screen(ScreenInstruction::OpenInPlaceEditor(
|
||||||
|
PaneId::Terminal(pid),
|
||||||
|
client_id,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to open editor: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
PtyInstruction::SpawnTerminalVertically(terminal_action, client_id) => {
|
PtyInstruction::SpawnTerminalVertically(terminal_action, client_id) => {
|
||||||
let pid =
|
let pid = pty
|
||||||
pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id));
|
.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id))
|
||||||
|
.unwrap(); // TODO: handle error here
|
||||||
pty.bus
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::VerticalSplit(
|
.send_to_screen(ScreenInstruction::VerticalSplit(
|
||||||
|
|
@ -104,8 +129,9 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<LayoutFromYaml>) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
PtyInstruction::SpawnTerminalHorizontally(terminal_action, client_id) => {
|
PtyInstruction::SpawnTerminalHorizontally(terminal_action, client_id) => {
|
||||||
let pid =
|
let pid = pty
|
||||||
pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id));
|
.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id))
|
||||||
|
.unwrap(); // TODO: handle error here
|
||||||
pty.bus
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::HorizontalSplit(
|
.send_to_screen(ScreenInstruction::HorizontalSplit(
|
||||||
|
|
@ -267,13 +293,18 @@ fn stream_terminal_bytes(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pty {
|
impl Pty {
|
||||||
pub fn new(bus: Bus<PtyInstruction>, debug_to_file: bool) -> Self {
|
pub fn new(
|
||||||
|
bus: Bus<PtyInstruction>,
|
||||||
|
debug_to_file: bool,
|
||||||
|
default_editor: Option<PathBuf>,
|
||||||
|
) -> Self {
|
||||||
Pty {
|
Pty {
|
||||||
active_panes: HashMap::new(),
|
active_panes: HashMap::new(),
|
||||||
bus,
|
bus,
|
||||||
id_to_child_pid: HashMap::new(),
|
id_to_child_pid: HashMap::new(),
|
||||||
debug_to_file,
|
debug_to_file,
|
||||||
task_handles: HashMap::new(),
|
task_handles: HashMap::new(),
|
||||||
|
default_editor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_default_terminal(&self) -> TerminalAction {
|
pub fn get_default_terminal(&self) -> TerminalAction {
|
||||||
|
|
@ -306,7 +337,7 @@ impl Pty {
|
||||||
&mut self,
|
&mut self,
|
||||||
terminal_action: Option<TerminalAction>,
|
terminal_action: Option<TerminalAction>,
|
||||||
client_or_tab_index: ClientOrTabIndex,
|
client_or_tab_index: ClientOrTabIndex,
|
||||||
) -> RawFd {
|
) -> Result<RawFd, &'static str> {
|
||||||
let terminal_action = match client_or_tab_index {
|
let terminal_action = match client_or_tab_index {
|
||||||
ClientOrTabIndex::ClientId(client_id) => {
|
ClientOrTabIndex::ClientId(client_id) => {
|
||||||
let mut terminal_action =
|
let mut terminal_action =
|
||||||
|
|
@ -329,7 +360,7 @@ impl Pty {
|
||||||
.os_input
|
.os_input
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.spawn_terminal(terminal_action, quit_cb);
|
.spawn_terminal(terminal_action, quit_cb, self.default_editor.clone())?;
|
||||||
let task_handle = stream_terminal_bytes(
|
let task_handle = stream_terminal_bytes(
|
||||||
pid_primary,
|
pid_primary,
|
||||||
self.bus.senders.clone(),
|
self.bus.senders.clone(),
|
||||||
|
|
@ -338,7 +369,7 @@ impl Pty {
|
||||||
);
|
);
|
||||||
self.task_handles.insert(pid_primary, task_handle);
|
self.task_handles.insert(pid_primary, task_handle);
|
||||||
self.id_to_child_pid.insert(pid_primary, child_fd);
|
self.id_to_child_pid.insert(pid_primary, child_fd);
|
||||||
pid_primary
|
Ok(pid_primary)
|
||||||
}
|
}
|
||||||
pub fn spawn_terminals_for_layout(
|
pub fn spawn_terminals_for_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -365,7 +396,8 @@ impl Pty {
|
||||||
.os_input
|
.os_input
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.spawn_terminal(cmd, quit_cb);
|
.spawn_terminal(cmd, quit_cb, self.default_editor.clone())
|
||||||
|
.unwrap(); // TODO: handle error here
|
||||||
self.id_to_child_pid.insert(pid_primary, child_fd);
|
self.id_to_child_pid.insert(pid_primary, child_fd);
|
||||||
new_pane_pids.push(pid_primary);
|
new_pane_pids.push(pid_primary);
|
||||||
}
|
}
|
||||||
|
|
@ -375,7 +407,8 @@ impl Pty {
|
||||||
.os_input
|
.os_input
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.spawn_terminal(default_shell.clone(), quit_cb);
|
.spawn_terminal(default_shell.clone(), quit_cb, self.default_editor.clone())
|
||||||
|
.unwrap(); // TODO: handle error here
|
||||||
self.id_to_child_pid.insert(pid_primary, child_fd);
|
self.id_to_child_pid.insert(pid_primary, child_fd);
|
||||||
new_pane_pids.push(pid_primary);
|
new_pane_pids.push(pid_primary);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,12 @@ fn route_action(
|
||||||
.send_to_screen(ScreenInstruction::DumpScreen(val, client_id))
|
.send_to_screen(ScreenInstruction::DumpScreen(val, client_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
Action::EditScrollback => {
|
||||||
|
session
|
||||||
|
.senders
|
||||||
|
.send_to_screen(ScreenInstruction::EditScrollback(client_id))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
Action::ScrollUp => {
|
Action::ScrollUp => {
|
||||||
session
|
session
|
||||||
.senders
|
.senders
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ pub enum ScreenInstruction {
|
||||||
PtyBytes(RawFd, VteBytes),
|
PtyBytes(RawFd, VteBytes),
|
||||||
Render,
|
Render,
|
||||||
NewPane(PaneId, ClientOrTabIndex),
|
NewPane(PaneId, ClientOrTabIndex),
|
||||||
|
OpenInPlaceEditor(PaneId, ClientId),
|
||||||
TogglePaneEmbedOrFloating(ClientId),
|
TogglePaneEmbedOrFloating(ClientId),
|
||||||
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
|
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
|
||||||
HorizontalSplit(PaneId, ClientId),
|
HorizontalSplit(PaneId, ClientId),
|
||||||
|
|
@ -67,6 +68,7 @@ pub enum ScreenInstruction {
|
||||||
MovePaneLeft(ClientId),
|
MovePaneLeft(ClientId),
|
||||||
Exit,
|
Exit,
|
||||||
DumpScreen(String, ClientId),
|
DumpScreen(String, ClientId),
|
||||||
|
EditScrollback(ClientId),
|
||||||
ScrollUp(ClientId),
|
ScrollUp(ClientId),
|
||||||
ScrollUpAt(Position, ClientId),
|
ScrollUpAt(Position, ClientId),
|
||||||
ScrollDown(ClientId),
|
ScrollDown(ClientId),
|
||||||
|
|
@ -115,6 +117,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||||
ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes,
|
ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes,
|
||||||
ScreenInstruction::Render => ScreenContext::Render,
|
ScreenInstruction::Render => ScreenContext::Render,
|
||||||
ScreenInstruction::NewPane(..) => ScreenContext::NewPane,
|
ScreenInstruction::NewPane(..) => ScreenContext::NewPane,
|
||||||
|
ScreenInstruction::OpenInPlaceEditor(..) => ScreenContext::OpenInPlaceEditor,
|
||||||
ScreenInstruction::TogglePaneEmbedOrFloating(..) => {
|
ScreenInstruction::TogglePaneEmbedOrFloating(..) => {
|
||||||
ScreenContext::TogglePaneEmbedOrFloating
|
ScreenContext::TogglePaneEmbedOrFloating
|
||||||
}
|
}
|
||||||
|
|
@ -148,6 +151,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||||
ScreenInstruction::MovePaneLeft(..) => ScreenContext::MovePaneLeft,
|
ScreenInstruction::MovePaneLeft(..) => ScreenContext::MovePaneLeft,
|
||||||
ScreenInstruction::Exit => ScreenContext::Exit,
|
ScreenInstruction::Exit => ScreenContext::Exit,
|
||||||
ScreenInstruction::DumpScreen(..) => ScreenContext::DumpScreen,
|
ScreenInstruction::DumpScreen(..) => ScreenContext::DumpScreen,
|
||||||
|
ScreenInstruction::EditScrollback(..) => ScreenContext::EditScrollback,
|
||||||
ScreenInstruction::ScrollUp(..) => ScreenContext::ScrollUp,
|
ScreenInstruction::ScrollUp(..) => ScreenContext::ScrollUp,
|
||||||
ScreenInstruction::ScrollDown(..) => ScreenContext::ScrollDown,
|
ScreenInstruction::ScrollDown(..) => ScreenContext::ScrollDown,
|
||||||
ScreenInstruction::ScrollToBottom(..) => ScreenContext::ScrollToBottom,
|
ScreenInstruction::ScrollToBottom(..) => ScreenContext::ScrollToBottom,
|
||||||
|
|
@ -798,6 +802,7 @@ pub(crate) fn screen_thread_main(
|
||||||
client_attributes: ClientAttributes,
|
client_attributes: ClientAttributes,
|
||||||
config_options: Box<Options>,
|
config_options: Box<Options>,
|
||||||
) {
|
) {
|
||||||
|
// let mut scrollbacks: HashMap<String, PaneId> = HashMap::new();
|
||||||
let capabilities = config_options.simplified_ui;
|
let capabilities = config_options.simplified_ui;
|
||||||
let draw_pane_frames = config_options.pane_frames.unwrap_or(true);
|
let draw_pane_frames = config_options.pane_frames.unwrap_or(true);
|
||||||
let session_is_mirrored = config_options.mirror_session.unwrap_or(false);
|
let session_is_mirrored = config_options.mirror_session.unwrap_or(false);
|
||||||
|
|
@ -867,6 +872,22 @@ pub(crate) fn screen_thread_main(
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
ScreenInstruction::OpenInPlaceEditor(pid, client_id) => {
|
||||||
|
if let Some(active_tab) = screen.get_active_tab_mut(client_id) {
|
||||||
|
active_tab.suppress_active_pane(pid, client_id);
|
||||||
|
} else {
|
||||||
|
log::error!("Active tab not found for client id: {:?}", client_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
screen
|
||||||
|
.bus
|
||||||
|
.senders
|
||||||
|
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||||
|
.unwrap();
|
||||||
|
screen.update_tabs();
|
||||||
|
|
||||||
|
screen.render();
|
||||||
|
}
|
||||||
ScreenInstruction::TogglePaneEmbedOrFloating(client_id) => {
|
ScreenInstruction::TogglePaneEmbedOrFloating(client_id) => {
|
||||||
if let Some(active_tab) = screen.get_active_tab_mut(client_id) {
|
if let Some(active_tab) = screen.get_active_tab_mut(client_id) {
|
||||||
active_tab.toggle_pane_embed_or_floating(client_id);
|
active_tab.toggle_pane_embed_or_floating(client_id);
|
||||||
|
|
@ -1079,6 +1100,15 @@ pub(crate) fn screen_thread_main(
|
||||||
|
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
|
ScreenInstruction::EditScrollback(client_id) => {
|
||||||
|
if let Some(active_tab) = screen.get_active_tab_mut(client_id) {
|
||||||
|
active_tab.edit_scrollback(client_id);
|
||||||
|
} else {
|
||||||
|
log::error!("Active tab not found for client id: {:?}", client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
screen.render();
|
||||||
|
}
|
||||||
ScreenInstruction::ScrollUp(client_id) => {
|
ScreenInstruction::ScrollUp(client_id) => {
|
||||||
if let Some(active_tab) = screen.get_active_tab_mut(client_id) {
|
if let Some(active_tab) = screen.get_active_tab_mut(client_id) {
|
||||||
active_tab.scroll_active_terminal_up(client_id);
|
active_tab.scroll_active_terminal_up(client_id);
|
||||||
|
|
@ -1241,7 +1271,7 @@ pub(crate) fn screen_thread_main(
|
||||||
Some(client_id) => {
|
Some(client_id) => {
|
||||||
screen
|
screen
|
||||||
.get_active_tab_mut(client_id)
|
.get_active_tab_mut(client_id)
|
||||||
.and_then(|active_tab| active_tab.close_pane(id))
|
.and_then(|active_tab| active_tab.close_pane(id, false))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
log::error!("Active tab not found for client id: {:?}", client_id);
|
log::error!("Active tab not found for client id: {:?}", client_id);
|
||||||
None
|
None
|
||||||
|
|
@ -1250,7 +1280,7 @@ pub(crate) fn screen_thread_main(
|
||||||
None => {
|
None => {
|
||||||
for tab in screen.tabs.values_mut() {
|
for tab in screen.tabs.values_mut() {
|
||||||
if tab.get_all_pane_ids().contains(&id) {
|
if tab.get_all_pane_ids().contains(&id) {
|
||||||
tab.close_pane(id);
|
tab.close_pane(id, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ mod clipboard;
|
||||||
mod copy_command;
|
mod copy_command;
|
||||||
|
|
||||||
use copy_command::CopyCommand;
|
use copy_command::CopyCommand;
|
||||||
|
use std::env::temp_dir;
|
||||||
|
use uuid::Uuid;
|
||||||
use zellij_tile::prelude::Style;
|
use zellij_tile::prelude::Style;
|
||||||
use zellij_utils::position::{Column, Line};
|
use zellij_utils::position::{Column, Line};
|
||||||
use zellij_utils::{position::Position, serde, zellij_tile};
|
use zellij_utils::{position::Position, serde, zellij_tile};
|
||||||
|
|
@ -70,6 +72,7 @@ pub(crate) struct Tab {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
tiled_panes: TiledPanes,
|
tiled_panes: TiledPanes,
|
||||||
floating_panes: FloatingPanes,
|
floating_panes: FloatingPanes,
|
||||||
|
suppressed_panes: HashMap<PaneId, Box<dyn Pane>>,
|
||||||
max_panes: Option<usize>,
|
max_panes: Option<usize>,
|
||||||
viewport: Rc<RefCell<Viewport>>, // includes all non-UI panes
|
viewport: Rc<RefCell<Viewport>>, // includes all non-UI panes
|
||||||
display_area: Rc<RefCell<Size>>, // includes all panes (including eg. the status bar and tab bar in the default layout)
|
display_area: Rc<RefCell<Size>>, // includes all panes (including eg. the status bar and tab bar in the default layout)
|
||||||
|
|
@ -268,6 +271,9 @@ pub trait Pane {
|
||||||
fn borderless(&self) -> bool;
|
fn borderless(&self) -> bool;
|
||||||
fn handle_right_click(&mut self, _to: &Position, _client_id: ClientId) {}
|
fn handle_right_click(&mut self, _to: &Position, _client_id: ClientId) {}
|
||||||
fn mouse_mode(&self) -> bool;
|
fn mouse_mode(&self) -> bool;
|
||||||
|
fn get_line_number(&self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
|
|
@ -339,6 +345,7 @@ impl Tab {
|
||||||
position,
|
position,
|
||||||
tiled_panes,
|
tiled_panes,
|
||||||
floating_panes,
|
floating_panes,
|
||||||
|
suppressed_panes: HashMap::new(),
|
||||||
name,
|
name,
|
||||||
max_panes,
|
max_panes,
|
||||||
viewport,
|
viewport,
|
||||||
|
|
@ -577,7 +584,8 @@ impl Tab {
|
||||||
if let Some(focused_floating_pane_id) = self.floating_panes.active_pane_id(client_id) {
|
if let Some(focused_floating_pane_id) = self.floating_panes.active_pane_id(client_id) {
|
||||||
if self.tiled_panes.has_room_for_new_pane() {
|
if self.tiled_panes.has_room_for_new_pane() {
|
||||||
// this unwrap is safe because floating panes should not be visible if there are no floating panes
|
// this unwrap is safe because floating panes should not be visible if there are no floating panes
|
||||||
let floating_pane_to_embed = self.close_pane(focused_floating_pane_id).unwrap();
|
let floating_pane_to_embed =
|
||||||
|
self.close_pane(focused_floating_pane_id, true).unwrap();
|
||||||
self.tiled_panes
|
self.tiled_panes
|
||||||
.insert_pane(focused_floating_pane_id, floating_pane_to_embed);
|
.insert_pane(focused_floating_pane_id, floating_pane_to_embed);
|
||||||
self.should_clear_display_before_rendering = true;
|
self.should_clear_display_before_rendering = true;
|
||||||
|
|
@ -592,7 +600,7 @@ impl Tab {
|
||||||
// don't close the only pane on screen...
|
// don't close the only pane on screen...
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(mut embedded_pane_to_float) = self.close_pane(focused_pane_id) {
|
if let Some(mut embedded_pane_to_float) = self.close_pane(focused_pane_id, true) {
|
||||||
embedded_pane_to_float.set_geom(new_pane_geom);
|
embedded_pane_to_float.set_geom(new_pane_geom);
|
||||||
resize_pty!(embedded_pane_to_float, self.os_api);
|
resize_pty!(embedded_pane_to_float, self.os_api);
|
||||||
embedded_pane_to_float.set_active_at(Instant::now());
|
embedded_pane_to_float.set_active_at(Instant::now());
|
||||||
|
|
@ -691,6 +699,47 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn suppress_active_pane(&mut self, pid: PaneId, client_id: ClientId) {
|
||||||
|
// this method creates a new pane from pid and replaces it with the active pane
|
||||||
|
// the active pane is then suppressed (hidden and not rendered) until the current
|
||||||
|
// created pane is closed, in which case it will be replaced back by it
|
||||||
|
match pid {
|
||||||
|
PaneId::Terminal(pid) => {
|
||||||
|
let next_terminal_position = self.get_next_terminal_position(); // TODO: this is not accurate in this case
|
||||||
|
let new_pane = TerminalPane::new(
|
||||||
|
pid,
|
||||||
|
PaneGeom::default(), // the initial size will be set later
|
||||||
|
self.style,
|
||||||
|
next_terminal_position,
|
||||||
|
String::new(),
|
||||||
|
self.link_handler.clone(),
|
||||||
|
self.character_cell_size.clone(),
|
||||||
|
self.terminal_emulator_colors.clone(),
|
||||||
|
);
|
||||||
|
let replaced_pane = if self.floating_panes.panes_are_visible() {
|
||||||
|
self.floating_panes
|
||||||
|
.replace_active_pane(Box::new(new_pane), client_id)
|
||||||
|
} else {
|
||||||
|
self.tiled_panes
|
||||||
|
.replace_active_pane(Box::new(new_pane), client_id)
|
||||||
|
};
|
||||||
|
match replaced_pane {
|
||||||
|
Some(replaced_pane) => {
|
||||||
|
self.suppressed_panes
|
||||||
|
.insert(PaneId::Terminal(pid), replaced_pane);
|
||||||
|
let current_active_pane = self.get_active_pane(client_id).unwrap(); // this will be the newly replaced pane we just created
|
||||||
|
resize_pty!(current_active_pane, self.os_api);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
log::error!("Could not find editor pane to replace - is no pane focused?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PaneId::Plugin(_pid) => {
|
||||||
|
// TBD, currently unsupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn horizontal_split(&mut self, pid: PaneId, client_id: ClientId) {
|
pub fn horizontal_split(&mut self, pid: PaneId, client_id: ClientId) {
|
||||||
if self.floating_panes.panes_are_visible() {
|
if self.floating_panes.panes_are_visible() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -792,12 +841,22 @@ impl Tab {
|
||||||
pub fn has_terminal_pid(&self, pid: RawFd) -> bool {
|
pub fn has_terminal_pid(&self, pid: RawFd) -> bool {
|
||||||
self.tiled_panes.panes_contain(&PaneId::Terminal(pid))
|
self.tiled_panes.panes_contain(&PaneId::Terminal(pid))
|
||||||
|| self.floating_panes.panes_contain(&PaneId::Terminal(pid))
|
|| self.floating_panes.panes_contain(&PaneId::Terminal(pid))
|
||||||
|
|| self
|
||||||
|
.suppressed_panes
|
||||||
|
.values()
|
||||||
|
.find(|s_p| s_p.pid() == PaneId::Terminal(pid))
|
||||||
|
.is_some()
|
||||||
}
|
}
|
||||||
pub fn handle_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) {
|
pub fn handle_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) {
|
||||||
if let Some(terminal_output) = self
|
if let Some(terminal_output) = self
|
||||||
.tiled_panes
|
.tiled_panes
|
||||||
.get_pane_mut(PaneId::Terminal(pid))
|
.get_pane_mut(PaneId::Terminal(pid))
|
||||||
.or_else(|| self.floating_panes.get_pane_mut(PaneId::Terminal(pid)))
|
.or_else(|| self.floating_panes.get_pane_mut(PaneId::Terminal(pid)))
|
||||||
|
.or_else(|| {
|
||||||
|
self.suppressed_panes
|
||||||
|
.values_mut()
|
||||||
|
.find(|s_p| s_p.pid() == PaneId::Terminal(pid))
|
||||||
|
})
|
||||||
{
|
{
|
||||||
// If the pane is scrolled buffer the vte events
|
// If the pane is scrolled buffer the vte events
|
||||||
if terminal_output.is_scrolled() {
|
if terminal_output.is_scrolled() {
|
||||||
|
|
@ -827,6 +886,11 @@ impl Tab {
|
||||||
.tiled_panes
|
.tiled_panes
|
||||||
.get_pane_mut(PaneId::Terminal(pid))
|
.get_pane_mut(PaneId::Terminal(pid))
|
||||||
.or_else(|| self.floating_panes.get_pane_mut(PaneId::Terminal(pid)))
|
.or_else(|| self.floating_panes.get_pane_mut(PaneId::Terminal(pid)))
|
||||||
|
.or_else(|| {
|
||||||
|
self.suppressed_panes
|
||||||
|
.values_mut()
|
||||||
|
.find(|s_p| s_p.pid() == PaneId::Terminal(pid))
|
||||||
|
})
|
||||||
{
|
{
|
||||||
terminal_output.handle_pty_bytes(bytes);
|
terminal_output.handle_pty_bytes(bytes);
|
||||||
let messages_to_pty = terminal_output.drain_messages_to_pty();
|
let messages_to_pty = terminal_output.drain_messages_to_pty();
|
||||||
|
|
@ -1070,6 +1134,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
pub fn resize_whole_tab(&mut self, new_screen_size: Size) {
|
pub fn resize_whole_tab(&mut self, new_screen_size: Size) {
|
||||||
self.floating_panes.resize(new_screen_size);
|
self.floating_panes.resize(new_screen_size);
|
||||||
|
self.floating_panes.resize_pty_all_panes(&mut self.os_api); // we need to do this explicitly because floating_panes.resize does not do this
|
||||||
self.tiled_panes.resize(new_screen_size);
|
self.tiled_panes.resize(new_screen_size);
|
||||||
self.should_clear_display_before_rendering = true;
|
self.should_clear_display_before_rendering = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1308,7 +1373,7 @@ impl Tab {
|
||||||
self.senders
|
self.senders
|
||||||
.send_to_pty(PtyInstruction::ClosePane(pid))
|
.send_to_pty(PtyInstruction::ClosePane(pid))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.close_pane(pid);
|
self.close_pane(pid, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1345,7 +1410,20 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn close_pane(&mut self, id: PaneId) -> Option<Box<dyn Pane>> {
|
pub fn close_pane(
|
||||||
|
&mut self,
|
||||||
|
id: PaneId,
|
||||||
|
ignore_suppressed_panes: bool,
|
||||||
|
) -> Option<Box<dyn Pane>> {
|
||||||
|
// we need to ignore suppressed panes when we toggle a pane to be floating/embedded(tiled)
|
||||||
|
// this is because in that case, while we do use this logic, we're not actually closing the
|
||||||
|
// pane, we're moving it
|
||||||
|
//
|
||||||
|
// TODO: separate the "close_pane" logic and the "move_pane_somewhere_else" logic, they're
|
||||||
|
// overloaded here and that's not great
|
||||||
|
if !ignore_suppressed_panes && self.suppressed_panes.contains_key(&id) {
|
||||||
|
return self.replace_pane_with_suppressed_pane(id);
|
||||||
|
}
|
||||||
if self.floating_panes.panes_contain(&id) {
|
if self.floating_panes.panes_contain(&id) {
|
||||||
let closed_pane = self.floating_panes.remove_pane(id);
|
let closed_pane = self.floating_panes.remove_pane(id);
|
||||||
self.floating_panes.move_clients_out_of_pane(id);
|
self.floating_panes.move_clients_out_of_pane(id);
|
||||||
|
|
@ -1365,10 +1443,38 @@ impl Tab {
|
||||||
closed_pane
|
closed_pane
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn replace_pane_with_suppressed_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
||||||
|
self.suppressed_panes
|
||||||
|
.remove(&pane_id)
|
||||||
|
.and_then(|suppressed_pane| {
|
||||||
|
let suppressed_pane_id = suppressed_pane.pid();
|
||||||
|
let replaced_pane = if self.are_floating_panes_visible() {
|
||||||
|
self.floating_panes.replace_pane(pane_id, suppressed_pane)
|
||||||
|
} else {
|
||||||
|
self.tiled_panes.replace_pane(pane_id, suppressed_pane)
|
||||||
|
};
|
||||||
|
if let Some(suppressed_pane) = self
|
||||||
|
.floating_panes
|
||||||
|
.get_pane(suppressed_pane_id)
|
||||||
|
.or_else(|| self.tiled_panes.get_pane(suppressed_pane_id))
|
||||||
|
{
|
||||||
|
// You may be thinking: why aren't we using the original "suppressed_pane" here,
|
||||||
|
// isn't it the same one?
|
||||||
|
//
|
||||||
|
// Yes, you are right! However, we moved it into its correct environment above
|
||||||
|
// (either floating_panes or tiled_panes) where it received a new geometry based on
|
||||||
|
// the pane there we replaced. Now, we need to update its pty about its new size.
|
||||||
|
// We couldn't do that before, and we can't use the original moved item now - so we
|
||||||
|
// need to refetch it
|
||||||
|
resize_pty!(suppressed_pane, self.os_api);
|
||||||
|
}
|
||||||
|
replaced_pane
|
||||||
|
})
|
||||||
|
}
|
||||||
pub fn close_focused_pane(&mut self, client_id: ClientId) {
|
pub fn close_focused_pane(&mut self, client_id: ClientId) {
|
||||||
if self.floating_panes.panes_are_visible() {
|
if self.floating_panes.panes_are_visible() {
|
||||||
if let Some(active_floating_pane_id) = self.floating_panes.active_pane_id(client_id) {
|
if let Some(active_floating_pane_id) = self.floating_panes.active_pane_id(client_id) {
|
||||||
self.close_pane(active_floating_pane_id);
|
self.close_pane(active_floating_pane_id, false);
|
||||||
self.senders
|
self.senders
|
||||||
.send_to_pty(PtyInstruction::ClosePane(active_floating_pane_id))
|
.send_to_pty(PtyInstruction::ClosePane(active_floating_pane_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -1376,7 +1482,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(active_pane_id) = self.tiled_panes.get_active_pane_id(client_id) {
|
if let Some(active_pane_id) = self.tiled_panes.get_active_pane_id(client_id) {
|
||||||
self.close_pane(active_pane_id);
|
self.close_pane(active_pane_id, false);
|
||||||
self.senders
|
self.senders
|
||||||
.send_to_pty(PtyInstruction::ClosePane(active_pane_id))
|
.send_to_pty(PtyInstruction::ClosePane(active_pane_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -1388,6 +1494,22 @@ impl Tab {
|
||||||
self.os_api.write_to_file(dump, file);
|
self.os_api.write_to_file(dump, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn edit_scrollback(&mut self, client_id: ClientId) {
|
||||||
|
let mut file = temp_dir();
|
||||||
|
file.push(format!("{}.dump", Uuid::new_v4()));
|
||||||
|
self.dump_active_terminal_screen(Some(String::from(file.to_string_lossy())), client_id);
|
||||||
|
// let line_number = self.get_active_pane(client_id).map(|a_t| a_t.get_line_number());
|
||||||
|
let line_number = self
|
||||||
|
.get_active_pane(client_id)
|
||||||
|
.and_then(|a_t| a_t.get_line_number());
|
||||||
|
self.senders
|
||||||
|
.send_to_pty(PtyInstruction::OpenInPlaceEditor(
|
||||||
|
file,
|
||||||
|
line_number,
|
||||||
|
client_id,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
pub fn scroll_active_terminal_up(&mut self, client_id: ClientId) {
|
pub fn scroll_active_terminal_up(&mut self, client_id: ClientId) {
|
||||||
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
|
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
|
||||||
active_pane.scroll_up(1, client_id);
|
active_pane.scroll_up(1, client_id);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1352
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ │
|
||||||
|
02 (C): │ │
|
||||||
|
03 (C): │ │
|
||||||
|
04 (C): │ │
|
||||||
|
05 (C): │ ┌ Pane #2 ─────────────────────────────────────────────────┐ │
|
||||||
|
06 (C): │ │ │ │
|
||||||
|
07 (C): │ │ │ │
|
||||||
|
08 (C): │ │ │ │
|
||||||
|
09 (C): │ │I am the original pane │ │
|
||||||
|
10 (C): │ │ │ │
|
||||||
|
11 (C): │ │ │ │
|
||||||
|
12 (C): │ │ │ │
|
||||||
|
13 (C): │ │ │ │
|
||||||
|
14 (C): │ └──────────────────────────────────────────────────────────┘ │
|
||||||
|
15 (C): │ │
|
||||||
|
16 (C): │ │
|
||||||
|
17 (C): │ │
|
||||||
|
18 (C): │ │
|
||||||
|
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1318
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ │
|
||||||
|
02 (C): │ │
|
||||||
|
03 (C): │ │
|
||||||
|
04 (C): │I am the original pane │
|
||||||
|
05 (C): │ │
|
||||||
|
06 (C): │ │
|
||||||
|
07 (C): │ │
|
||||||
|
08 (C): │ │
|
||||||
|
09 (C): │ │
|
||||||
|
10 (C): │ │
|
||||||
|
11 (C): │ │
|
||||||
|
12 (C): │ │
|
||||||
|
13 (C): │ │
|
||||||
|
14 (C): │ │
|
||||||
|
15 (C): │ │
|
||||||
|
16 (C): │ │
|
||||||
|
17 (C): │ │
|
||||||
|
18 (C): │ │
|
||||||
|
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1474
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #1 ────────────────────┌ Pane #3 ─────────────────────────────────────────────────┐─────────┐
|
||||||
|
01 (C): │ │ │ │
|
||||||
|
02 (C): │ │ │ │
|
||||||
|
03 (C): │ │ │ │
|
||||||
|
04 (C): │ │I am an editor pane │ │
|
||||||
|
05 (C): │ │ │ │
|
||||||
|
06 (C): │ │ │ │
|
||||||
|
07 (C): │ │ │ │
|
||||||
|
08 (C): │ │ │ │
|
||||||
|
09 (C): └─────────────────────────────└──────────────────────────────────────────────────────────┘─────────┘
|
||||||
|
10 (C):
|
||||||
|
11 (C):
|
||||||
|
12 (C):
|
||||||
|
13 (C):
|
||||||
|
14 (C):
|
||||||
|
15 (C):
|
||||||
|
16 (C):
|
||||||
|
17 (C):
|
||||||
|
18 (C):
|
||||||
|
19 (C):
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1444
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ │
|
||||||
|
02 (C): │ │
|
||||||
|
03 (C): │ │
|
||||||
|
04 (C): │I am an editor pane │
|
||||||
|
05 (C): │ │
|
||||||
|
06 (C): │ │
|
||||||
|
07 (C): │ │
|
||||||
|
08 (C): │ │
|
||||||
|
09 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
10 (C):
|
||||||
|
11 (C):
|
||||||
|
12 (C):
|
||||||
|
13 (C):
|
||||||
|
14 (C):
|
||||||
|
15 (C):
|
||||||
|
16 (C):
|
||||||
|
17 (C):
|
||||||
|
18 (C):
|
||||||
|
19 (C):
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1288
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ │
|
||||||
|
02 (C): │ │
|
||||||
|
03 (C): │ │
|
||||||
|
04 (C): │ │
|
||||||
|
05 (C): │ ┌ Pane #3 ─────────────────────────────────────────────────┐ │
|
||||||
|
06 (C): │ │ │ │
|
||||||
|
07 (C): │ │ │ │
|
||||||
|
08 (C): │ │ │ │
|
||||||
|
09 (C): │ │I am an editor pane │ │
|
||||||
|
10 (C): │ │ │ │
|
||||||
|
11 (C): │ │ │ │
|
||||||
|
12 (C): │ │ │ │
|
||||||
|
13 (C): │ │ │ │
|
||||||
|
14 (C): │ └──────────────────────────────────────────────────────────┘ │
|
||||||
|
15 (C): │ │
|
||||||
|
16 (C): │ │
|
||||||
|
17 (C): │ │
|
||||||
|
18 (C): │ │
|
||||||
|
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1418
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ ││ │
|
||||||
|
02 (C): │ ││ │
|
||||||
|
03 (C): │ ││ │
|
||||||
|
04 (C): │ ││I am the original pane │
|
||||||
|
05 (C): │ ││ │
|
||||||
|
06 (C): │ ││ │
|
||||||
|
07 (C): │ ││ │
|
||||||
|
08 (C): │ ││ │
|
||||||
|
09 (C): │ ││ │
|
||||||
|
10 (C): │ ││ │
|
||||||
|
11 (C): │ ││ │
|
||||||
|
12 (C): │ ││ │
|
||||||
|
13 (C): │ ││ │
|
||||||
|
14 (C): │ ││ │
|
||||||
|
15 (C): │ ││ │
|
||||||
|
16 (C): │ ││ │
|
||||||
|
17 (C): │ ││ │
|
||||||
|
18 (C): │ ││ │
|
||||||
|
19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1259
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ │
|
||||||
|
02 (C): │ │
|
||||||
|
03 (C): │ │
|
||||||
|
04 (C): │I am an editor pane │
|
||||||
|
05 (C): │ │
|
||||||
|
06 (C): │ │
|
||||||
|
07 (C): │ │
|
||||||
|
08 (C): │ │
|
||||||
|
09 (C): │ │
|
||||||
|
10 (C): │ │
|
||||||
|
11 (C): │ │
|
||||||
|
12 (C): │ │
|
||||||
|
13 (C): │ │
|
||||||
|
14 (C): │ │
|
||||||
|
15 (C): │ │
|
||||||
|
16 (C): │ │
|
||||||
|
17 (C): │ │
|
||||||
|
18 (C): │ │
|
||||||
|
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 1383
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ │
|
||||||
|
02 (C): │ │
|
||||||
|
03 (C): │ │
|
||||||
|
04 (C): │I am the original pane │
|
||||||
|
05 (C): │ │
|
||||||
|
06 (C): │ │
|
||||||
|
07 (C): │ │
|
||||||
|
08 (C): │ │
|
||||||
|
09 (C): │ │
|
||||||
|
10 (C): │ │
|
||||||
|
11 (C): │ │
|
||||||
|
12 (C): │ │
|
||||||
|
13 (C): │ │
|
||||||
|
14 (C): │ │
|
||||||
|
15 (C): │ │
|
||||||
|
16 (C): │ │
|
||||||
|
17 (C): │ │
|
||||||
|
18 (C): │ │
|
||||||
|
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -45,7 +45,8 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
&self,
|
&self,
|
||||||
_file_to_open: TerminalAction,
|
_file_to_open: TerminalAction,
|
||||||
_quit_cb: Box<dyn Fn(PaneId) + Send>,
|
_quit_cb: Box<dyn Fn(PaneId) + Send>,
|
||||||
) -> (RawFd, RawFd) {
|
_default_editor: Option<PathBuf>,
|
||||||
|
) -> Result<(RawFd, RawFd), &'static str> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
|
|
@ -1233,3 +1234,213 @@ fn save_cursor_position_across_resizes() {
|
||||||
);
|
);
|
||||||
assert_snapshot!(snapshot);
|
assert_snapshot!(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn suppress_tiled_pane() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let mut output = Output::default();
|
||||||
|
tab.suppress_active_pane(new_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn suppress_floating_pane() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let editor_pane_id = PaneId::Terminal(3);
|
||||||
|
let mut output = Output::default();
|
||||||
|
|
||||||
|
tab.toggle_floating_panes(client_id, None);
|
||||||
|
tab.new_pane(new_pane_id, Some(client_id));
|
||||||
|
tab.suppress_active_pane(editor_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn close_suppressing_tiled_pane() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let mut output = Output::default();
|
||||||
|
tab.suppress_active_pane(new_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes()));
|
||||||
|
tab.close_pane(new_pane_id, false);
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn close_suppressing_floating_pane() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let editor_pane_id = PaneId::Terminal(3);
|
||||||
|
let mut output = Output::default();
|
||||||
|
|
||||||
|
tab.toggle_floating_panes(client_id, None);
|
||||||
|
tab.new_pane(new_pane_id, Some(client_id));
|
||||||
|
tab.suppress_active_pane(editor_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes()));
|
||||||
|
tab.close_pane(editor_pane_id, false);
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn suppress_tiled_pane_float_it_and_close() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let mut output = Output::default();
|
||||||
|
tab.suppress_active_pane(new_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes()));
|
||||||
|
tab.toggle_pane_embed_or_floating(client_id);
|
||||||
|
tab.close_pane(new_pane_id, false);
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn suppress_floating_pane_embed_it_and_close_it() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let editor_pane_id = PaneId::Terminal(3);
|
||||||
|
let mut output = Output::default();
|
||||||
|
|
||||||
|
tab.toggle_floating_panes(client_id, None);
|
||||||
|
tab.new_pane(new_pane_id, Some(client_id));
|
||||||
|
tab.suppress_active_pane(editor_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes()));
|
||||||
|
tab.toggle_pane_embed_or_floating(client_id);
|
||||||
|
tab.close_pane(editor_pane_id, false);
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resize_whole_tab_while_tiled_pane_is_suppressed() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let mut output = Output::default();
|
||||||
|
tab.suppress_active_pane(new_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.resize_whole_tab(Size {
|
||||||
|
cols: 100,
|
||||||
|
rows: 10,
|
||||||
|
});
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resize_whole_tab_while_floting_pane_is_suppressed() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size);
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
let editor_pane_id = PaneId::Terminal(3);
|
||||||
|
let mut output = Output::default();
|
||||||
|
|
||||||
|
tab.toggle_floating_panes(client_id, None);
|
||||||
|
tab.new_pane(new_pane_id, Some(client_id));
|
||||||
|
tab.suppress_active_pane(editor_pane_id, client_id);
|
||||||
|
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()));
|
||||||
|
tab.resize_whole_tab(Size {
|
||||||
|
cols: 100,
|
||||||
|
rows: 10,
|
||||||
|
});
|
||||||
|
tab.render(&mut output, None);
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
&self,
|
&self,
|
||||||
_file_to_open: TerminalAction,
|
_file_to_open: TerminalAction,
|
||||||
_quit_cb: Box<dyn Fn(PaneId) + Send>,
|
_quit_cb: Box<dyn Fn(PaneId) + Send>,
|
||||||
) -> (RawFd, RawFd) {
|
_default_editor: Option<PathBuf>,
|
||||||
|
) -> Result<(RawFd, RawFd), &'static str> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
&self,
|
&self,
|
||||||
_file_to_open: TerminalAction,
|
_file_to_open: TerminalAction,
|
||||||
_quit_db: Box<dyn Fn(PaneId) + Send>,
|
_quit_db: Box<dyn Fn(PaneId) + Send>,
|
||||||
) -> (RawFd, RawFd) {
|
_default_editor: Option<PathBuf>,
|
||||||
|
) -> Result<(RawFd, RawFd), &'static str> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
|
|
|
||||||
|
|
@ -393,7 +393,7 @@ fn host_open_file(plugin_env: &PluginEnv) {
|
||||||
plugin_env
|
plugin_env
|
||||||
.senders
|
.senders
|
||||||
.send_to_pty(PtyInstruction::SpawnTerminal(
|
.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||||
Some(TerminalAction::OpenFile(path)),
|
Some(TerminalAction::OpenFile(path, None)),
|
||||||
ClientOrTabIndex::TabIndex(plugin_env.tab_index),
|
ClientOrTabIndex::TabIndex(plugin_env.tab_index),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,8 @@ keybinds:
|
||||||
- action: [Resize: Decrease,]
|
- action: [Resize: Decrease,]
|
||||||
key: [ Alt: '-']
|
key: [ Alt: '-']
|
||||||
scroll:
|
scroll:
|
||||||
|
- action: [EditScrollback, SwitchToMode: Normal]
|
||||||
|
key: [Char: 'e']
|
||||||
- action: [SwitchToMode: Normal,]
|
- action: [SwitchToMode: Normal,]
|
||||||
key: [Ctrl: 's', Char: ' ', Char: "\n", Esc]
|
key: [Ctrl: 's', Char: ' ', Char: "\n", Esc]
|
||||||
- action: [SwitchToMode: Tab,]
|
- action: [SwitchToMode: Tab,]
|
||||||
|
|
@ -548,3 +550,6 @@ plugins:
|
||||||
|
|
||||||
# Enable or disable automatic copy (and clear) of selection when releasing mouse
|
# Enable or disable automatic copy (and clear) of selection when releasing mouse
|
||||||
#copy_on_select: true
|
#copy_on_select: true
|
||||||
|
|
||||||
|
# Path to the default editor to use to edit pane scrollbuffer
|
||||||
|
# scrollback_editor: /usr/bin/nano
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,7 @@ pub enum ScreenContext {
|
||||||
HandlePtyBytes,
|
HandlePtyBytes,
|
||||||
Render,
|
Render,
|
||||||
NewPane,
|
NewPane,
|
||||||
|
OpenInPlaceEditor,
|
||||||
ToggleFloatingPanes,
|
ToggleFloatingPanes,
|
||||||
TogglePaneEmbedOrFloating,
|
TogglePaneEmbedOrFloating,
|
||||||
HorizontalSplit,
|
HorizontalSplit,
|
||||||
|
|
@ -243,6 +244,7 @@ pub enum ScreenContext {
|
||||||
MovePaneLeft,
|
MovePaneLeft,
|
||||||
Exit,
|
Exit,
|
||||||
DumpScreen,
|
DumpScreen,
|
||||||
|
EditScrollback,
|
||||||
ScrollUp,
|
ScrollUp,
|
||||||
ScrollUpAt,
|
ScrollUpAt,
|
||||||
ScrollDown,
|
ScrollDown,
|
||||||
|
|
@ -292,6 +294,7 @@ pub enum ScreenContext {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum PtyContext {
|
pub enum PtyContext {
|
||||||
SpawnTerminal,
|
SpawnTerminal,
|
||||||
|
OpenInPlaceEditor,
|
||||||
SpawnTerminalVertically,
|
SpawnTerminalVertically,
|
||||||
SpawnTerminalHorizontally,
|
SpawnTerminalHorizontally,
|
||||||
UpdateActivePane,
|
UpdateActivePane,
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ pub enum Action {
|
||||||
/// Dumps the screen to a file
|
/// Dumps the screen to a file
|
||||||
DumpScreen(String),
|
DumpScreen(String),
|
||||||
/// Scroll up in focus pane.
|
/// Scroll up in focus pane.
|
||||||
|
EditScrollback,
|
||||||
ScrollUp,
|
ScrollUp,
|
||||||
/// Scroll up at point
|
/// Scroll up at point
|
||||||
ScrollUpAt(Position),
|
ScrollUpAt(Position),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TerminalAction {
|
pub enum TerminalAction {
|
||||||
OpenFile(PathBuf),
|
OpenFile(PathBuf, Option<usize>), // path to file and optional line_number
|
||||||
RunCommand(RunCommand),
|
RunCommand(RunCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ pub fn get_mode_info(mode: InputMode, style: Style, capabilities: PluginCapabili
|
||||||
("↓↑".to_string(), "Scroll".to_string()),
|
("↓↑".to_string(), "Scroll".to_string()),
|
||||||
("PgUp/PgDn".to_string(), "Scroll Page".to_string()),
|
("PgUp/PgDn".to_string(), "Scroll Page".to_string()),
|
||||||
("u/d".to_string(), "Scroll Half Page".to_string()),
|
("u/d".to_string(), "Scroll Half Page".to_string()),
|
||||||
|
(
|
||||||
|
"e".to_string(),
|
||||||
|
"Edit Scrollback in Default Editor".to_string(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
InputMode::RenameTab => vec![("Enter".to_string(), "when done".to_string())],
|
InputMode::RenameTab => vec![("Enter".to_string(), "when done".to_string())],
|
||||||
InputMode::RenamePane => vec![("Enter".to_string(), "when done".to_string())],
|
InputMode::RenamePane => vec![("Enter".to_string(), "when done".to_string())],
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,10 @@ pub struct Options {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub copy_on_select: Option<bool>,
|
pub copy_on_select: Option<bool>,
|
||||||
|
|
||||||
|
/// Explicit full path to open the scrollback editor (default is $EDITOR or $VISUAL)
|
||||||
|
#[clap(long, parse(from_os_str))]
|
||||||
|
pub scrollback_editor: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
|
#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
@ -131,6 +135,9 @@ impl Options {
|
||||||
let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
|
let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
|
||||||
let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
|
let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
|
||||||
let copy_on_select = other.copy_on_select.or(self.copy_on_select);
|
let copy_on_select = other.copy_on_select.or(self.copy_on_select);
|
||||||
|
let scrollback_editor = other
|
||||||
|
.scrollback_editor
|
||||||
|
.or_else(|| self.scrollback_editor.clone());
|
||||||
|
|
||||||
Options {
|
Options {
|
||||||
simplified_ui,
|
simplified_ui,
|
||||||
|
|
@ -146,6 +153,7 @@ impl Options {
|
||||||
copy_command,
|
copy_command,
|
||||||
copy_clipboard,
|
copy_clipboard,
|
||||||
copy_on_select,
|
copy_on_select,
|
||||||
|
scrollback_editor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,6 +186,9 @@ impl Options {
|
||||||
let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
|
let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
|
||||||
let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
|
let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard);
|
||||||
let copy_on_select = other.copy_on_select.or(self.copy_on_select);
|
let copy_on_select = other.copy_on_select.or(self.copy_on_select);
|
||||||
|
let scrollback_editor = other
|
||||||
|
.scrollback_editor
|
||||||
|
.or_else(|| self.scrollback_editor.clone());
|
||||||
|
|
||||||
Options {
|
Options {
|
||||||
simplified_ui,
|
simplified_ui,
|
||||||
|
|
@ -193,6 +204,7 @@ impl Options {
|
||||||
copy_command,
|
copy_command,
|
||||||
copy_clipboard,
|
copy_clipboard,
|
||||||
copy_on_select,
|
copy_on_select,
|
||||||
|
scrollback_editor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,6 +256,7 @@ impl From<CliOptions> for Options {
|
||||||
copy_command: opts.copy_command,
|
copy_command: opts.copy_command,
|
||||||
copy_clipboard: opts.copy_clipboard,
|
copy_clipboard: opts.copy_clipboard,
|
||||||
copy_on_select: opts.copy_on_select,
|
copy_on_select: opts.copy_on_select,
|
||||||
|
scrollback_editor: opts.scrollback_editor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -403,6 +403,10 @@ impl Setup {
|
||||||
message.push_str(" Can be temporarily disabled through pressing the [SHIFT] key.\n");
|
message.push_str(" Can be temporarily disabled through pressing the [SHIFT] key.\n");
|
||||||
message.push_str(" If that doesn't fix any issues consider to disable the mouse handling of zellij: 'zellij options --disable-mouse-mode'\n");
|
message.push_str(" If that doesn't fix any issues consider to disable the mouse handling of zellij: 'zellij options --disable-mouse-mode'\n");
|
||||||
|
|
||||||
|
let default_editor = std::env::var("EDITOR")
|
||||||
|
.or_else(|_| std::env::var("VISUAL"))
|
||||||
|
.unwrap_or_else(|_| String::from("Not set, checked $EDITOR and $VISUAL"));
|
||||||
|
writeln!(&mut message, "[DEFAULT EDITOR]: {}", default_editor).unwrap();
|
||||||
writeln!(&mut message, "[FEATURES]: {:?}", FEATURES).unwrap();
|
writeln!(&mut message, "[FEATURES]: {:?}", FEATURES).unwrap();
|
||||||
let mut hyperlink = String::new();
|
let mut hyperlink = String::new();
|
||||||
hyperlink.push_str(hyperlink_start);
|
hyperlink.push_str(hyperlink_start);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue