From cd0985af971f5798bb3d5f9ab2365fcd310e5b81 Mon Sep 17 00:00:00 2001 From: dgyurov Date: Fri, 18 Aug 2023 11:07:26 +0200 Subject: [PATCH 1/3] Implement change picker --- book/src/keymap.md | 1 + helix-term/src/commands.rs | 76 +++++++++++++++++++++++++++++++- helix-term/src/keymap/default.rs | 1 + 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/book/src/keymap.md b/book/src/keymap.md index 0f41b3247f3d..5cbb4328c608 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -284,6 +284,7 @@ This layer is a kludge of mappings, mostly pickers. | `S` | Open workspace symbol picker (**LSP**) | `workspace_symbol_picker` | | `d` | Open document diagnostics picker (**LSP**) | `diagnostics_picker` | | `D` | Open workspace diagnostics picker (**LSP**) | `workspace_diagnostics_picker` | +| `c` | Open change picker | `change_picker` | | `r` | Rename symbol (**LSP**) | `rename_symbol` | | `a` | Apply code action (**LSP**) | `code_action` | | `h` | Select symbol references (**LSP**) | `select_references_to_symbol_under_cursor` | diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 07aef95d0ac4..5f2a4a804ac9 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -316,6 +316,7 @@ impl MappableCommand { select_references_to_symbol_under_cursor, "Select symbol references", workspace_symbol_picker, "Open workspace symbol picker", diagnostics_picker, "Open diagnostic picker", + change_picker, "Open change picker", workspace_diagnostics_picker, "Open workspace diagnostic picker", last_picker, "Open last picker", insert_at_line_start, "Insert at start of line", @@ -2619,6 +2620,79 @@ fn file_picker_in_current_directory(cx: &mut Context) { cx.push_layer(Box::new(overlaid(picker))); } +fn change_picker(cx: &mut Context) { + struct ChangeMeta { + document_id: DocumentId, + line_number: usize, + text: String, + range: Range, + } + + impl ui::menu::Item for ChangeMeta { + type Data = (); + + fn format(&self, _data: &Self::Data) -> Row { + Row::new([self.line_number.to_string(), self.text.to_owned()]) + } + } + + let doc_id = view!(cx.editor).doc; + let document = cx.editor.documents.get(&doc_id); + + let items = match (document, document.map(|d| d.diff_handle()).flatten()) { + (Some(document), Some(handle)) => { + let diff = handle.load(); + let hunks_count = diff.len(); + let mut items: Vec = vec![]; + + for i in 0..hunks_count { + let current_hunk = diff.nth_hunk(i); + let text = document.text().slice(..); + let range = hunk_range(current_hunk.clone(), text); + let pos = range.cursor(text); + let current_line = text.char_to_line(pos); + + items.push(ChangeMeta { + document_id: document.id(), + line_number: current_line + 1, + text: document.text().line(current_line).to_string(), + range: range.clone(), + }) + } + + items + } + _ => { + cx.editor.set_status("No changes in the current buffer."); + return; + } + }; + + let picker = Picker::new(items, (), |cx, item, action| { + let config = cx.editor.config(); + let (view, doc) = current!(cx.editor); + + doc.set_selection( + view.id, + Selection::single(item.range.anchor, item.range.head), + ); + + if action.align_view(view, doc.id()) { + view.ensure_cursor_in_view_center(doc, config.scrolloff); + } + }) + .with_preview(|editor, meta| { + let doc = &editor.documents.get(&meta.document_id)?; + let text = doc.text().slice(..); + let line_number_start = text.char_to_line(meta.range.from()); + let line_number_end = text.char_to_line(meta.range.to()) - 1; + + Some((doc.id().into(), Some((line_number_start, line_number_end)))) + }); + + cx.push_layer(Box::new(overlaid(picker))); +} + fn buffer_picker(cx: &mut Context) { let current = view!(cx.editor).doc; @@ -2859,7 +2933,7 @@ pub fn command_palette(cx: &mut Context) { } fn last_picker(cx: &mut Context) { - // TODO: last picker does not seem to work well with buffer_picker + // TODO: last picker does not seem to work well with buffer_picker and change_picker cx.callback = Some(Box::new(|compositor, cx| { if let Some(picker) = compositor.last_picker.take() { compositor.push(picker); diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 763ed4ae71ce..871b8b987fe5 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -222,6 +222,7 @@ pub fn default() -> HashMap { "S" => workspace_symbol_picker, "d" => diagnostics_picker, "D" => workspace_diagnostics_picker, + "c" => change_picker, "a" => code_action, "'" => last_picker, "g" => { "Debug (experimental)" sticky=true From 3e53f81d1049f1e03fd09f0810b9e1c1ce50c9cb Mon Sep 17 00:00:00 2001 From: dgyurov Date: Fri, 18 Aug 2023 11:49:55 +0200 Subject: [PATCH 2/3] Improve error handling --- helix-term/src/commands.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 5f2a4a804ac9..46a5b28f364b 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2662,12 +2662,14 @@ fn change_picker(cx: &mut Context) { items } - _ => { - cx.editor.set_status("No changes in the current buffer."); - return; - } + _ => vec![], }; + if items.is_empty() { + cx.editor.set_status("No changes in the current buffer."); + return; + } + let picker = Picker::new(items, (), |cx, item, action| { let config = cx.editor.config(); let (view, doc) = current!(cx.editor); From bf670003a8959f4b992da3d45095fb528fcad853 Mon Sep 17 00:00:00 2001 From: dgyurov Date: Fri, 18 Aug 2023 13:29:21 +0200 Subject: [PATCH 3/3] Fix lint warnings --- helix-term/src/commands.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 46a5b28f364b..b3fcfc9e2971 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2639,7 +2639,7 @@ fn change_picker(cx: &mut Context) { let doc_id = view!(cx.editor).doc; let document = cx.editor.documents.get(&doc_id); - let items = match (document, document.map(|d| d.diff_handle()).flatten()) { + let items = match (document, document.and_then(|d| d.diff_handle())) { (Some(document), Some(handle)) => { let diff = handle.load(); let hunks_count = diff.len(); @@ -2648,15 +2648,15 @@ fn change_picker(cx: &mut Context) { for i in 0..hunks_count { let current_hunk = diff.nth_hunk(i); let text = document.text().slice(..); - let range = hunk_range(current_hunk.clone(), text); - let pos = range.cursor(text); - let current_line = text.char_to_line(pos); + let current_range = hunk_range(current_hunk.clone(), text); + let position = current_range.cursor(text); + let current_line = text.char_to_line(position); items.push(ChangeMeta { document_id: document.id(), line_number: current_line + 1, text: document.text().line(current_line).to_string(), - range: range.clone(), + range: current_range, }) }