From 8657c57cf2d56b9a89e2f780675426f4c78df48b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Wed, 24 Mar 2021 18:01:26 +0900
Subject: [PATCH] Trivial jumplist implementation.

---
 helix-term/src/commands.rs | 22 ++++++++++++++++++++
 helix-term/src/keymap.rs   |  3 +++
 helix-view/src/editor.rs   |  4 ++++
 helix-view/src/view.rs     | 42 +++++++++++++++++++++++++++++++++++++-
 4 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 7a1643fe..24fb1709 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1642,3 +1642,25 @@ pub fn match_brackets(cx: &mut Context) {
         };
     }
 }
+
+//
+
+pub fn jump_forward(cx: &mut Context) {
+    let count = cx.count;
+    let view = cx.view();
+
+    if let Some((id, selection)) = view.jumps.forward(count) {
+        view.first_line = 0;
+        view.doc = *id;
+    };
+}
+
+pub fn jump_backward(cx: &mut Context) {
+    let count = cx.count;
+    let view = cx.view();
+
+    if let Some((id, selection)) = view.jumps.backward(count) {
+        view.first_line = 0;
+        view.doc = *id;
+    };
+}
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index bd1b31ef..824d469d 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -253,6 +253,9 @@ pub fn default() -> Keymaps {
         shift!('K') => commands::hover,
 
         // z family for save/restore/combine from/to sels from register
+
+        ctrl!('i') => commands::jump_forward, // TODO: ctrl-i conflicts tab
+        ctrl!('o') => commands::jump_backward,
     );
     // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether
     // we keep this separate select mode. More keys can fit into normal mode then, but it's weird
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index 33e380a0..08dd4d00 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -90,7 +90,11 @@ impl Editor {
         use crate::tree::Layout;
         match action {
             Action::Replace => {
+                let view = self.view();
+                let jump = (view.doc, self.documents[view.doc].selection().clone());
+
                 let view = self.view_mut();
+                view.jumps.push(jump);
                 view.doc = id;
                 view.first_line = 0;
                 return Ok(id);
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index 5f6a0456..7a239d4a 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -5,17 +5,56 @@ use std::borrow::Cow;
 use crate::{Document, DocumentId, ViewId};
 use helix_core::{
     graphemes::{grapheme_width, RopeGraphemes},
-    Position, RopeSlice,
+    Position, RopeSlice, Selection,
 };
 use tui::layout::Rect;
 
 pub const PADDING: usize = 5;
 
+type Jump = (DocumentId, Selection);
+
+pub struct JumpList {
+    jumps: Vec<Jump>,
+    current: usize,
+}
+
+impl JumpList {
+    pub fn new(initial: Jump) -> Self {
+        Self {
+            jumps: vec![initial],
+            current: 0,
+        }
+    }
+
+    pub fn push(&mut self, jump: Jump) {
+        self.jumps.truncate(self.current + 1);
+        self.jumps.push(jump);
+        self.current += 1;
+    }
+
+    pub fn forward(&mut self, count: usize) -> Option<&Jump> {
+        if self.current + count < self.jumps.len() {
+            self.current += count;
+            return self.jumps.get(self.current);
+        }
+        None
+    }
+
+    pub fn backward(&mut self, count: usize) -> Option<&Jump> {
+        if self.current.checked_sub(count).is_some() {
+            self.current -= count;
+            return self.jumps.get(self.current);
+        }
+        None
+    }
+}
+
 pub struct View {
     pub id: ViewId,
     pub doc: DocumentId,
     pub first_line: usize,
     pub area: Rect,
+    pub jumps: JumpList,
 }
 
 impl View {
@@ -25,6 +64,7 @@ impl View {
             doc,
             first_line: 0,
             area: Rect::default(), // will get calculated upon inserting into tree
+            jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
         };
 
         Ok(view)