use std::io::{self, Write};
use std::sync::atomic::{AtomicBool, Ordering};
static BRACKETED_PASTE_ENABLED: AtomicBool = AtomicBool::new(false);
pub fn is_bracketed_paste_enabled() -> bool {
BRACKETED_PASTE_ENABLED.load(Ordering::SeqCst)
}
pub fn enable_bracketed_paste() -> io::Result<()> {
let mut stdout = io::stdout();
stdout.write_all(b"\x1b[?2004h")?;
stdout.flush()?;
BRACKETED_PASTE_ENABLED.store(true, Ordering::SeqCst);
Ok(())
}
pub fn disable_bracketed_paste() -> io::Result<()> {
let mut stdout = io::stdout();
stdout.write_all(b"\x1b[?2004l")?;
stdout.flush()?;
BRACKETED_PASTE_ENABLED.store(false, Ordering::SeqCst);
Ok(())
}
pub struct BracketedPasteGuard {
was_enabled: bool,
}
impl BracketedPasteGuard {
pub fn new() -> io::Result<Self> {
let was_enabled = is_bracketed_paste_enabled();
if !was_enabled {
enable_bracketed_paste()?;
}
Ok(Self { was_enabled })
}
}
impl Drop for BracketedPasteGuard {
fn drop(&mut self) {
if !self.was_enabled {
let _ = disable_bracketed_paste();
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PasteEvent {
pub content: String,
}
impl PasteEvent {
pub fn new(content: impl Into<String>) -> Self {
Self {
content: content.into(),
}
}
pub fn content(&self) -> &str {
&self.content
}
pub fn len(&self) -> usize {
self.content.len()
}
pub fn is_empty(&self) -> bool {
self.content.is_empty()
}
pub fn line_count(&self) -> usize {
self.content.lines().count().max(1)
}
pub fn is_multiline(&self) -> bool {
self.content.contains('\n')
}
pub fn lines(&self) -> impl Iterator<Item = &str> {
self.content.lines()
}
}
use std::cell::RefCell;
use std::rc::Rc;
type PasteHandlerRc = Rc<dyn Fn(&PasteEvent)>;
thread_local! {
static PASTE_HANDLERS: RefCell<Vec<PasteHandlerRc>> = RefCell::new(Vec::new());
}
pub(crate) fn register_paste_handler<F>(handler: F)
where
F: Fn(&PasteEvent) + 'static,
{
PASTE_HANDLERS.with(|handlers| {
handlers.borrow_mut().push(Rc::new(handler));
});
}
pub(crate) fn clear_paste_handlers() {
PASTE_HANDLERS.with(|handlers| {
handlers.borrow_mut().clear();
});
}
pub fn dispatch_paste(content: &str) {
let event = PasteEvent::new(content);
PASTE_HANDLERS.with(|handlers| {
for handler in handlers.borrow().iter() {
handler(&event);
}
});
}
pub fn use_paste<F>(handler: F)
where
F: Fn(&PasteEvent) + 'static,
{
register_paste_handler(handler);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_paste_event_creation() {
let event = PasteEvent::new("Hello, World!");
assert_eq!(event.content(), "Hello, World!");
assert_eq!(event.len(), 13);
assert!(!event.is_empty());
}
#[test]
fn test_paste_event_empty() {
let event = PasteEvent::new("");
assert!(event.is_empty());
assert_eq!(event.len(), 0);
}
#[test]
fn test_paste_event_multiline() {
let event = PasteEvent::new("Line 1\nLine 2\nLine 3");
assert!(event.is_multiline());
assert_eq!(event.line_count(), 3);
}
#[test]
fn test_paste_event_single_line() {
let event = PasteEvent::new("Single line");
assert!(!event.is_multiline());
assert_eq!(event.line_count(), 1);
}
#[test]
fn test_paste_event_lines() {
let event = PasteEvent::new("A\nB\nC");
let lines: Vec<&str> = event.lines().collect();
assert_eq!(lines, vec!["A", "B", "C"]);
}
#[test]
fn test_bracketed_paste_flag() {
BRACKETED_PASTE_ENABLED.store(false, Ordering::SeqCst);
assert!(!is_bracketed_paste_enabled());
BRACKETED_PASTE_ENABLED.store(true, Ordering::SeqCst);
assert!(is_bracketed_paste_enabled());
BRACKETED_PASTE_ENABLED.store(false, Ordering::SeqCst);
}
#[test]
fn test_paste_handler_dispatch() {
clear_paste_handlers();
let received = Rc::new(RefCell::new(String::new()));
let received_clone = received.clone();
register_paste_handler(move |event| {
*received_clone.borrow_mut() = event.content().to_string();
});
dispatch_paste("test paste");
assert_eq!(*received.borrow(), "test paste");
clear_paste_handlers();
}
#[test]
fn test_multiple_paste_handlers() {
clear_paste_handlers();
let count = Rc::new(RefCell::new(0));
let count1 = count.clone();
let count2 = count.clone();
register_paste_handler(move |_| {
*count1.borrow_mut() += 1;
});
register_paste_handler(move |_| {
*count2.borrow_mut() += 1;
});
dispatch_paste("test");
assert_eq!(*count.borrow(), 2);
clear_paste_handlers();
}
}