aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristophe Verbinnen <christophe@verbinnen.org>2022-01-15 06:38:45 -0500
committerGitHub <noreply@github.com>2022-01-15 12:38:45 +0100
commit9cc2645db024df0a95efda42aed34bd3def19b28 (patch)
treed2345ca39dba0d5a2bf2cd7527799859147d6d69
parentdocs(changelog): fix dependency problem (diff)
downloadsl0ppij-9cc2645db024df0a95efda42aed34bd3def19b28.tar.gz
sl0ppij-9cc2645db024df0a95efda42aed34bd3def19b28.tar.xz
sl0ppij-9cc2645db024df0a95efda42aed34bd3def19b28.zip
Add a copy command option (#996)
Usage: zellij options --copy-command "xclip -sel clip" Co-authored-by: Christophe Verbinnen <christophev@knowbe4.com>
-rw-r--r--default-plugins/status-bar/src/main.rs19
-rw-r--r--default-plugins/status-bar/src/second_line.rs12
-rw-r--r--zellij-server/src/screen.rs5
-rw-r--r--zellij-server/src/tab/copy_command.rs39
-rw-r--r--zellij-server/src/tab/mod.rs28
-rw-r--r--zellij-server/src/tab/unit/tab_tests.rs2
-rw-r--r--zellij-server/src/unit/screen_tests.rs2
-rw-r--r--zellij-tile/src/data.rs1
-rw-r--r--zellij-utils/src/input/options.rs10
9 files changed, 112 insertions, 6 deletions
diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs
index bc322e6a..1a26ca14 100644
--- a/default-plugins/status-bar/src/main.rs
+++ b/default-plugins/status-bar/src/main.rs
@@ -10,7 +10,8 @@ use zellij_tile_utils::style;
use first_line::{ctrl_keys, superkey};
use second_line::{
- fullscreen_panes_to_hide, keybinds, locked_fullscreen_panes_to_hide, text_copied_hint,
+ fullscreen_panes_to_hide, keybinds, locked_fullscreen_panes_to_hide, system_clipboard_error,
+ text_copied_hint,
};
use tip::utils::get_cached_tip_name;
@@ -24,6 +25,7 @@ struct State {
tip_name: String,
mode_info: ModeInfo,
diplay_text_copied_hint: bool,
+ display_system_clipboard_failure: bool,
}
register_plugin!(State);
@@ -142,6 +144,7 @@ impl ZellijPlugin for State {
EventType::TabUpdate,
EventType::CopyToClipboard,
EventType::InputReceived,
+ EventType::SystemClipboardFailure,
]);
}
@@ -156,8 +159,12 @@ impl ZellijPlugin for State {
Event::CopyToClipboard => {
self.diplay_text_copied_hint = true;
}
+ Event::SystemClipboardFailure => {
+ self.display_system_clipboard_failure = true;
+ }
Event::InputReceived => {
self.diplay_text_copied_hint = false;
+ self.display_system_clipboard_failure = false;
}
_ => {}
}
@@ -188,12 +195,16 @@ impl ZellijPlugin for State {
if t.is_fullscreen_active {
second_line = if self.diplay_text_copied_hint {
text_copied_hint(&self.mode_info.palette)
+ } else if self.display_system_clipboard_failure {
+ system_clipboard_error(&self.mode_info.palette)
} else {
fullscreen_panes_to_hide(&self.mode_info.palette, t.panes_to_hide)
}
} else {
second_line = if self.diplay_text_copied_hint {
text_copied_hint(&self.mode_info.palette)
+ } else if self.display_system_clipboard_failure {
+ system_clipboard_error(&self.mode_info.palette)
} else {
keybinds(&self.mode_info, &self.tip_name, cols)
}
@@ -203,6 +214,8 @@ impl ZellijPlugin for State {
if t.is_fullscreen_active {
second_line = if self.diplay_text_copied_hint {
text_copied_hint(&self.mode_info.palette)
+ } else if self.display_system_clipboard_failure {
+ system_clipboard_error(&self.mode_info.palette)
} else {
locked_fullscreen_panes_to_hide(
&self.mode_info.palette,
@@ -212,6 +225,8 @@ impl ZellijPlugin for State {
} else {
second_line = if self.diplay_text_copied_hint {
text_copied_hint(&self.mode_info.palette)
+ } else if self.display_system_clipboard_failure {
+ system_clipboard_error(&self.mode_info.palette)
} else {
keybinds(&self.mode_info, &self.tip_name, cols)
}
@@ -220,6 +235,8 @@ impl ZellijPlugin for State {
_ => {
second_line = if self.diplay_text_copied_hint {
text_copied_hint(&self.mode_info.palette)
+ } else if self.display_system_clipboard_failure {
+ system_clipboard_error(&self.mode_info.palette)
} else {
keybinds(&self.mode_info, &self.tip_name, cols)
}
diff --git a/default-plugins/status-bar/src/second_line.rs b/default-plugins/status-bar/src/second_line.rs
index 6f3c6461..32a44425 100644
--- a/default-plugins/status-bar/src/second_line.rs
+++ b/default-plugins/status-bar/src/second_line.rs
@@ -241,6 +241,18 @@ pub fn text_copied_hint(palette: &Palette) -> LinePart {
}
}
+pub fn system_clipboard_error(palette: &Palette) -> LinePart {
+ let hint = " Error using the system clipboard.";
+ let red_color = match palette.red {
+ PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
+ PaletteColor::EightBit(color) => Fixed(color),
+ };
+ LinePart {
+ part: Style::new().fg(red_color).bold().paint(hint).to_string(),
+ len: hint.len(),
+ }
+}
+
pub fn fullscreen_panes_to_hide(palette: &Palette, panes_to_hide: usize) -> LinePart {
let white_color = match palette.white {
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index cfc99518..71ae0805 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -193,6 +193,7 @@ pub(crate) struct Screen {
colors: Palette,
draw_pane_frames: bool,
session_is_mirrored: bool,
+ copy_command: Option<String>,
}
impl Screen {
@@ -204,6 +205,7 @@ impl Screen {
mode_info: ModeInfo,
draw_pane_frames: bool,
session_is_mirrored: bool,
+ copy_command: Option<String>,
) -> Self {
Screen {
bus,
@@ -219,6 +221,7 @@ impl Screen {
default_mode_info: mode_info,
draw_pane_frames,
session_is_mirrored,
+ copy_command,
}
}
@@ -491,6 +494,7 @@ impl Screen {
self.connected_clients.clone(),
self.session_is_mirrored,
client_id,
+ self.copy_command.clone(),
);
tab.apply_layout(layout, new_pids, tab_index, client_id);
if self.session_is_mirrored {
@@ -692,6 +696,7 @@ pub(crate) fn screen_thread_main(
),
draw_pane_frames,
session_is_mirrored,
+ config_options.copy_command,
);
loop {
let (event, mut err_ctx) = screen
diff --git a/zellij-server/src/tab/copy_command.rs b/zellij-server/src/tab/copy_command.rs
new file mode 100644
index 00000000..61240950
--- /dev/null
+++ b/zellij-server/src/tab/copy_command.rs
@@ -0,0 +1,39 @@
+use std::io::prelude::*;
+use std::process::{Command, Stdio};
+
+pub struct CopyCommand {
+ command: String,
+ args: Vec<String>,
+}
+
+impl CopyCommand {
+ pub fn new(command: String) -> Self {
+ let mut command_with_args = command.split(' ').map(String::from);
+
+ Self {
+ command: command_with_args.next().expect("missing command"),
+ args: command_with_args.collect(),
+ }
+ }
+ pub fn set(&self, value: String) -> bool {
+ let process = match Command::new(self.command.clone())
+ .args(self.args.clone())
+ .stdin(Stdio::piped())
+ .spawn()
+ {
+ Err(why) => {
+ eprintln!("couldn't spawn {}: {}", self.command, why);
+ return false;
+ }
+ Ok(process) => process,
+ };
+
+ match process.stdin.unwrap().write_all(value.as_bytes()) {
+ Err(why) => {
+ eprintln!("couldn't write to {} stdin: {}", self.command, why);
+ false
+ }
+ Ok(_) => true,
+ }
+ }
+}
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index 2e98915a..cc83d37c 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -1,9 +1,11 @@
//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
//! as well as how they should be resized
+mod copy_command;
mod pane_grid;
mod pane_resizer;
+use copy_command::CopyCommand;
use zellij_utils::position::{Column, Line};
use zellij_utils::{position::Position, serde, zellij_tile};
@@ -119,6 +121,7 @@ pub(crate) struct Tab {
session_is_mirrored: bool,
pending_vte_events: HashMap<RawFd, Vec<VteBytes>>,
selecting_with_mouse: bool,
+ copy_command: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
@@ -299,6 +302,7 @@ impl Tab {
connected_clients_in_app: Rc<RefCell<HashSet<ClientId>>>,
session_is_mirrored: bool,
client_id: ClientId,
+ copy_command: Option<String>,
) -> Self {
let panes = BTreeMap::new();
@@ -335,6 +339,7 @@ impl Tab {
connected_clients_in_app,
connected_clients,
selecting_with_mouse: false,
+ copy_command,
}
}
@@ -1912,11 +1917,20 @@ impl Tab {
fn write_selection_to_clipboard(&self, selection: &str) {
let mut output = Output::default();
+ let mut system_clipboard_failure = false;
output.add_clients(&self.connected_clients);
- output.push_str_to_multiple_clients(
- &format!("\u{1b}]52;c;{}\u{1b}\\", base64::encode(selection)),
- self.connected_clients.iter().copied(),
- );
+ match self.copy_command.clone() {
+ Some(copy_command) => {
+ let system_clipboard = CopyCommand::new(copy_command);
+ system_clipboard_failure = !system_clipboard.set(selection.to_owned());
+ }
+ None => {
+ output.push_str_to_multiple_clients(
+ &format!("\u{1b}]52;c;{}\u{1b}\\", base64::encode(selection)),
+ self.connected_clients.iter().copied(),
+ );
+ }
+ }
// TODO: ideally we should be sending the Render instruction from the screen
self.senders
@@ -1926,7 +1940,11 @@ impl Tab {
.send_to_plugin(PluginInstruction::Update(
None,
None,
- Event::CopyToClipboard,
+ if system_clipboard_failure {
+ Event::SystemClipboardFailure
+ } else {
+ Event::CopyToClipboard
+ },
))
.unwrap();
}
diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs
index 13b97124..5ac63f6b 100644
--- a/zellij-server/src/tab/unit/tab_tests.rs
+++ b/zellij-server/src/tab/unit/tab_tests.rs
@@ -96,6 +96,7 @@ fn create_new_tab(size: Size) -> Tab {
let mut connected_clients = HashSet::new();
connected_clients.insert(client_id);
let connected_clients = Rc::new(RefCell::new(connected_clients));
+ let copy_command = None;
let mut tab = Tab::new(
index,
position,
@@ -110,6 +111,7 @@ fn create_new_tab(size: Size) -> Tab {
connected_clients,
session_is_mirrored,
client_id,
+ copy_command,
);
tab.apply_layout(
LayoutTemplate::default().try_into().unwrap(),
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index ca34645d..1fbc85fc 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -91,6 +91,7 @@ fn create_new_screen(size: Size) -> Screen {
let mode_info = ModeInfo::default();
let draw_pane_frames = false;
let session_is_mirrored = true;
+ let copy_command = None;
Screen::new(
bus,
&client_attributes,
@@ -98,6 +99,7 @@ fn create_new_screen(size: Size) -> Screen {
mode_info,
draw_pane_frames,
session_is_mirrored,
+ copy_command,
)
}
diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs
index 36b44e25..e120748a 100644
--- a/zellij-tile/src/data.rs
+++ b/zellij-tile/src/data.rs
@@ -76,6 +76,7 @@ pub enum Event {
Mouse(Mouse),
Timer(f64),
CopyToClipboard,
+ SystemClipboardFailure,
InputReceived,
Visible(bool),
}
diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs
index dcaad306..d8bef694 100644
--- a/zellij-utils/src/input/options.rs
+++ b/zellij-utils/src/input/options.rs
@@ -74,6 +74,11 @@ pub struct Options {
pub on_force_close: Option<OnForceClose>,
#[structopt(long)]
pub scroll_buffer_size: Option<usize>,
+
+ /// Switch to using a user supplied command for clipboard instead of OSC52
+ #[structopt(long)]
+ #[serde(default)]
+ pub copy_command: Option<String>,
}
impl Options {
@@ -99,6 +104,7 @@ impl Options {
let theme = other.theme.or_else(|| self.theme.clone());
let on_force_close = other.on_force_close.or(self.on_force_close);
let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
+ let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
Options {
simplified_ui,
@@ -111,6 +117,7 @@ impl Options {
mirror_session,
on_force_close,
scroll_buffer_size,
+ copy_command,
}
}
@@ -140,6 +147,7 @@ impl Options {
let theme = other.theme.or_else(|| self.theme.clone());
let on_force_close = other.on_force_close.or(self.on_force_close);
let scroll_buffer_size = other.scroll_buffer_size.or(self.scroll_buffer_size);
+ let copy_command = other.copy_command.or_else(|| self.copy_command.clone());
Options {
simplified_ui,
@@ -152,6 +160,7 @@ impl Options {
mirror_session,
on_force_close,
scroll_buffer_size,
+ copy_command,
}
}
@@ -200,6 +209,7 @@ impl From<CliOptions> for Options {
mirror_session: opts.mirror_session,
on_force_close: opts.on_force_close,
scroll_buffer_size: opts.scroll_buffer_size,
+ copy_command: opts.copy_command,
}
}
}