From 742b3a709fd7680d77bcda5ae08f2a9539b6a20e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Mon, 29 Mar 2021 16:32:42 +0900
Subject: [PATCH] Store intra-files jumps (goto) on the jumplist.

---
 helix-core/src/selection.rs |  2 +-
 helix-term/src/commands.rs  | 16 ++++++++++++++++
 helix-term/src/keymap.rs    |  1 +
 helix-term/src/ui/mod.rs    |  2 +-
 helix-view/src/document.rs  |  5 +++++
 helix-view/src/view.rs      |  9 ++++++---
 6 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs
index e76e12b4..c8109297 100644
--- a/helix-core/src/selection.rs
+++ b/helix-core/src/selection.rs
@@ -127,7 +127,7 @@ impl Range {
 
 /// A selection consists of one or more selection ranges.
 /// invariant: A selection can never be empty (always contains at least primary range).
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Selection {
     ranges: SmallVec<[Range; 1]>,
     primary_index: usize,
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index ddee8b93..d2aa481a 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -238,11 +238,13 @@ pub fn move_next_word_end(cx: &mut Context) {
 }
 
 pub fn move_file_start(cx: &mut Context) {
+    push_jump(cx);
     let doc = cx.doc();
     doc.set_selection(Selection::point(0));
 }
 
 pub fn move_file_end(cx: &mut Context) {
+    push_jump(cx);
     let doc = cx.doc();
     let text = doc.text();
     let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
@@ -1027,9 +1029,21 @@ pub fn normal_mode(cx: &mut Context) {
     }
 }
 
+// Store a jump on the jumplist.
+fn push_jump(cx: &mut Context) {
+    let jump = {
+        let doc = cx.doc();
+        (doc.id(), doc.selection().clone())
+    };
+    cx.view().jumps.push(jump);
+}
+
 pub fn goto_mode(cx: &mut Context) {
     let count = cx.count;
+
     if count > 1 {
+        push_jump(cx);
+
         // TODO: can't go to line 1 since we can't distinguish between g and 1g, g gets converted
         // to 1g
         let doc = cx.doc();
@@ -1069,6 +1083,8 @@ pub fn exit_select_mode(cx: &mut Context) {
 fn _goto(cx: &mut Context, locations: Vec<lsp::Location>) {
     use helix_view::editor::Action;
 
+    push_jump(cx);
+
     fn jump_to(editor: &mut Editor, location: &lsp::Location, action: Action) {
         let id = editor
             .open(PathBuf::from(location.uri.path()), action)
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index f401f0f9..d7a72377 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -258,6 +258,7 @@ pub fn default() -> Keymaps {
 
         ctrl!('i') => commands::jump_forward, // TODO: ctrl-i conflicts tab
         ctrl!('o') => commands::jump_backward,
+        // ctrl!('s') => commands::save_selection,
     );
     // 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-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 8cb82fcb..0e38e598 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -49,7 +49,7 @@ pub fn regex_prompt(
                     doc.set_selection(snapshot.clone());
                 }
                 PromptEvent::Validate => {
-                    //
+                    // TODO: push_jump to store selection just before jump
                 }
                 PromptEvent::Update => {
                     // skip empty input, TODO: trigger default
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index abc829c0..6e73104a 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -274,6 +274,11 @@ impl Document {
         self.history.commit_revision(&transaction, &old_state);
     }
 
+    #[inline]
+    pub fn id(&self) -> DocumentId {
+        self.id
+    }
+
     #[inline]
     pub fn mode(&self) -> Mode {
         self.mode
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index 7a239d4a..9e4c1373 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -27,9 +27,12 @@ impl JumpList {
     }
 
     pub fn push(&mut self, jump: Jump) {
-        self.jumps.truncate(self.current + 1);
-        self.jumps.push(jump);
-        self.current += 1;
+        self.jumps.truncate(self.current);
+        // don't push duplicates
+        if self.jumps.last() != Some(&jump) {
+            self.jumps.push(jump);
+            self.current = self.jumps.len();
+        }
     }
 
     pub fn forward(&mut self, count: usize) -> Option<&Jump> {