diff options
Diffstat (limited to 'helix-term/src/commands.rs')
-rw-r--r-- | helix-term/src/commands.rs | 145 |
1 files changed, 83 insertions, 62 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 382137d1..c80716d4 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4,8 +4,9 @@ use helix_core::{ movement::{self, Direction}, object, pos_at_coords, regex::{self, Regex}, - register, search, selection, Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection, - SmallVec, Tendril, Transaction, + register::{self, Register, Registers}, + search, selection, Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection, SmallVec, + Tendril, Transaction, }; use helix_view::{ @@ -39,7 +40,7 @@ use crossterm::event::{KeyCode, KeyEvent}; use once_cell::sync::Lazy; pub struct Context<'a> { - pub register: helix_view::RegisterSelection, + pub selected_register: helix_view::RegisterSelection, pub count: Option<std::num::NonZeroUsize>, pub editor: &'a mut Editor, @@ -65,6 +66,11 @@ impl<'a> Context<'a> { self.editor.current() } + #[inline] + pub fn current_with_registers(&mut self) -> (&mut View, &mut Document, &mut Registers) { + self.editor.current_with_registers() + } + /// Push a new component onto the compositor. pub fn push_layer(&mut self, mut component: Box<dyn Component>) { self.callback = Some(Box::new(|compositor: &mut Compositor| { @@ -631,7 +637,7 @@ pub fn select_all(cx: &mut Context) { } pub fn select_regex(cx: &mut Context) { - let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, regex| { + let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, _, regex| { let text = doc.text().slice(..); if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), ®ex) { @@ -643,7 +649,7 @@ pub fn select_regex(cx: &mut Context) { } pub fn split_selection(cx: &mut Context) { - let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, regex| { + let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, _, regex| { let text = doc.text().slice(..); let selection = selection::split_on_matches(text, doc.selection(view.id), ®ex); doc.set_selection(view.id, selection); @@ -714,20 +720,24 @@ pub fn search(cx: &mut Context) { let contents = doc.text().slice(..).to_string(); let view_id = view.id; - let prompt = ui::regex_prompt(cx, "search:".to_string(), move |view, doc, regex| { - search_impl(doc, view, &contents, ®ex, false); - // TODO: only store on enter (accept), not update - register::set('\\', vec![regex.as_str().to_string()]); - }); + let prompt = ui::regex_prompt( + cx, + "search:".to_string(), + move |view, doc, registers, regex| { + search_impl(doc, view, &contents, ®ex, false); + // TODO: only store on enter (accept), not update + registers.write('\\', vec![regex.as_str().to_string()]); + }, + ); cx.push_layer(Box::new(prompt)); } // can't search next for ""compose"" for some reason pub fn search_next_impl(cx: &mut Context, extend: bool) { - if let Some(query) = register::get('\\') { + let (view, doc, registers) = cx.current_with_registers(); + if let Some(query) = registers.read('\\') { let query = query.first().unwrap(); - let (view, doc) = cx.current(); let contents = doc.text().slice(..).to_string(); let regex = Regex::new(query).unwrap(); search_impl(doc, view, &contents, ®ex, extend); @@ -747,7 +757,7 @@ pub fn search_selection(cx: &mut Context) { let contents = doc.text().slice(..); let query = doc.selection(view.id).primary().fragment(contents); let regex = regex::escape(&query); - register::set('\\', vec![regex]); + cx.editor.registers.write('\\', vec![regex]); search_next(cx); } @@ -794,7 +804,7 @@ pub fn extend_line(cx: &mut Context) { // heuristic: append changes to history after each command, unless we're in insert mode -fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) { +fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId) { // first yank the selection let values: Vec<String> = doc .selection(view_id) @@ -802,7 +812,7 @@ fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) { .map(Cow::into_owned) .collect(); - register::set(reg, values); + reg.write(values); // then delete let transaction = @@ -815,8 +825,9 @@ fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) { } pub fn delete_selection(cx: &mut Context) { - let reg = cx.register.name(); - let (view, doc) = cx.current(); + let reg_name = cx.selected_register.name(); + let (view, doc, registers) = cx.current_with_registers(); + let reg = registers.get_or_insert(reg_name); delete_selection_impl(reg, doc, view.id); doc.append_changes_to_history(view.id); @@ -826,8 +837,9 @@ pub fn delete_selection(cx: &mut Context) { } pub fn change_selection(cx: &mut Context) { - let reg = cx.register.name(); - let (view, doc) = cx.current(); + let reg_name = cx.selected_register.name(); + let (view, doc, registers) = cx.current_with_registers(); + let reg = registers.get_or_insert(reg_name); delete_selection_impl(reg, doc, view.id); enter_insert_mode(doc); } @@ -2227,10 +2239,12 @@ pub fn yank(cx: &mut Context) { let msg = format!( "yanked {} selection(s) to register {}", values.len(), - cx.register.name() + cx.selected_register.name() ); - register::set(cx.register.name(), values); + cx.editor + .registers + .write(cx.selected_register.name(), values); cx.editor.set_status(msg) } @@ -2241,47 +2255,48 @@ enum Paste { After, } -fn paste_impl(reg: char, doc: &mut Document, view: &View, action: Paste) -> Option<Transaction> { - if let Some(values) = register::get(reg) { - let repeat = std::iter::repeat( - values - .last() - .map(|value| Tendril::from_slice(value)) - .unwrap(), - ); +fn paste_impl( + values: &[String], + doc: &mut Document, + view: &View, + action: Paste, +) -> Option<Transaction> { + let repeat = std::iter::repeat( + values + .last() + .map(|value| Tendril::from_slice(value)) + .unwrap(), + ); - // if any of values ends \n it's linewise paste - let linewise = values.iter().any(|value| value.ends_with('\n')); + // if any of values ends \n it's linewise paste + let linewise = values.iter().any(|value| value.ends_with('\n')); - let mut values = values.into_iter().map(Tendril::from).chain(repeat); + let mut values = values.iter().cloned().map(Tendril::from).chain(repeat); - let text = doc.text(); + let text = doc.text(); - let transaction = - Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { - let pos = match (action, linewise) { - // paste linewise before - (Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())), - // paste linewise after - (Paste::After, true) => text.line_to_char(text.char_to_line(range.to()) + 1), - // paste insert - (Paste::Before, false) => range.from(), - // paste append - (Paste::After, false) => range.to() + 1, - }; - (pos, pos, Some(values.next().unwrap())) - }); - return Some(transaction); - } - None + let transaction = Transaction::change_by_selection(text, doc.selection(view.id), |range| { + let pos = match (action, linewise) { + // paste linewise before + (Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())), + // paste linewise after + (Paste::After, true) => text.line_to_char(text.char_to_line(range.to()) + 1), + // paste insert + (Paste::Before, false) => range.from(), + // paste append + (Paste::After, false) => range.to() + 1, + }; + (pos, pos, Some(values.next().unwrap())) + }); + + Some(transaction) } pub fn replace_with_yanked(cx: &mut Context) { - let reg = cx.register.name(); - - if let Some(values) = register::get(reg) { - let (view, doc) = cx.current(); + let reg_name = cx.selected_register.name(); + let (view, doc, registers) = cx.current_with_registers(); + if let Some(values) = registers.read(reg_name) { if let Some(yank) = values.first() { let transaction = Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { @@ -2307,20 +2322,26 @@ pub fn replace_with_yanked(cx: &mut Context) { // default insert pub fn paste_after(cx: &mut Context) { - let reg = cx.register.name(); - let (view, doc) = cx.current(); + let reg_name = cx.selected_register.name(); + let (view, doc, registers) = cx.current_with_registers(); - if let Some(transaction) = paste_impl(reg, doc, view, Paste::After) { + if let Some(transaction) = registers + .read(reg_name) + .and_then(|values| paste_impl(values, doc, view, Paste::After)) + { doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); } } pub fn paste_before(cx: &mut Context) { - let reg = cx.register.name(); - let (view, doc) = cx.current(); + let reg_name = cx.selected_register.name(); + let (view, doc, registers) = cx.current_with_registers(); - if let Some(transaction) = paste_impl(reg, doc, view, Paste::Before) { + if let Some(transaction) = registers + .read(reg_name) + .and_then(|values| paste_impl(values, doc, view, Paste::Before)) + { doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); } @@ -2494,7 +2515,7 @@ pub fn join_selections(cx: &mut Context) { pub fn keep_selections(cx: &mut Context) { // keep selections matching regex - let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, regex| { + let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, _, regex| { let text = doc.text().slice(..); if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), ®ex) { @@ -2788,7 +2809,7 @@ pub fn select_register(cx: &mut Context) { .. } = event { - cx.editor.register.select(ch); + cx.editor.selected_register.select(ch); } }) } |