diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 219f6b95f..7c81105b5 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -3,6 +3,9 @@ | `:quit`, `:q` | Close the current view. | | `:quit!`, `:q!` | Force close the current view, ignoring unsaved changes. | | `:open`, `:o`, `:edit`, `:e` | Open a file from disk into the current view. | +| `:jump-to-buffer`, `:j` | Switch to a document buffer using its index in the buffer jumplist. | +| `:add-buffer-to-jumplist`, `:ab` | Add the current document buffer to the buffer jumplist. | +| `:remove-buffer-from-jumplist`, `:rb` | Remove the current document buffer from the buffer jumplist. | | `:buffer-close`, `:bc`, `:bclose` | Close the current buffer. | | `:buffer-close!`, `:bc!`, `:bclose!` | Close the current buffer forcefully, ignoring unsaved changes. | | `:buffer-close-others`, `:bco`, `:bcloseother` | Close all buffers but the currently focused one. | diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 2cbdeb451..4aa0321c1 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3145,6 +3145,7 @@ fn buffer_picker(cx: &mut Context) { is_modified: bool, is_current: bool, focused_at: std::time::Instant, + index_in_buffer_jumplist: Option, } let new_meta = |doc: &Document| BufferMeta { @@ -3153,6 +3154,11 @@ fn buffer_picker(cx: &mut Context) { is_modified: doc.is_modified(), is_current: doc.id() == current, focused_at: doc.focused_at, + index_in_buffer_jumplist: cx + .editor + .buffer_jumplist + .iter() + .position(|id| id == &doc.id()), }; let mut items = cx @@ -3175,6 +3181,10 @@ fn buffer_picker(cx: &mut Context) { if meta.is_current { flags.push('*'); } + if let Some(index) = meta.index_in_buffer_jumplist { + flags.push_str(&index.to_string()); + } + flags.into() }), PickerColumn::new("path", |meta: &BufferMeta, _| { diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 2013a9d81..daea1d463 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -99,6 +99,51 @@ fn force_quit(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> Ok(()) } +fn buffer_jump(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + let pos: usize = args.first().unwrap_or("0").parse().unwrap_or(0); + if let Some(doc_id) = cx.editor.buffer_jumplist.get(pos) { + cx.editor.switch(*doc_id, Action::Replace); + } + + return Ok(()); +} + +fn add_buffer_to_jumplist( + cx: &mut compositor::Context, + _args: Args, + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + let current = view!(cx.editor).doc; + cx.editor.buffer_jumplist.push(current); + + return Ok(()); +} + +fn remove_buffer_from_jumplist( + cx: &mut compositor::Context, + _args: Args, + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + let current = view!(cx.editor).doc; + cx.editor + .buffer_jumplist + .retain(|doc_id| *doc_id != current); + + return Ok(()); +} + fn open(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> { if event != PromptEvent::Validate { return Ok(()); @@ -2615,6 +2660,39 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ ..Signature::DEFAULT }, }, + TypableCommand { + name: "jump-to-buffer", + aliases: &["j"], + doc: "Switch to a document buffer using its index in the buffer jumplist.", + fun: buffer_jump, + completer: CommandCompleter::all(completers::filename), + signature: Signature { + positionals: (1, None), + ..Signature::DEFAULT + }, + }, + TypableCommand { + name: "add-buffer-to-jumplist", + aliases: &["ab"], + doc: "Add the current document buffer to the buffer jumplist.", + fun: add_buffer_to_jumplist, + completer: CommandCompleter::all(completers::filename), + signature: Signature { + positionals: (0, None), + ..Signature::DEFAULT + }, + }, + TypableCommand { + name: "remove-buffer-from-jumplist", + aliases: &["rb"], + doc: "Remove the current document buffer from the buffer jumplist.", + fun: remove_buffer_from_jumplist, + completer: CommandCompleter::all(completers::filename), + signature: Signature { + positionals: (0, None), + ..Signature::DEFAULT + }, + }, TypableCommand { name: "buffer-close", aliases: &["bc", "bclose"], diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 89f053741..8a8ae4ddf 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1133,6 +1133,9 @@ pub struct Editor { pub mouse_down_range: Option, pub cursor_cache: CursorCache, + + /// Stores a manually curated list of document IDs for navigation. + pub buffer_jumplist: Vec, } pub type Motion = Box; @@ -1255,6 +1258,7 @@ impl Editor { handlers, mouse_down_range: None, cursor_cache: CursorCache::default(), + buffer_jumplist: Vec::new(), } } @@ -1843,6 +1847,9 @@ impl Editor { // This will also disallow any follow-up writes self.saves.remove(&doc_id); + // Removes the document from the buffer jumplist when closed. + self.buffer_jumplist.retain(|doc| *doc != doc_id); + enum Action { Close(ViewId), ReplaceDoc(ViewId, DocumentId),