diff --git a/book/src/keymap.md b/book/src/keymap.md index abb6f3022..9acbd3b6b 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -238,6 +238,7 @@ #### Space mode | ----- | ----------- | ------- | | `f` | Open file picker | `file_picker` | | `b` | Open buffer picker | `buffer_picker` | +| `j` | Open jumplist picker | `jumplist_picker` | | `k` | Show documentation for item under cursor in a [popup](#popup) (**LSP**) | `hover` | | `s` | Open document symbol picker (**LSP**) | `symbol_picker` | | `S` | Open workspace symbol picker (**LSP**) | `workspace_symbol_picker` | diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 3ee75f6a3..12d988d09 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -264,6 +264,7 @@ pub fn doc(&self) -> &str { file_picker_in_current_directory, "Open file picker at current working directory", code_action, "Perform code action", buffer_picker, "Open buffer picker", + jumplist_picker, "Open jumplist picker", symbol_picker, "Open symbol picker", select_references_to_symbol_under_cursor, "Select symbol references", workspace_symbol_picker, "Open workspace symbol picker", @@ -2270,6 +2271,87 @@ fn label(&self, _data: &Self::Data) -> Spans { cx.push_layer(Box::new(overlayed(picker))); } +fn jumplist_picker(cx: &mut Context) { + struct JumpMeta { + id: DocumentId, + path: Option, + selection: Selection, + text: String, + is_current: bool, + } + + impl ui::menu::Item for JumpMeta { + type Data = (); + + fn label(&self, _data: &Self::Data) -> Spans { + let path = self + .path + .as_deref() + .map(helix_core::path::get_relative_path); + let path = match path.as_deref().and_then(Path::to_str) { + Some(path) => path, + None => SCRATCH_BUFFER_NAME, + }; + + let mut flags = Vec::new(); + if self.is_current { + flags.push("*"); + } + + let flag = if flags.is_empty() { + "".into() + } else { + format!(" ({})", flags.join("")) + }; + format!("{} {}{} {}", self.id, path, flag, self.text).into() + } + } + + let new_meta = |view: &View, doc_id: DocumentId, selection: Selection| { + let doc = &cx.editor.documents.get(&doc_id); + let text = doc.map_or("".into(), |d| { + selection + .fragments(d.text().slice(..)) + .map(Cow::into_owned) + .collect::>() + .join(" ") + }); + + JumpMeta { + id: doc_id, + path: doc.and_then(|d| d.path().cloned()), + selection, + text, + is_current: view.doc == doc_id, + } + }; + + let picker = FilePicker::new( + cx.editor + .tree + .views() + .flat_map(|(view, _)| { + view.jumps + .get() + .iter() + .map(|(doc_id, selection)| new_meta(view, *doc_id, selection.clone())) + }) + .collect(), + (), + |cx, meta, action| { + cx.editor.switch(meta.id, action); + let (view, doc) = current!(cx.editor); + doc.set_selection(view.id, meta.selection.clone()); + }, + |editor, meta| { + let doc = &editor.documents.get(&meta.id)?; + let line = meta.selection.primary().cursor_line(doc.text().slice(..)); + Some((meta.path.clone()?, Some((line, line)))) + }, + ); + cx.push_layer(Box::new(overlayed(picker))); +} + impl ui::menu::Item for MappableCommand { type Data = ReverseKeymap; diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index b8c1a4007..f6fb6140b 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -205,6 +205,7 @@ pub fn default() -> HashMap { "f" => file_picker, "F" => file_picker_in_current_directory, "b" => buffer_picker, + "j" => jumplist_picker, "s" => symbol_picker, "S" => workspace_symbol_picker, "g" => diagnostics_picker, diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index 8bf3611f6..e74e0f651 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -58,6 +58,10 @@ pub fn backward(&mut self, view_id: ViewId, doc: &mut Document, count: usize) -> pub fn remove(&mut self, doc_id: &DocumentId) { self.jumps.retain(|(other_id, _)| other_id != doc_id); } + + pub fn get(&self) -> &[Jump] { + &self.jumps + } } pub struct View {