aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src')
-rw-r--r--helix-term/src/commands.rs148
-rw-r--r--helix-term/src/commands/typed.rs13
-rw-r--r--helix-term/src/ui/prompt.rs56
3 files changed, 113 insertions, 104 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 58c17296..d9ea580d 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1847,11 +1847,11 @@ fn search_impl(
fn search_completions(cx: &mut Context, reg: Option<char>) -> Vec<String> {
let mut items = reg
- .and_then(|reg| cx.editor.registers.get(reg))
- .map_or(Vec::new(), |reg| reg.read().iter().take(200).collect());
+ .and_then(|reg| cx.editor.registers.read(reg, cx.editor))
+ .map_or(Vec::new(), |reg| reg.take(200).collect());
items.sort_unstable();
items.dedup();
- items.into_iter().cloned().collect()
+ items.into_iter().map(|value| value.to_string()).collect()
}
fn search(cx: &mut Context) {
@@ -1910,9 +1910,8 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
let count = cx.count();
let config = cx.editor.config();
let scrolloff = config.scrolloff;
- let (_, doc) = current!(cx.editor);
- let registers = &cx.editor.registers;
- if let Some(query) = registers.read('/').and_then(|query| query.last()) {
+ if let Some(query) = cx.editor.registers.last('/', cx.editor) {
+ let doc = doc!(cx.editor);
let contents = doc.text().slice(..).to_string();
let search_config = &config.search;
let case_insensitive = if search_config.smart_case {
@@ -1921,7 +1920,7 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
false
};
let wrap_around = search_config.wrap_around;
- if let Ok(regex) = RegexBuilder::new(query)
+ if let Ok(regex) = RegexBuilder::new(&query)
.case_insensitive(case_insensitive)
.multi_line(true)
.build()
@@ -1974,12 +1973,14 @@ fn search_selection(cx: &mut Context) {
.join("|");
let msg = format!("register '{}' set to '{}'", '/', &regex);
- cx.editor.registers.push('/', regex);
- cx.editor.set_status(msg);
+ match cx.editor.registers.push('/', regex) {
+ Ok(_) => cx.editor.set_status(msg),
+ Err(err) => cx.editor.set_error(err.to_string()),
+ }
}
fn make_search_word_bounded(cx: &mut Context) {
- let regex = match cx.editor.registers.last('/') {
+ let regex = match cx.editor.registers.last('/', cx.editor) {
Some(regex) => regex,
None => return,
};
@@ -1997,14 +1998,16 @@ fn make_search_word_bounded(cx: &mut Context) {
if !start_anchored {
new_regex.push_str("\\b");
}
- new_regex.push_str(regex);
+ new_regex.push_str(&regex);
if !end_anchored {
new_regex.push_str("\\b");
}
let msg = format!("register '{}' set to '{}'", '/', &new_regex);
- cx.editor.registers.push('/', new_regex);
- cx.editor.set_status(msg);
+ match cx.editor.registers.push('/', new_regex) {
+ Ok(_) => cx.editor.set_status(msg),
+ Err(err) => cx.editor.set_error(err.to_string()),
+ }
}
fn global_search(cx: &mut Context) {
@@ -2367,7 +2370,10 @@ fn delete_selection_impl(cx: &mut Context, op: Operation) {
let text = doc.text().slice(..);
let values: Vec<String> = selection.fragments(text).map(Cow::into_owned).collect();
let reg_name = cx.register.unwrap_or('"');
- cx.editor.registers.write(reg_name, values);
+ if let Err(err) = cx.editor.registers.write(reg_name, values) {
+ cx.editor.set_error(err.to_string());
+ return;
+ }
};
// then delete
@@ -3758,18 +3764,16 @@ fn yank(cx: &mut Context) {
.fragments(text)
.map(Cow::into_owned)
.collect();
+ let selections = values.len();
+ let register = cx.register.unwrap_or('"');
- let msg = format!(
- "yanked {} selection(s) to register {}",
- values.len(),
- cx.register.unwrap_or('"')
- );
-
- cx.editor
- .registers
- .write(cx.register.unwrap_or('"'), values);
+ match cx.editor.registers.write(register, values) {
+ Ok(_) => cx.editor.set_status(format!(
+ "yanked {selections} selection(s) to register {register}",
+ )),
+ Err(err) => cx.editor.set_error(err.to_string()),
+ }
- cx.editor.set_status(msg);
exit_select_mode(cx);
}
@@ -3778,6 +3782,7 @@ fn yank_joined_impl(editor: &mut Editor, separator: &str, register: char) {
let text = doc.text().slice(..);
let selection = doc.selection(view.id);
+ let selections = selection.len();
let joined = selection
.fragments(text)
.fold(String::new(), |mut acc, fragment| {
@@ -3788,14 +3793,12 @@ fn yank_joined_impl(editor: &mut Editor, separator: &str, register: char) {
acc
});
- let msg = format!(
- "joined and yanked {} selection(s) to register {}",
- selection.len(),
- register,
- );
-
- editor.registers.write(register, vec![joined]);
- editor.set_status(msg);
+ match editor.registers.write(register, vec![joined]) {
+ Ok(_) => editor.set_status(format!(
+ "joined and yanked {selections} selection(s) to register {register}",
+ )),
+ Err(err) => editor.set_error(err.to_string()),
+ }
}
fn yank_joined(cx: &mut Context) {
@@ -4040,34 +4043,34 @@ fn paste_primary_clipboard_before(cx: &mut Context) {
fn replace_with_yanked(cx: &mut Context) {
let count = cx.count();
let reg_name = cx.register.unwrap_or('"');
- let (view, doc) = current!(cx.editor);
- let registers = &mut cx.editor.registers;
-
- if let Some(values) = registers.read(reg_name) {
- if !values.is_empty() {
- let repeat = std::iter::repeat(
- values
- .last()
- .map(|value| Tendril::from(&value.repeat(count)))
- .unwrap(),
- );
- let mut values = values
- .iter()
- .map(|value| Tendril::from(&value.repeat(count)))
- .chain(repeat);
- let selection = doc.selection(view.id);
- let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
- if !range.is_empty() {
- (range.from(), range.to(), Some(values.next().unwrap()))
- } else {
- (range.from(), range.to(), None)
- }
- });
- doc.apply(&transaction, view.id);
- exit_select_mode(cx);
+ let Some(values) = cx.editor.registers
+ .read(reg_name, cx.editor)
+ .filter(|values| values.len() > 0) else { return };
+ let values: Vec<_> = values.map(|value| value.to_string()).collect();
+
+ let (view, doc) = current!(cx.editor);
+ let repeat = std::iter::repeat(
+ values
+ .last()
+ .map(|value| Tendril::from(&value.repeat(count)))
+ .unwrap(),
+ );
+ let mut values = values
+ .iter()
+ .map(|value| Tendril::from(&value.repeat(count)))
+ .chain(repeat);
+ let selection = doc.selection(view.id);
+ let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
+ if !range.is_empty() {
+ (range.from(), range.to(), Some(values.next().unwrap()))
+ } else {
+ (range.from(), range.to(), None)
}
- }
+ });
+
+ doc.apply(&transaction, view.id);
+ exit_select_mode(cx);
}
fn replace_selections_with_clipboard_impl(
@@ -4109,12 +4112,12 @@ fn replace_selections_with_primary_clipboard(cx: &mut Context) {
fn paste(cx: &mut Context, pos: Paste) {
let count = cx.count();
let reg_name = cx.register.unwrap_or('"');
- let (view, doc) = current!(cx.editor);
- let registers = &mut cx.editor.registers;
- if let Some(values) = registers.read(reg_name) {
- paste_impl(values, doc, view, pos, count, cx.editor.mode);
- }
+ let Some(values) = cx.editor.registers.read(reg_name, cx.editor) else { return };
+ let values: Vec<_> = values.map(|value| value.to_string()).collect();
+
+ let (view, doc) = current!(cx.editor);
+ paste_impl(&values, doc, view, pos, count, cx.editor.mode);
}
fn paste_after(cx: &mut Context) {
@@ -5593,9 +5596,12 @@ fn record_macro(cx: &mut Context) {
}
})
.collect::<String>();
- cx.editor.registers.write(reg, vec![s]);
- cx.editor
- .set_status(format!("Recorded to register [{}]", reg));
+ match cx.editor.registers.write(reg, vec![s]) {
+ Ok(_) => cx
+ .editor
+ .set_status(format!("Recorded to register [{}]", reg)),
+ Err(err) => cx.editor.set_error(err.to_string()),
+ }
} else {
let reg = cx.register.take().unwrap_or('@');
cx.editor.macro_recording = Some((reg, Vec::new()));
@@ -5615,8 +5621,14 @@ fn replay_macro(cx: &mut Context) {
return;
}
- let keys: Vec<KeyEvent> = if let Some([keys_str]) = cx.editor.registers.read(reg) {
- match helix_view::input::parse_macro(keys_str) {
+ let keys: Vec<KeyEvent> = if let Some(keys) = cx
+ .editor
+ .registers
+ .read(reg, cx.editor)
+ .filter(|values| values.len() == 1)
+ .map(|mut values| values.next().unwrap())
+ {
+ match helix_view::input::parse_macro(&keys) {
Ok(keys) => keys,
Err(err) => {
cx.editor.set_error(format!("Invalid macro: {}", err));
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 175f8bc6..28759b3f 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -2285,13 +2285,12 @@ fn clear_register(
format!("Invalid register {}", args[0])
);
let register = args[0].chars().next().unwrap_or_default();
- match cx.editor.registers.remove(register) {
- Some(_) => cx
- .editor
- .set_status(format!("Register {} cleared", register)),
- None => cx
- .editor
- .set_error(format!("Register {} not found", register)),
+ if cx.editor.registers.remove(register) {
+ cx.editor
+ .set_status(format!("Register {} cleared", register));
+ } else {
+ cx.editor
+ .set_error(format!("Register {} not found", register));
}
Ok(())
}
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index 1352f493..8dc2906a 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -306,8 +306,8 @@ impl Prompt {
direction: CompletionDirection,
) {
(self.callback_fn)(cx, &self.line, PromptEvent::Abort);
- let values = match cx.editor.registers.read(register) {
- Some(values) if !values.is_empty() => values,
+ let mut values = match cx.editor.registers.read(register, cx.editor) {
+ Some(values) if values.len() > 0 => values,
_ => return,
};
@@ -315,13 +315,16 @@ impl Prompt {
let index = match direction {
CompletionDirection::Forward => self.history_pos.map_or(0, |i| i + 1),
- CompletionDirection::Backward => {
- self.history_pos.unwrap_or(values.len()).saturating_sub(1)
- }
+ CompletionDirection::Backward => self
+ .history_pos
+ .unwrap_or_else(|| values.len())
+ .saturating_sub(1),
}
.min(end);
- self.line = values[index].clone();
+ self.line = values.nth(index).unwrap().to_string();
+ // Appease the borrow checker.
+ drop(values);
self.history_pos = Some(index);
@@ -470,7 +473,7 @@ impl Prompt {
// Show the most recently entered value as a suggestion.
if let Some(suggestion) = self
.history_register
- .and_then(|reg| cx.editor.registers.last(reg))
+ .and_then(|reg| cx.editor.registers.last(reg, cx.editor))
{
surface.set_string(line_area.x, line_area.y, suggestion, suggestion_color);
}
@@ -567,25 +570,29 @@ impl Component for Prompt {
} else {
let last_item = self
.history_register
- .and_then(|reg| cx.editor.registers.last(reg).cloned())
- .map(|entry| entry.into())
- .unwrap_or_else(|| Cow::from(""));
+ .and_then(|reg| cx.editor.registers.last(reg, cx.editor))
+ .map(|entry| entry.to_string())
+ .unwrap_or_else(|| String::from(""));
// handle executing with last command in history if nothing entered
- let input: Cow<str> = if self.line.is_empty() {
- last_item
+ let input = if self.line.is_empty() {
+ &last_item
} else {
if last_item != self.line {
// store in history
if let Some(register) = self.history_register {
- cx.editor.registers.push(register, self.line.clone());
+ if let Err(err) =
+ cx.editor.registers.push(register, self.line.clone())
+ {
+ cx.editor.set_error(err.to_string());
+ }
};
}
- self.line.as_str().into()
+ &self.line
};
- (self.callback_fn)(cx, &input, PromptEvent::Validate);
+ (self.callback_fn)(cx, input, PromptEvent::Validate);
return close_fn;
}
@@ -617,25 +624,16 @@ impl Component for Prompt {
self.completion = cx
.editor
.registers
- .inner()
- .iter()
- .map(|(ch, reg)| {
- let content = reg
- .read()
- .get(0)
- .and_then(|s| s.lines().next().to_owned())
- .unwrap_or_default();
- (0.., format!("{} {}", ch, &content).into())
- })
+ .iter_preview()
+ .map(|(ch, preview)| (0.., format!("{} {}", ch, &preview).into()))
.collect();
self.next_char_handler = Some(Box::new(|prompt, c, context| {
prompt.insert_str(
- context
+ &context
.editor
.registers
- .read(c)
- .and_then(|r| r.first())
- .map_or("", |r| r.as_str()),
+ .first(c, context.editor)
+ .unwrap_or_default(),
context.editor,
);
}));