on master help sign in
Regex prompts should have a history with a specifiable register
72cf86eBlaž Hrastnik
helix-term/src/commands.rs+20−18
@@ -44,7 +44,7 @@ use once_cell::sync::Lazy;
44use serde::de::{self, Deserialize, Deserializer};
44use serde::de::{self, Deserialize, Deserializer};
45
45
46pub struct Context<'a> {
46pub struct Context<'a> {
47− pub selected_register: helix_view::RegisterSelection,
47+ pub register: Option<char>,
48 pub count: Option<NonZeroUsize>,
48 pub count: Option<NonZeroUsize>,
49 pub editor: &'a mut Editor,
49 pub editor: &'a mut Editor,
50
50
@@ -1030,7 +1030,8 @@ fn select_all(cx: &mut Context) {
1030}
1030}
1031
1031
1032fn select_regex(cx: &mut Context) {
1032fn select_regex(cx: &mut Context) {
1033+ let reg = cx.register.unwrap_or('/');
1033− let prompt = ui::regex_prompt(cx, "select:".into(), move |view, doc, _, regex| {
1034+ let prompt = ui::regex_prompt(cx, "select:".into(), Some(reg), move |view, doc, regex| {
1034 let text = doc.text().slice(..);
1035 let text = doc.text().slice(..);
1035 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
1036 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
1036 {
1037 {
@@ -1042,7 +1043,8 @@ fn select_regex(cx: &mut Context) {
1042}
1043}
1043
1044
1044fn split_selection(cx: &mut Context) {
1045fn split_selection(cx: &mut Context) {
1046+ let reg = cx.register.unwrap_or('/');
1045− let prompt = ui::regex_prompt(cx, "split:".into(), move |view, doc, _, regex| {
1047+ let prompt = ui::regex_prompt(cx, "split:".into(), Some(reg), move |view, doc, regex| {
1046 let text = doc.text().slice(..);
1048 let text = doc.text().slice(..);
1047 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
1049 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
1048 doc.set_selection(view.id, selection);
1050 doc.set_selection(view.id, selection);
@@ -1100,6 +1102,7 @@ fn search_impl(doc: &mut Document, view: &mut View, contents: &str, regex: &Rege
1100
1102
1101// TODO: use one function for search vs extend
1103// TODO: use one function for search vs extend
1102fn search(cx: &mut Context) {
1104fn search(cx: &mut Context) {
1105+ let reg = cx.register.unwrap_or('/');
1103 let (_, doc) = current!(cx.editor);
1106 let (_, doc) = current!(cx.editor);
1104
1107
1105 // TODO: could probably share with select_on_matches?
1108 // TODO: could probably share with select_on_matches?
@@ -1108,10 +1111,8 @@ fn search(cx: &mut Context) {
1108 // feed chunks into the regex yet
1111 // feed chunks into the regex yet
1109 let contents = doc.text().slice(..).to_string();
1112 let contents = doc.text().slice(..).to_string();
1110
1113
1111− let prompt = ui::regex_prompt(cx, "search:".into(), move |view, doc, registers, regex| {
1114+ let prompt = ui::regex_prompt(cx, "search:".into(), Some(reg), move |view, doc, regex| {
1112 search_impl(doc, view, &contents, &regex, false);
1115 search_impl(doc, view, &contents, &regex, false);
1113− // TODO: only store on enter (accept), not update
1114− registers.write('/', vec![regex.as_str().to_string()]);
1115 });
1116 });
1116
1117
1117 cx.push_layer(Box::new(prompt));
1118 cx.push_layer(Box::new(prompt));
@@ -1119,9 +1120,9 @@ fn search(cx: &mut Context) {
1119
1120
1120fn search_next_impl(cx: &mut Context, extend: bool) {
1121fn search_next_impl(cx: &mut Context, extend: bool) {
1121 let (view, doc) = current!(cx.editor);
1122 let (view, doc) = current!(cx.editor);
1122− let registers = &mut cx.editor.registers;
1123+ let registers = &cx.editor.registers;
1123 if let Some(query) = registers.read('/') {
1124 if let Some(query) = registers.read('/') {
1124− let query = query.first().unwrap();
1125+ let query = query.last().unwrap();
1125 let contents = doc.text().slice(..).to_string();
1126 let contents = doc.text().slice(..).to_string();
1126 let regex = Regex::new(query).unwrap();
1127 let regex = Regex::new(query).unwrap();
1127 search_impl(doc, view, &contents, &regex, extend);
1128 search_impl(doc, view, &contents, &regex, extend);
@@ -1141,7 +1142,7 @@ fn search_selection(cx: &mut Context) {
1141 let contents = doc.text().slice(..);
1142 let contents = doc.text().slice(..);
1142 let query = doc.selection(view.id).primary().fragment(contents);
1143 let query = doc.selection(view.id).primary().fragment(contents);
1143 let regex = regex::escape(&query);
1144 let regex = regex::escape(&query);
1144− cx.editor.registers.write('/', vec![regex]);
1145+ cx.editor.registers.get_mut('/').push(regex);
1145 search_next(cx);
1146 search_next(cx);
1146}
1147}
1147
1148
@@ -1200,7 +1201,7 @@ fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId
1200}
1201}
1201
1202
1202fn delete_selection(cx: &mut Context) {
1203fn delete_selection(cx: &mut Context) {
1203− let reg_name = cx.selected_register.name();
1204+ let reg_name = cx.register.unwrap_or('"');
1204 let (view, doc) = current!(cx.editor);
1205 let (view, doc) = current!(cx.editor);
1205 let registers = &mut cx.editor.registers;
1206 let registers = &mut cx.editor.registers;
1206 let reg = registers.get_mut(reg_name);
1207 let reg = registers.get_mut(reg_name);
@@ -1213,7 +1214,7 @@ fn delete_selection(cx: &mut Context) {
1213}
1214}
1214
1215
1215fn change_selection(cx: &mut Context) {
1216fn change_selection(cx: &mut Context) {
1216− let reg_name = cx.selected_register.name();
1217+ let reg_name = cx.register.unwrap_or('"');
1217 let (view, doc) = current!(cx.editor);
1218 let (view, doc) = current!(cx.editor);
1218 let registers = &mut cx.editor.registers;
1219 let registers = &mut cx.editor.registers;
1219 let reg = registers.get_mut(reg_name);
1220 let reg = registers.get_mut(reg_name);
@@ -3346,12 +3347,12 @@ fn yank(cx: &mut Context) {
3346 let msg = format!(
3347 let msg = format!(
3347 "yanked {} selection(s) to register {}",
3348 "yanked {} selection(s) to register {}",
3348 values.len(),
3349 values.len(),
3349− cx.selected_register.name()
3350+ cx.register.unwrap_or('"')
3350 );
3351 );
3351
3352
3352 cx.editor
3353 cx.editor
3353 .registers
3354 .registers
3354− .write(cx.selected_register.name(), values);
3355+ .write(cx.register.unwrap_or('"'), values);
3355
3356
3356 cx.editor.set_status(msg);
3357 cx.editor.set_status(msg);
3357 exit_select_mode(cx);
3358 exit_select_mode(cx);
@@ -3524,7 +3525,7 @@ fn paste_primary_clipboard_before(cx: &mut Context) {
3524}
3525}
3525
3526
3526fn replace_with_yanked(cx: &mut Context) {
3527fn replace_with_yanked(cx: &mut Context) {
3527− let reg_name = cx.selected_register.name();
3528+ let reg_name = cx.register.unwrap_or('"');
3528 let (view, doc) = current!(cx.editor);
3529 let (view, doc) = current!(cx.editor);
3529 let registers = &mut cx.editor.registers;
3530 let registers = &mut cx.editor.registers;
3530
3531
@@ -3575,7 +3576,7 @@ fn replace_selections_with_primary_clipboard(cx: &mut Context) {
3575}
3576}
3576
3577
3577fn paste_after(cx: &mut Context) {
3578fn paste_after(cx: &mut Context) {
3578− let reg_name = cx.selected_register.name();
3579+ let reg_name = cx.register.unwrap_or('"');
3579 let (view, doc) = current!(cx.editor);
3580 let (view, doc) = current!(cx.editor);
3580 let registers = &mut cx.editor.registers;
3581 let registers = &mut cx.editor.registers;
3581
3582
@@ -3589,7 +3590,7 @@ fn paste_after(cx: &mut Context) {
3589}
3590}
3590
3591
3591fn paste_before(cx: &mut Context) {
3592fn paste_before(cx: &mut Context) {
3592− let reg_name = cx.selected_register.name();
3593+ let reg_name = cx.register.unwrap_or('"');
3593 let (view, doc) = current!(cx.editor);
3594 let (view, doc) = current!(cx.editor);
3594 let registers = &mut cx.editor.registers;
3595 let registers = &mut cx.editor.registers;
3595
3596
@@ -3770,7 +3771,8 @@ fn join_selections(cx: &mut Context) {
3770
3771
3771fn keep_selections(cx: &mut Context) {
3772fn keep_selections(cx: &mut Context) {
3772 // keep selections matching regex
3773 // keep selections matching regex
3774+ let reg = cx.register.unwrap_or('/');
3773− let prompt = ui::regex_prompt(cx, "keep:".into(), move |view, doc, _, regex| {
3775+ let prompt = ui::regex_prompt(cx, "keep:".into(), Some(reg), move |view, doc, regex| {
3774 let text = doc.text().slice(..);
3776 let text = doc.text().slice(..);
3775
3777
3776 if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
3778 if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
@@ -4103,7 +4105,7 @@ fn wclose(cx: &mut Context) {
4103fn select_register(cx: &mut Context) {
4105fn select_register(cx: &mut Context) {
4104 cx.on_next_key(move |cx, event| {
4106 cx.on_next_key(move |cx, event| {
4105 if let Some(ch) = event.char() {
4107 if let Some(ch) = event.char() {
4106− cx.editor.selected_register.select(ch);
4108+ cx.editor.selected_register = Some(ch);
4107 }
4109 }
4108 })
4110 })
4109}
4111}
helix-term/src/ui/editor.rs+2−2
@@ -710,7 +710,7 @@ impl EditorView {
710 // debug_assert!(cxt.count != 0);
710 // debug_assert!(cxt.count != 0);
711
711
712 // set the register
712 // set the register
713− cxt.selected_register = cxt.editor.selected_register.take();
713+ cxt.register = cxt.editor.selected_register.take();
714
714
715 self.handle_keymap_event(mode, cxt, event);
715 self.handle_keymap_event(mode, cxt, event);
716 if self.keymaps.pending().is_empty() {
716 if self.keymaps.pending().is_empty() {
@@ -887,9 +887,9 @@ impl EditorView {
887impl Component for EditorView {
887impl Component for EditorView {
888 fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
888 fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
889 let mut cxt = commands::Context {
889 let mut cxt = commands::Context {
890− selected_register: helix_view::RegisterSelection::default(),
891 editor: &mut cx.editor,
890 editor: &mut cx.editor,
892 count: None,
891 count: None,
892+ register: None,
893 callback: None,
893 callback: None,
894 on_next_key_callback: None,
894 on_next_key_callback: None,
895 jobs: cx.jobs,
895 jobs: cx.jobs,
helix-term/src/ui/mod.rs+4−5
@@ -20,7 +20,6 @@ pub use spinner::{ProgressSpinners, Spinner};
20pub use text::Text;
20pub use text::Text;
21
21
22use helix_core::regex::Regex;
22use helix_core::regex::Regex;
23−use helix_core::register::Registers;
24use helix_view::{Document, Editor, View};
23use helix_view::{Document, Editor, View};
25
24
26use std::path::PathBuf;
25use std::path::PathBuf;
@@ -28,7 +27,8 @@ use std::path::PathBuf;
28pub fn regex_prompt(
27pub fn regex_prompt(
29 cx: &mut crate::commands::Context,
28 cx: &mut crate::commands::Context,
30 prompt: std::borrow::Cow<'static, str>,
29 prompt: std::borrow::Cow<'static, str>,
30+ history_register: Option<char>,
31− fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static,
31+ fun: impl Fn(&mut View, &mut Document, Regex) + 'static,
32) -> Prompt {
32) -> Prompt {
33 let (view, doc) = current!(cx.editor);
33 let (view, doc) = current!(cx.editor);
34 let view_id = view.id;
34 let view_id = view.id;
@@ -36,7 +36,7 @@ pub fn regex_prompt(
36
36
37 Prompt::new(
37 Prompt::new(
38 prompt,
38 prompt,
39− None,
39+ history_register,
40 |_input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate
40 |_input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate
41 move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
41 move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
42 match event {
42 match event {
@@ -56,12 +56,11 @@ pub fn regex_prompt(
56 match Regex::new(input) {
56 match Regex::new(input) {
57 Ok(regex) => {
57 Ok(regex) => {
58 let (view, doc) = current!(cx.editor);
58 let (view, doc) = current!(cx.editor);
59− let registers = &mut cx.editor.registers;
60
59
61 // revert state to what it was before the last update
60 // revert state to what it was before the last update
62 doc.set_selection(view.id, snapshot.clone());
61 doc.set_selection(view.id, snapshot.clone());
63
62
64− fun(view, doc, registers, regex);
63+ fun(view, doc, regex);
65
64
66 view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff);
65 view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff);
67 }
66 }
helix-view/src/editor.rs+3−3
@@ -3,7 +3,7 @@ use crate::{
3 graphics::{CursorKind, Rect},
3 graphics::{CursorKind, Rect},
4 theme::{self, Theme},
4 theme::{self, Theme},
5 tree::Tree,
5 tree::Tree,
6− Document, DocumentId, RegisterSelection, View, ViewId,
6+ Document, DocumentId, View, ViewId,
7};
7};
8
8
9use futures_util::future;
9use futures_util::future;
@@ -73,7 +73,7 @@ pub struct Editor {
73 pub tree: Tree,
73 pub tree: Tree,
74 pub documents: SlotMap<DocumentId, Document>,
74 pub documents: SlotMap<DocumentId, Document>,
75 pub count: Option<std::num::NonZeroUsize>,
75 pub count: Option<std::num::NonZeroUsize>,
76− pub selected_register: RegisterSelection,
76+ pub selected_register: Option<char>,
77 pub registers: Registers,
77 pub registers: Registers,
78 pub theme: Theme,
78 pub theme: Theme,
79 pub language_servers: helix_lsp::Registry,
79 pub language_servers: helix_lsp::Registry,
@@ -111,7 +111,7 @@ impl Editor {
111 tree: Tree::new(area),
111 tree: Tree::new(area),
112 documents: SlotMap::with_key(),
112 documents: SlotMap::with_key(),
113 count: None,
113 count: None,
114− selected_register: RegisterSelection::default(),
114+ selected_register: None,
115 theme: themes.default(),
115 theme: themes.default(),
116 language_servers,
116 language_servers,
117 syn_loader: config_loader,
117 syn_loader: config_loader,
helix-view/src/lib.rs−2
@@ -8,7 +8,6 @@ pub mod graphics;
8pub mod info;
8pub mod info;
9pub mod input;
9pub mod input;
10pub mod keyboard;
10pub mod keyboard;
11−pub mod register_selection;
12pub mod theme;
11pub mod theme;
13pub mod tree;
12pub mod tree;
14pub mod view;
13pub mod view;
@@ -20,6 +19,5 @@ slotmap::new_key_type! {
20
19
21pub use document::Document;
20pub use document::Document;
22pub use editor::Editor;
21pub use editor::Editor;
23−pub use register_selection::RegisterSelection;
24pub use theme::Theme;
22pub use theme::Theme;
25pub use view::View;
23pub use view::View;
helix-view/src/register_selection.rsdeleted−48
@@ -1,48 +0,0 @@
1−/// Register selection and configuration
2−///
3−/// This is a kind a of specialized `Option<char>` for register selection.
4−/// Point is to keep whether the register selection has been explicitely
5−/// set or not while being convenient by knowing the default register name.
6−#[derive(Debug)]
7−pub struct RegisterSelection {
8− selected: char,
9− default_name: char,
10−}
11−
12−impl RegisterSelection {
13− pub fn new(default_name: char) -> Self {
14− Self {
15− selected: default_name,
16− default_name,
17− }
18− }
19−
20− pub fn select(&mut self, name: char) {
21− self.selected = name;
22− }
23−
24− pub fn take(&mut self) -> Self {
25− Self {
26− selected: std::mem::replace(&mut self.selected, self.default_name),
27− default_name: self.default_name,
28− }
29− }
30−
31− pub fn is_default(&self) -> bool {
32− self.selected == self.default_name
33− }
34−
35− pub fn name(&self) -> char {
36− self.selected
37− }
38−}
39−
40−impl Default for RegisterSelection {
41− fn default() -> Self {
42− let default_name = '"';
43− Self {
44− selected: default_name,
45− default_name,
46− }
47− }
48−}