use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum YankType {
#[default]
Characterwise,
Linewise,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RegisterContent {
pub text: String,
pub yank_type: YankType,
}
impl RegisterContent {
#[must_use]
pub fn new(text: impl Into<String>, yank_type: YankType) -> Self {
Self {
text: text.into(),
yank_type,
}
}
#[must_use]
pub fn characterwise(text: impl Into<String>) -> Self {
Self::new(text, YankType::Characterwise)
}
#[must_use]
pub fn linewise(text: impl Into<String>) -> Self {
Self::new(text, YankType::Linewise)
}
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn is_empty(&self) -> bool {
self.text.is_empty()
}
#[must_use]
pub const fn is_characterwise(&self) -> bool {
matches!(self.yank_type, YankType::Characterwise)
}
#[must_use]
pub const fn is_linewise(&self) -> bool {
matches!(self.yank_type, YankType::Linewise)
}
}
impl Default for RegisterContent {
fn default() -> Self {
Self {
text: String::new(),
yank_type: YankType::Characterwise,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Register {
Default,
Slot(char),
History(u8),
System,
Session(char),
PeerHistory {
client: usize,
index: u8,
},
}
impl Register {
#[must_use]
pub const fn is_bank_register(&self) -> bool {
matches!(self, Self::Default | Self::Slot(_))
}
#[must_use]
pub const fn is_read_only(&self) -> bool {
matches!(self, Self::History(_) | Self::PeerHistory { .. })
}
#[must_use]
pub const fn is_session_scoped(&self) -> bool {
matches!(self, Self::Session(_) | Self::PeerHistory { .. })
}
}
#[derive(Debug, Clone)]
pub struct RegisterBank {
unnamed: RegisterContent,
named: HashMap<char, RegisterContent>,
}
impl RegisterBank {
#[must_use]
pub fn new() -> Self {
Self {
unnamed: RegisterContent::default(),
named: HashMap::new(),
}
}
#[must_use]
pub const fn get(&self) -> &RegisterContent {
&self.unnamed
}
pub fn set(&mut self, content: RegisterContent) {
self.unnamed = content;
}
#[must_use]
pub fn get_named(&self, name: char) -> Option<&RegisterContent> {
if name.is_ascii_lowercase() {
self.named.get(&name)
} else {
None
}
}
pub fn set_named(&mut self, name: char, content: RegisterContent) -> bool {
if name.is_ascii_lowercase() {
self.named.insert(name, content);
true
} else {
false
}
}
pub fn append_named(&mut self, name: char, content: &str) -> bool {
if name.is_ascii_uppercase() {
let lower = name.to_ascii_lowercase();
if let Some(existing) = self.named.get_mut(&lower) {
existing.text.push_str(content);
} else {
self.named
.insert(lower, RegisterContent::characterwise(content.to_string()));
}
true
} else {
false
}
}
pub fn clear(&mut self) {
self.unnamed = RegisterContent::default();
}
pub fn clear_named(&mut self, name: char) -> bool {
if name.is_ascii_lowercase() {
self.named.remove(&name).is_some()
} else {
false
}
}
pub fn clear_all(&mut self) {
self.unnamed = RegisterContent::default();
self.named.clear();
}
#[must_use]
pub fn get_by_name(&self, name: Option<char>) -> Option<&RegisterContent> {
match name {
None | Some('"') => Some(&self.unnamed),
Some(c) if c.is_ascii_lowercase() => self.named.get(&c),
_ => None,
}
}
pub fn iter_non_empty(&self) -> impl Iterator<Item = (char, &RegisterContent)> {
let unnamed_iter = if self.unnamed.is_empty() {
None
} else {
Some(('"', &self.unnamed))
};
unnamed_iter.into_iter().chain(
self.named
.iter()
.filter(|(_, content)| !content.is_empty())
.map(|(name, content)| (*name, content)),
)
}
#[must_use]
pub fn get_register(&self, reg: &Register) -> Option<&RegisterContent> {
match reg {
Register::Default => Some(&self.unnamed),
Register::Slot(c) if c.is_ascii_lowercase() => self.named.get(c),
_ => None,
}
}
pub fn set_register(&mut self, reg: &Register, content: RegisterContent) -> bool {
match reg {
Register::Default => {
self.unnamed = content;
true
}
Register::Slot(c) if c.is_ascii_lowercase() => {
self.named.insert(*c, content);
true
}
_ => false,
}
}
pub fn append_slot(&mut self, slot: char, content: &str) -> bool {
if !slot.is_ascii_lowercase() {
return false;
}
if let Some(existing) = self.named.get_mut(&slot) {
existing.text.push_str(content);
} else {
self.named
.insert(slot, RegisterContent::characterwise(content.to_string()));
}
true
}
pub fn set_by_name(&mut self, name: Option<char>, content: RegisterContent) -> bool {
match name {
None | Some('"') => {
self.unnamed = content;
true
}
Some(c) if c.is_ascii_lowercase() => {
self.named.insert(c, content);
true
}
Some(c) if c.is_ascii_uppercase() => {
let lower = c.to_ascii_lowercase();
if let Some(existing) = self.named.get_mut(&lower) {
existing.text.push_str(&content.text);
} else {
self.named.insert(lower, content);
}
true
}
_ => false,
}
}
}
impl Default for RegisterBank {
fn default() -> Self {
Self::new()
}
}