diff --git a/src/engine.rs b/src/engine.rs index 29d14810..859b1d34 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,5 @@ use std::path::PathBuf; -use crossterm::event::{DisableBracketedPaste, EnableBracketedPaste}; -use crossterm::execute; use itertools::Itertools; use crate::{enums::ReedlineRawEvent, CursorConfig}; @@ -31,6 +29,7 @@ use { painting::{Painter, PromptLines}, prompt::{PromptEditMode, PromptHistorySearchStatus}, result::{ReedlineError, ReedlineErrorVariants}, + terminal_extensions::{bracketed_paste::BracketedPasteGuard, kitty::KittyProtocolGuard}, utils::text_manipulation, EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, Prompt, PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult, Validator, @@ -144,11 +143,11 @@ pub struct Reedline { // Use different cursors depending on the current edit mode cursor_shapes: Option, - // Indicate if global terminal have enabled BracketedPaste - bracket_paste_enabled: bool, + // Manage bracketed paste mode + bracketed_paste: BracketedPasteGuard, - // Use kitty protocol to handle escape code input or not - use_kitty_protocol: bool, + // Manage optional kitty protocol + kitty_protocol: KittyProtocolGuard, #[cfg(feature = "external_printer")] external_printer: Option>, @@ -172,12 +171,6 @@ impl Drop for Reedline { // Ensures that the terminal is in a good state if we panic semigracefully // Calling `disable_raw_mode()` twice is fine with Linux let _ignore = terminal::disable_raw_mode(); - if self.bracket_paste_enabled { - let _ = execute!(io::stdout(), DisableBracketedPaste); - } - if self.use_kitty_protocol { - let _ = execute!(io::stdout(), event::PopKeyboardEnhancementFlags); - } } } @@ -223,8 +216,8 @@ impl Reedline { menus: Vec::new(), buffer_editor: None, cursor_shapes: None, - bracket_paste_enabled: false, - use_kitty_protocol: false, + bracketed_paste: BracketedPasteGuard::default(), + kitty_protocol: KittyProtocolGuard::default(), #[cfg(feature = "external_printer")] external_printer: None, } @@ -242,20 +235,14 @@ impl Reedline { /// Enable BracketedPaste feature. pub fn enable_bracketed_paste(&mut self) -> Result<()> { - let res = execute!(io::stdout(), EnableBracketedPaste); - if res.is_ok() { - self.bracket_paste_enabled = true; - } - res + self.bracketed_paste.set(true); + Ok(()) } /// Disable BracketedPaste feature. pub fn disable_bracketed_paste(&mut self) -> Result<()> { - let res = execute!(io::stdout(), DisableBracketedPaste); - if res.is_ok() { - self.bracket_paste_enabled = false; - } - res + self.bracketed_paste.set(false); + Ok(()) } /// Return terminal support on keyboard enhancement @@ -269,12 +256,12 @@ impl Reedline { /// Enable keyboard enhancement to disambiguate escape code pub fn enable_kitty_protocol(&mut self) { - self.use_kitty_protocol = true; + self.kitty_protocol.set(true); } /// Disable keyboard enhancement to disambiguate escape code pub fn disable_kitty_protocol(&mut self) { - self.use_kitty_protocol = false; + self.kitty_protocol.set(false); } /// Return the previously generated history session id @@ -627,13 +614,13 @@ impl Reedline { /// and the `Ok` variant wraps a [`Signal`] which handles user inputs. pub fn read_line(&mut self, prompt: &dyn Prompt) -> Result { terminal::enable_raw_mode()?; + self.bracketed_paste.enter(); + self.kitty_protocol.enter(); let result = self.read_line_helper(prompt); - if self.use_kitty_protocol { - let _ = execute!(io::stdout(), event::PopKeyboardEnhancementFlags); - } - + self.bracketed_paste.exit(); + self.kitty_protocol.exit(); terminal::disable_raw_mode()?; result } @@ -679,31 +666,6 @@ impl Reedline { let mut crossterm_events: Vec = vec![]; let mut reedline_events: Vec = vec![]; - if self.use_kitty_protocol { - if let Ok(true) = crossterm::terminal::supports_keyboard_enhancement() { - // enable kitty protocol - // - // Note that, currently, only the following support this protocol: - // * [kitty terminal](https://sw.kovidgoyal.net/kitty/) - // * [foot terminal](https://codeberg.org/dnkl/foot/issues/319) - // * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html) - // * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131) - // * [neovim text editor](https://github.com/neovim/neovim/pull/18181) - // * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103) - // * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138) - // - // Refer to https://sw.kovidgoyal.net/kitty/keyboard-protocol/ if you're curious. - let _ = execute!( - io::stdout(), - event::PushKeyboardEnhancementFlags( - event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES - ) - ); - } else { - // TODO: Log or warning - } - } - loop { let mut paste_enter_state = false; diff --git a/src/lib.rs b/src/lib.rs index 83fb2a9e..aa60a103 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -280,6 +280,8 @@ pub use menu::{ menu_functions, ColumnarMenu, ListMenu, Menu, MenuEvent, MenuTextStyle, ReedlineMenu, }; +mod terminal_extensions; + mod utils; mod external_printer; diff --git a/src/terminal_extensions/bracketed_paste.rs b/src/terminal_extensions/bracketed_paste.rs new file mode 100644 index 00000000..00f874ac --- /dev/null +++ b/src/terminal_extensions/bracketed_paste.rs @@ -0,0 +1,37 @@ +use crossterm::{execute, event}; + +/// Helper managing proper setup and teardown of bracketed paste mode +/// +/// https://en.wikipedia.org/wiki/Bracketed-paste +#[derive(Default)] +pub(crate) struct BracketedPasteGuard { + enabled: bool, + active: bool, +} + +impl BracketedPasteGuard { + pub fn set(&mut self, enable: bool) { + self.enabled = enable; + } + pub fn enter(&mut self) { + if self.enabled && !self.active { + let _ = execute!(std::io::stdout(), event::EnableBracketedPaste); + self.active = true; + } + } + pub fn exit(&mut self) { + if self.active { + let _ = execute!(std::io::stdout(), event::DisableBracketedPaste); + self.active = false; + } + } +} + +impl Drop for BracketedPasteGuard { + fn drop(&mut self) { + if self.active { + let _ = execute!(std::io::stdout(), event::DisableBracketedPaste); + } + } +} + diff --git a/src/terminal_extensions/kitty.rs b/src/terminal_extensions/kitty.rs new file mode 100644 index 00000000..94e6e5d7 --- /dev/null +++ b/src/terminal_extensions/kitty.rs @@ -0,0 +1,52 @@ +use crossterm::{event, execute}; + +/// Helper managing proper setup and teardown of the kitty keyboard enhancement protocol +/// +/// Note that, currently, only the following support this protocol: +/// * [kitty terminal](https://sw.kovidgoyal.net/kitty/) +/// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319) +/// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html) +/// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131) +/// * [neovim text editor](https://github.com/neovim/neovim/pull/18181) +/// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103) +/// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138) +/// +/// Refer to https://sw.kovidgoyal.net/kitty/keyboard-protocol/ if you're curious. +#[derive(Default)] +pub(crate) struct KittyProtocolGuard { + enabled: bool, + active: bool, +} + +impl KittyProtocolGuard { + pub fn set(&mut self, enable: bool) { + self.enabled = + enable && crossterm::terminal::supports_keyboard_enhancement().unwrap_or_default(); + } + pub fn enter(&mut self) { + if self.enabled && !self.active { + let _ = execute!( + std::io::stdout(), + event::PushKeyboardEnhancementFlags( + event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + ) + ); + + self.active = true; + } + } + pub fn exit(&mut self) { + if self.active { + let _ = execute!(std::io::stdout(), event::PopKeyboardEnhancementFlags); + self.active = false; + } + } +} + +impl Drop for KittyProtocolGuard { + fn drop(&mut self) { + if self.active { + let _ = execute!(std::io::stdout(), event::PopKeyboardEnhancementFlags); + } + } +} diff --git a/src/terminal_extensions/mod.rs b/src/terminal_extensions/mod.rs new file mode 100644 index 00000000..69a718e1 --- /dev/null +++ b/src/terminal_extensions/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod kitty; +pub(crate) mod bracketed_paste;