use once_cell::sync::Lazy;
use regex::Regex;
const KILL_RING_MAX_SIZE: usize = 10;
static KILL_RING: std::sync::Mutex<Vec<String>> = std::sync::Mutex::new(Vec::new());
static KILL_RING_INDEX: std::sync::Mutex<usize> = std::sync::Mutex::new(0);
static LAST_ACTION_WAS_KILL: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
static LAST_YANK_START: std::sync::Mutex<usize> = std::sync::Mutex::new(0);
static LAST_YANK_LENGTH: std::sync::Mutex<usize> = std::sync::Mutex::new(0);
static LAST_ACTION_WAS_YANK: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
pub fn push_to_kill_ring(text: &str, direction: &str) {
if text.is_empty() {
return;
}
let mut kill_ring = KILL_RING.lock().unwrap();
let mut last_action_kill = LAST_ACTION_WAS_KILL.lock().unwrap();
if *last_action_kill && !kill_ring.is_empty() {
if direction == "prepend" {
kill_ring[0] = format!("{}{}", text, kill_ring[0]);
} else {
kill_ring[0] = format!("{}{}", kill_ring[0], text);
}
} else {
kill_ring.insert(0, text.to_string());
if kill_ring.len() > KILL_RING_MAX_SIZE {
kill_ring.pop();
}
}
*last_action_kill = true;
let mut last_action_yank = LAST_ACTION_WAS_YANK.lock().unwrap();
*last_action_yank = false;
}
pub fn get_last_kill() -> String {
let kill_ring = KILL_RING.lock().unwrap();
kill_ring.first().cloned().unwrap_or_default()
}
pub fn get_kill_ring_item(index: usize) -> String {
let kill_ring = KILL_RING.lock().unwrap();
if kill_ring.is_empty() {
return String::new();
}
let normalized_index = ((index % kill_ring.len()) + kill_ring.len()) % kill_ring.len();
kill_ring.get(normalized_index).cloned().unwrap_or_default()
}
pub fn get_kill_ring_size() -> usize {
let kill_ring = KILL_RING.lock().unwrap();
kill_ring.len()
}
pub fn clear_kill_ring() {
let mut kill_ring = KILL_RING.lock().unwrap();
let mut kill_ring_index = KILL_RING_INDEX.lock().unwrap();
let mut last_action_kill = LAST_ACTION_WAS_KILL.lock().unwrap();
let mut last_action_yank = LAST_ACTION_WAS_YANK.lock().unwrap();
let mut last_yank_start = LAST_YANK_START.lock().unwrap();
let mut last_yank_length = LAST_YANK_LENGTH.lock().unwrap();
kill_ring.clear();
*kill_ring_index = 0;
*last_action_kill = false;
*last_action_yank = false;
*last_yank_start = 0;
*last_yank_length = 0;
}
pub fn reset_kill_accumulation() {
let mut last_action_kill = LAST_ACTION_WAS_KILL.lock().unwrap();
*last_action_kill = false;
}
pub fn record_yank(start: usize, length: usize) {
let mut last_yank_start = LAST_YANK_START.lock().unwrap();
let mut last_yank_length = LAST_YANK_LENGTH.lock().unwrap();
let mut last_action_yank = LAST_ACTION_WAS_YANK.lock().unwrap();
let mut kill_ring_index = KILL_RING_INDEX.lock().unwrap();
*last_yank_start = start;
*last_yank_length = length;
*last_action_yank = true;
*kill_ring_index = 0;
}
pub fn can_yank_pop() -> bool {
let last_action_yank = LAST_ACTION_WAS_YANK.lock().unwrap();
let kill_ring = KILL_RING.lock().unwrap();
*last_action_yank && kill_ring.len() > 1
}
pub fn yank_pop() -> Option<YankPopResult> {
let last_action_yank = LAST_ACTION_WAS_YANK.lock().unwrap();
let kill_ring = KILL_RING.lock().unwrap();
if !*last_action_yank || kill_ring.len() <= 1 {
return None;
}
drop(last_action_yank);
drop(kill_ring);
let mut kill_ring_index = KILL_RING_INDEX.lock().unwrap();
let last_yank_start = LAST_YANK_START.lock().unwrap();
let last_yank_length = LAST_YANK_LENGTH.lock().unwrap();
let kill_ring = KILL_RING.lock().unwrap();
*kill_ring_index = (*kill_ring_index + 1) % kill_ring.len();
let text = kill_ring.get(*kill_ring_index).cloned().unwrap_or_default();
Some(YankPopResult {
text,
start: *last_yank_start,
length: *last_yank_length,
})
}
pub fn update_yank_length(length: usize) {
let mut last_yank_length = LAST_YANK_LENGTH.lock().unwrap();
*last_yank_length = length;
}
pub fn reset_yank_state() {
let mut last_action_yank = LAST_ACTION_WAS_YANK.lock().unwrap();
*last_action_yank = false;
}
#[derive(Debug, Clone)]
pub struct YankPopResult {
pub text: String,
pub start: usize,
pub length: usize,
}
pub static VIM_WORD_CHAR_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^[\p{L}\p{N}\p{M}_]$").unwrap());
pub static WHITESPACE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s").unwrap());
pub fn is_vim_word_char(ch: &str) -> bool {
VIM_WORD_CHAR_REGEX.is_match(ch)
}
pub fn is_vim_whitespace(ch: &str) -> bool {
WHITESPACE_REGEX.is_match(ch)
}
pub fn is_vim_punctuation(ch: &str) -> bool {
!ch.is_empty() && !is_vim_whitespace(ch) && !is_vim_word_char(ch)
}