From 1dba0f2b1ccc0c6a29e05876b7b7153373221f87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Tue, 6 Oct 2020 16:00:23 +0900
Subject: [PATCH] Simple yank/paste registers.

---
 helix-core/src/lib.rs         |  1 +
 helix-core/src/register.rs    | 21 +++++++++++++++++++++
 helix-core/src/selection.rs   |  3 ++-
 helix-core/src/transaction.rs |  2 +-
 helix-view/src/commands.rs    | 35 ++++++++++++++++++++++++++++++++++-
 helix-view/src/keymap.rs      |  2 ++
 6 files changed, 61 insertions(+), 3 deletions(-)
 create mode 100644 helix-core/src/register.rs

diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs
index 9bc5d003..4a7a2dd4 100644
--- a/helix-core/src/lib.rs
+++ b/helix-core/src/lib.rs
@@ -3,6 +3,7 @@ pub mod graphemes;
 mod history;
 pub mod macros;
 mod position;
+pub mod register;
 pub mod selection;
 pub mod state;
 pub mod syntax;
diff --git a/helix-core/src/register.rs b/helix-core/src/register.rs
new file mode 100644
index 00000000..0be0ce89
--- /dev/null
+++ b/helix-core/src/register.rs
@@ -0,0 +1,21 @@
+use crate::Tendril;
+use once_cell::sync::Lazy;
+use std::{collections::HashMap, sync::RwLock};
+
+// TODO: could be an instance on Editor
+static REGISTRY: Lazy<RwLock<HashMap<char, Vec<String>>>> =
+    Lazy::new(|| RwLock::new(HashMap::new()));
+
+pub fn get(register: char) -> Option<Vec<String>> {
+    let registry = REGISTRY.read().unwrap();
+
+    // TODO: no cloning
+    registry.get(&register).cloned()
+}
+
+// restoring: bool
+pub fn set(register: char, values: Vec<String>) {
+    let mut registry = REGISTRY.write().unwrap();
+
+    registry.insert(register, values);
+}
diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs
index 2251c77f..bc677330 100644
--- a/helix-core/src/selection.rs
+++ b/helix-core/src/selection.rs
@@ -110,7 +110,8 @@ impl Range {
 
     #[inline]
     pub fn fragment<'a>(&'a self, text: &'a RopeSlice) -> Cow<'a, str> {
-        Cow::from(text.slice(self.from()..self.to()))
+        // end inclusive
+        Cow::from(text.slice(self.from()..self.to() + 1))
     }
 }
 
diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs
index 13c0c50f..33612ecf 100644
--- a/helix-core/src/transaction.rs
+++ b/helix-core/src/transaction.rs
@@ -445,7 +445,7 @@ impl Transaction {
     /// Generate a transaction with a change per selection range.
     pub fn change_by_selection<F>(state: &State, f: F) -> Self
     where
-        F: Fn(&Range) -> Change,
+        F: FnMut(&Range) -> Change,
     {
         Self::change(state, state.selection.ranges().iter().map(f))
     }
diff --git a/helix-view/src/commands.rs b/helix-view/src/commands.rs
index 9a6d2e5d..ca1e41c4 100644
--- a/helix-view/src/commands.rs
+++ b/helix-view/src/commands.rs
@@ -1,7 +1,7 @@
 use helix_core::{
     graphemes,
     regex::Regex,
-    selection,
+    register, selection,
     state::{Direction, Granularity, Mode, State},
     ChangeSet, Range, Selection, Tendril, Transaction,
 };
@@ -443,3 +443,36 @@ pub fn undo(view: &mut View, _count: usize) {
 pub fn redo(view: &mut View, _count: usize) {
     view.history.redo(&mut view.state);
 }
+
+// Yank / Paste
+
+pub fn yank(view: &mut View, _count: usize) {
+    // TODO: should selections be made end inclusive?
+    let values = view
+        .state
+        .selection()
+        .fragments(&view.state.doc().slice(..))
+        .map(|cow| cow.into_owned())
+        .collect();
+
+    register::set('"', values);
+}
+
+pub fn paste(view: &mut View, _count: usize) {
+    if let Some(values) = register::get('"') {
+        let repeat = std::iter::repeat(
+            values
+                .last()
+                .map(|value| Tendril::from_slice(value))
+                .unwrap(),
+        );
+
+        let mut values = values.into_iter().map(Tendril::from).chain(repeat);
+
+        let transaction = Transaction::change_by_selection(&view.state, |range| {
+            (range.head + 1, range.head + 1, Some(values.next().unwrap()))
+        });
+
+        transaction.apply(&mut view.state);
+    }
+}
diff --git a/helix-view/src/keymap.rs b/helix-view/src/keymap.rs
index e108324e..da5934eb 100644
--- a/helix-view/src/keymap.rs
+++ b/helix-view/src/keymap.rs
@@ -147,6 +147,8 @@ pub fn default() -> Keymaps {
                 vec![key!(';')] => commands::collapse_selection,
                 vec![key!('u')] => commands::undo,
                 vec![shift!('U')] => commands::redo,
+                vec![key!('y')] => commands::yank,
+                vec![key!('p')] => commands::paste,
                 vec![Key {
                     code: KeyCode::Esc,
                     modifiers: Modifiers::NONE