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, + 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)