repo_description: The user-friendly command line shell.
repo_topics: fish rust shell terminal
code: //! The killring.
  //!
  //! Works like the killring in emacs and readline. The killring is cut and paste with a memory of
  //! previous cuts.
  
  use std::collections::VecDeque;
  use std::sync::Mutex;
  
  use crate::prelude::*;
  
  struct KillRing(VecDeque<WString>);
  
  static KILL_RING: Mutex<KillRing> = Mutex::new(KillRing::new());
  
  impl KillRing {
      /// Create a new killring.
      const fn new() -> Self {
          Self(VecDeque::new())
      }
  
      /// Return whether this killring is empty.
      #[cfg(test)]
      fn is_empty(&self) -> bool {
          self.0.is_empty()
      }
  
      /// Add a string to the top of the killring.
      fn add(&mut self, new_entry: WString) {
          if !new_entry.is_empty() {
              self.0.push_front(new_entry);
          }
      }
  
      /// Replace the specified string in the killring.
      pub fn replace(&mut self, old_entry: &wstr, new_entry: WString) {
          if let Some(old_entry_idx) = self.0.iter().position(|entry| entry == old_entry) {
              self.0.remove(old_entry_idx);
          }
          if !new_entry.is_empty() {
              self.add(new_entry);
          }
      }
  
      /// Paste from the killring.
      pub fn yank(&mut self) -> WString {
          self.0.front().cloned().unwrap_or_default()
      }
  
      /// Rotate the killring.
      pub fn yank_rotate(&mut self) -> WString {
          self.0.rotate_left(1);
          self.yank()
      }
  
      /// Return a copy of the list of entries.
      pub fn entries(&self) -> Vec<WString> {
          self.0.iter().cloned().collect()
      }
  }
  
  /// Add a string to the top of the killring.
  pub fn kill_add(new_entry: WString) {
      KILL_RING.lock().unwrap().add(new_entry);
  }
  
  /// Replace the specified string in the killring.
  pub fn kill_replace(old_entry: &wstr, new_entry: WString) {
      KILL_RING.lock().unwrap().replace(old_entry, new_entry);
  }
  
  /// Rotate the killring.
  pub fn kill_yank_rotate() -> WString {
      KILL_RING.lock().unwrap().yank_rotate()
  }
  
  /// Paste from the killring.
  pub fn kill_yank() -> WString {
      KILL_RING.lock().unwrap().yank()
  }
  
  pub fn kill_entries() -> Vec<WString> {
      KILL_RING.lock().unwrap().entries()
  }
  
  #[cfg(test)]
  mod tests {
      use super::KillRing;
      use crate::prelude::*;
  
      #[test]
      fn test_killring() {
          let mut kr = KillRing::new();
  
          assert!(kr.is_empty());
  
          kr.add(L!("a").to_owned());
          kr.add(L!("b").to_owned());
          kr.add(L!("c").to_owned());
  
          assert_eq!(kr.entries(), [L!("c"), L!("b"), L!("a")]);
  
          assert_eq!(kr.yank_rotate(), "b");
          assert_eq!(kr.entries(), [L!("b"), L!("a"), L!("c")]);
  
          assert_eq!(kr.yank_rotate(), "a");
          assert_eq!(kr.entries(), [L!("a"), L!("c"), L!("b")]);
  
          kr.add(L!("d").to_owned());
  
          assert_eq!(kr.entries(), [L!("d"), L!("a"), L!("c"), L!("b")]);
  
          assert_eq!(kr.yank_rotate(), "a");
          assert_eq!(kr.entries(), [L!("a"), L!("c"), L!("b"), L!("d")]);
      }
  }
  