diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index ea36ea5f..aa8b9c82 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -186,6 +186,36 @@ impl From<&ScreenInstruction> for ScreenContext { } } +#[derive(Debug, Clone)] +pub(crate) struct CopyOptions { + pub command: Option, + pub clipboard: Clipboard, + pub copy_on_select: bool, +} + +impl CopyOptions { + pub(crate) fn new( + copy_command: Option, + copy_clipboard: Clipboard, + copy_on_select: bool, + ) -> Self { + Self { + command: copy_command, + clipboard: copy_clipboard, + copy_on_select, + } + } + + #[cfg(test)] + pub(crate) fn default() -> Self { + Self { + command: None, + clipboard: Clipboard::default(), + copy_on_select: true, + } + } +} + /// A [`Screen`] holds multiple [`Tab`]s, each one holding multiple [`panes`](crate::client::panes). /// It only directly controls which tab is active, delegating the rest to the individual `Tab`. pub(crate) struct Screen { @@ -210,13 +240,11 @@ pub(crate) struct Screen { style: Style, draw_pane_frames: bool, session_is_mirrored: bool, - copy_command: Option, - copy_clipboard: Clipboard, + copy_options: CopyOptions, } impl Screen { /// Creates and returns a new [`Screen`]. - #[allow(clippy::too_many_arguments)] pub fn new( bus: Bus, client_attributes: &ClientAttributes, @@ -224,8 +252,7 @@ impl Screen { mode_info: ModeInfo, draw_pane_frames: bool, session_is_mirrored: bool, - copy_command: Option, - copy_clipboard: Clipboard, + copy_options: CopyOptions, ) -> Self { Screen { bus, @@ -243,8 +270,7 @@ impl Screen { default_mode_info: mode_info, draw_pane_frames, session_is_mirrored, - copy_command, - copy_clipboard, + copy_options, } } @@ -546,8 +572,7 @@ impl Screen { self.connected_clients.clone(), self.session_is_mirrored, client_id, - self.copy_command.clone(), - self.copy_clipboard.clone(), + self.copy_options.clone(), ); tab.apply_layout(layout, new_pids, tab_index, client_id); if self.session_is_mirrored { @@ -743,6 +768,11 @@ pub(crate) fn screen_thread_main( let capabilities = config_options.simplified_ui; let draw_pane_frames = config_options.pane_frames.unwrap_or(true); let session_is_mirrored = config_options.mirror_session.unwrap_or(false); + let copy_options = CopyOptions::new( + config_options.copy_command, + config_options.copy_clipboard.unwrap_or_default(), + config_options.copy_on_select.unwrap_or(true), + ); let mut screen = Screen::new( bus, @@ -757,8 +787,7 @@ pub(crate) fn screen_thread_main( ), draw_pane_frames, session_is_mirrored, - config_options.copy_command, - config_options.copy_clipboard.unwrap_or_default(), + copy_options, ); loop { let (event, mut err_ctx) = screen diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 99e9e2f4..8a9fb0ea 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -6,10 +6,10 @@ mod copy_command; use copy_command::CopyCommand; use zellij_tile::prelude::Style; -use zellij_utils::input::options::Clipboard; use zellij_utils::position::{Column, Line}; use zellij_utils::{position::Position, serde, zellij_tile}; +use crate::screen::CopyOptions; use crate::ui::pane_boundaries_frame::FrameParams; use self::clipboard::ClipboardProvider; @@ -89,6 +89,7 @@ pub(crate) struct Tab { // TODO: used only to focus the pane when the layout is loaded // it seems that optimization is possible using `active_panes` focus_pane_id: Option, + copy_on_select: bool, } #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -281,8 +282,7 @@ impl Tab { connected_clients_in_app: Rc>>, session_is_mirrored: bool, client_id: ClientId, - copy_command: Option, - copy_clipboard: Clipboard, + copy_options: CopyOptions, ) -> Self { let name = if name.is_empty() { format!("Tab #{}", index + 1) @@ -322,9 +322,9 @@ impl Tab { style, ); - let clipboard_provider = match copy_command { + let clipboard_provider = match copy_options.command { Some(command) => ClipboardProvider::Command(CopyCommand::new(command)), - None => ClipboardProvider::Osc52(copy_clipboard), + None => ClipboardProvider::Osc52(copy_options.clipboard), }; Tab { @@ -351,6 +351,7 @@ impl Tab { link_handler: Rc::new(RefCell::new(LinkHandler::new())), clipboard_provider, focus_pane_id: None, + copy_on_select: copy_options.copy_on_select, } } @@ -1586,7 +1587,9 @@ impl Tab { return; } + // read these here to avoid use of borrowed `*self`, since we are holding active_pane let selecting = self.selecting_with_mouse; + let copy_on_release = self.copy_on_select; let active_pane = self.get_active_pane_or_floating_pane_mut(client_id); if let Some(active_pane) = active_pane { @@ -1604,11 +1607,15 @@ impl Tab { self.write_to_active_terminal(mouse_event.into_bytes(), client_id); } else if selecting { active_pane.end_selection(&relative_position, client_id); - let selected_text = active_pane.get_selected_text(); - active_pane.reset_selection(); - if let Some(selected_text) = selected_text { - self.write_selection_to_clipboard(&selected_text); + if copy_on_release { + let selected_text = active_pane.get_selected_text(); + active_pane.reset_selection(); + + if let Some(selected_text) = selected_text { + self.write_selection_to_clipboard(&selected_text); + } } + self.selecting_with_mouse = false; } } diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 76ab9cae..ba6fc8f2 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -1,4 +1,5 @@ use super::{Output, Tab}; +use crate::screen::CopyOptions; use crate::zellij_tile::data::{ModeInfo, Palette}; use crate::{ os_input_output::{AsyncReader, Pid, ServerOsApi}, @@ -11,7 +12,6 @@ use std::path::PathBuf; use zellij_tile::prelude::Style; use zellij_utils::envs::set_session_name; use zellij_utils::input::layout::LayoutTemplate; -use zellij_utils::input::options::Clipboard; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::Size; use zellij_utils::position::Position; @@ -102,9 +102,8 @@ 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 character_cell_info = Rc::new(RefCell::new(None)); - let clipboard = Clipboard::default(); + let copy_options = CopyOptions::default(); let mut tab = Tab::new( index, position, @@ -120,8 +119,7 @@ fn create_new_tab(size: Size) -> Tab { connected_clients, session_is_mirrored, client_id, - copy_command, - clipboard, + copy_options, ); tab.apply_layout( LayoutTemplate::default().try_into().unwrap(), diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 44de0842..62dfdc01 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -1,4 +1,5 @@ use super::Tab; +use crate::screen::CopyOptions; use crate::zellij_tile::data::{ModeInfo, Palette}; use crate::{ os_input_output::{AsyncReader, Pid, ServerOsApi}, @@ -10,7 +11,6 @@ use std::convert::TryInto; use std::path::PathBuf; use zellij_tile::prelude::Style; use zellij_utils::input::layout::LayoutTemplate; -use zellij_utils::input::options::Clipboard; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; @@ -99,8 +99,7 @@ fn create_new_tab(size: Size) -> Tab { let character_cell_info = Rc::new(RefCell::new(None)); connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); - let copy_command = None; - let copy_clipboard = Clipboard::default(); + let copy_options = CopyOptions::default(); let mut tab = Tab::new( index, position, @@ -116,8 +115,7 @@ fn create_new_tab(size: Size) -> Tab { connected_clients, session_is_mirrored, client_id, - copy_command, - copy_clipboard, + copy_options, ); tab.apply_layout( LayoutTemplate::default().try_into().unwrap(), @@ -146,8 +144,7 @@ fn create_new_tab_with_cell_size( 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 copy_clipboard = Clipboard::default(); + let copy_options = CopyOptions::default(); let mut tab = Tab::new( index, position, @@ -163,8 +160,7 @@ fn create_new_tab_with_cell_size( connected_clients, session_is_mirrored, client_id, - copy_command, - copy_clipboard, + copy_options, ); 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 333d85b0..e52a88ae 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -1,4 +1,4 @@ -use super::{Screen, ScreenInstruction}; +use super::{CopyOptions, Screen, ScreenInstruction}; use crate::panes::PaneId; use crate::zellij_tile::data::{ModeInfo, Palette}; use crate::{ @@ -10,7 +10,6 @@ use std::convert::TryInto; use std::path::PathBuf; use zellij_utils::input::command::TerminalAction; use zellij_utils::input::layout::LayoutTemplate; -use zellij_utils::input::options::Clipboard; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; @@ -92,8 +91,8 @@ 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; - let copy_clipboard = Clipboard::default(); + let copy_options = CopyOptions::default(); + Screen::new( bus, &client_attributes, @@ -101,8 +100,7 @@ fn create_new_screen(size: Size) -> Screen { mode_info, draw_pane_frames, session_is_mirrored, - copy_command, - copy_clipboard, + copy_options, ) } diff --git a/zellij-utils/assets/config/default.yaml b/zellij-utils/assets/config/default.yaml index 642d4644..070bacb6 100644 --- a/zellij-utils/assets/config/default.yaml +++ b/zellij-utils/assets/config/default.yaml @@ -42,6 +42,9 @@ keybinds: key: [ Alt: '+'] - action: [Resize: Decrease,] key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] locked: - action: [SwitchToMode: Normal,] key: [Ctrl: 'g',] @@ -316,6 +319,9 @@ keybinds: key: [ Alt: '+'] - action: [Resize: Decrease,] key: [ Alt: '-'] + # uncomment this and adjust key if using copy_on_select=false + # - action: [Copy: ] + # key: [ Alt: 'c'] renametab: - action: [SwitchToMode: Normal,] key: [Char: "\n", Ctrl: 'c', Esc] @@ -536,4 +542,7 @@ plugins: # Options: # - system (default) # - primary -# copy_clipboard: primary +#copy_clipboard: primary + +# Enable or disable automatic copy (and clear) of selection when releasing mouse +#copy_on_select: true diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index 34b4dc88..861e4877 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -84,9 +84,14 @@ pub struct Options { #[clap(long, arg_enum, ignore_case = true, conflicts_with = "copy-command")] #[serde(default)] pub copy_clipboard: Option, + + /// Automatically copy when selecting text (true or false) + #[clap(long)] + #[serde(default)] + pub copy_on_select: Option, } -#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, PartialEq)] +#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] pub enum Clipboard { #[serde(alias = "system")] System, @@ -124,7 +129,8 @@ impl Options { 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()); - let copy_clipboard = other.copy_clipboard.or_else(|| self.copy_clipboard.clone()); + let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard); + let copy_on_select = other.copy_on_select.or(self.copy_on_select); Options { simplified_ui, @@ -139,6 +145,7 @@ impl Options { scroll_buffer_size, copy_command, copy_clipboard, + copy_on_select, } } @@ -169,7 +176,8 @@ impl Options { 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()); - let copy_clipboard = other.copy_clipboard.or_else(|| self.copy_clipboard.clone()); + let copy_clipboard = other.copy_clipboard.or(self.copy_clipboard); + let copy_on_select = other.copy_on_select.or(self.copy_on_select); Options { simplified_ui, @@ -184,6 +192,7 @@ impl Options { scroll_buffer_size, copy_command, copy_clipboard, + copy_on_select, } } @@ -234,6 +243,7 @@ impl From for Options { scroll_buffer_size: opts.scroll_buffer_size, copy_command: opts.copy_command, copy_clipboard: opts.copy_clipboard, + copy_on_select: opts.copy_on_select, } } }