aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/commands.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/commands.rs')
-rw-r--r--helix-term/src/commands.rs145
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), &regex)
{
@@ -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), &regex);
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, &regex, 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, &regex, 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, &regex, 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), &regex) {
@@ -2788,7 +2809,7 @@ pub fn select_register(cx: &mut Context) {
..
} = event
{
- cx.editor.register.select(ch);
+ cx.editor.selected_register.select(ch);
}
})
}