Regex prompts should have a history with a specifiable register

pull/732/head
Blaž Hrastnik 3 years ago
parent 011f9aa47f
commit 72cf86e462

@ -44,7 +44,7 @@ use once_cell::sync::Lazy;
use serde::de::{self, Deserialize, Deserializer}; use serde::de::{self, Deserialize, Deserializer};
pub struct Context<'a> { pub struct Context<'a> {
pub selected_register: helix_view::RegisterSelection, pub register: Option<char>,
pub count: Option<NonZeroUsize>, pub count: Option<NonZeroUsize>,
pub editor: &'a mut Editor, pub editor: &'a mut Editor,
@ -1030,7 +1030,8 @@ fn select_all(cx: &mut Context) {
} }
fn select_regex(cx: &mut Context) { fn select_regex(cx: &mut Context) {
let prompt = ui::regex_prompt(cx, "select:".into(), move |view, doc, _, regex| { let reg = cx.register.unwrap_or('/');
let prompt = ui::regex_prompt(cx, "select:".into(), Some(reg), move |view, doc, regex| {
let text = doc.text().slice(..); let text = doc.text().slice(..);
if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex) if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
{ {
@ -1042,7 +1043,8 @@ fn select_regex(cx: &mut Context) {
} }
fn split_selection(cx: &mut Context) { fn split_selection(cx: &mut Context) {
let prompt = ui::regex_prompt(cx, "split:".into(), move |view, doc, _, regex| { let reg = cx.register.unwrap_or('/');
let prompt = ui::regex_prompt(cx, "split:".into(), Some(reg), move |view, doc, regex| {
let text = doc.text().slice(..); let text = doc.text().slice(..);
let selection = selection::split_on_matches(text, doc.selection(view.id), &regex); let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
@ -1100,6 +1102,7 @@ fn search_impl(doc: &mut Document, view: &mut View, contents: &str, regex: &Rege
// TODO: use one function for search vs extend // TODO: use one function for search vs extend
fn search(cx: &mut Context) { fn search(cx: &mut Context) {
let reg = cx.register.unwrap_or('/');
let (_, doc) = current!(cx.editor); let (_, doc) = current!(cx.editor);
// TODO: could probably share with select_on_matches? // TODO: could probably share with select_on_matches?
@ -1108,10 +1111,8 @@ fn search(cx: &mut Context) {
// feed chunks into the regex yet // feed chunks into the regex yet
let contents = doc.text().slice(..).to_string(); let contents = doc.text().slice(..).to_string();
let prompt = ui::regex_prompt(cx, "search:".into(), move |view, doc, registers, regex| { let prompt = ui::regex_prompt(cx, "search:".into(), Some(reg), move |view, doc, regex| {
search_impl(doc, view, &contents, &regex, false); 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)); cx.push_layer(Box::new(prompt));
@ -1119,9 +1120,9 @@ fn search(cx: &mut Context) {
fn search_next_impl(cx: &mut Context, extend: bool) { fn search_next_impl(cx: &mut Context, extend: bool) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers; let registers = &cx.editor.registers;
if let Some(query) = registers.read('/') { if let Some(query) = registers.read('/') {
let query = query.first().unwrap(); let query = query.last().unwrap();
let contents = doc.text().slice(..).to_string(); let contents = doc.text().slice(..).to_string();
let regex = Regex::new(query).unwrap(); let regex = Regex::new(query).unwrap();
search_impl(doc, view, &contents, &regex, extend); search_impl(doc, view, &contents, &regex, extend);
@ -1141,7 +1142,7 @@ fn search_selection(cx: &mut Context) {
let contents = doc.text().slice(..); let contents = doc.text().slice(..);
let query = doc.selection(view.id).primary().fragment(contents); let query = doc.selection(view.id).primary().fragment(contents);
let regex = regex::escape(&query); let regex = regex::escape(&query);
cx.editor.registers.write('/', vec![regex]); cx.editor.registers.get_mut('/').push(regex);
search_next(cx); search_next(cx);
} }
@ -1200,7 +1201,7 @@ fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId
} }
fn delete_selection(cx: &mut Context) { fn delete_selection(cx: &mut Context) {
let reg_name = cx.selected_register.name(); let reg_name = cx.register.unwrap_or('"');
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers; let registers = &mut cx.editor.registers;
let reg = registers.get_mut(reg_name); let reg = registers.get_mut(reg_name);
@ -1213,7 +1214,7 @@ fn delete_selection(cx: &mut Context) {
} }
fn change_selection(cx: &mut Context) { fn change_selection(cx: &mut Context) {
let reg_name = cx.selected_register.name(); let reg_name = cx.register.unwrap_or('"');
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers; let registers = &mut cx.editor.registers;
let reg = registers.get_mut(reg_name); let reg = registers.get_mut(reg_name);
@ -3346,12 +3347,12 @@ fn yank(cx: &mut Context) {
let msg = format!( let msg = format!(
"yanked {} selection(s) to register {}", "yanked {} selection(s) to register {}",
values.len(), values.len(),
cx.selected_register.name() cx.register.unwrap_or('"')
); );
cx.editor cx.editor
.registers .registers
.write(cx.selected_register.name(), values); .write(cx.register.unwrap_or('"'), values);
cx.editor.set_status(msg); cx.editor.set_status(msg);
exit_select_mode(cx); exit_select_mode(cx);
@ -3524,7 +3525,7 @@ fn paste_primary_clipboard_before(cx: &mut Context) {
} }
fn replace_with_yanked(cx: &mut Context) { fn replace_with_yanked(cx: &mut Context) {
let reg_name = cx.selected_register.name(); let reg_name = cx.register.unwrap_or('"');
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers; let registers = &mut cx.editor.registers;
@ -3575,7 +3576,7 @@ fn replace_selections_with_primary_clipboard(cx: &mut Context) {
} }
fn paste_after(cx: &mut Context) { fn paste_after(cx: &mut Context) {
let reg_name = cx.selected_register.name(); let reg_name = cx.register.unwrap_or('"');
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers; let registers = &mut cx.editor.registers;
@ -3589,7 +3590,7 @@ fn paste_after(cx: &mut Context) {
} }
fn paste_before(cx: &mut Context) { fn paste_before(cx: &mut Context) {
let reg_name = cx.selected_register.name(); let reg_name = cx.register.unwrap_or('"');
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers; let registers = &mut cx.editor.registers;
@ -3770,7 +3771,8 @@ fn join_selections(cx: &mut Context) {
fn keep_selections(cx: &mut Context) { fn keep_selections(cx: &mut Context) {
// keep selections matching regex // keep selections matching regex
let prompt = ui::regex_prompt(cx, "keep:".into(), move |view, doc, _, regex| { let reg = cx.register.unwrap_or('/');
let prompt = ui::regex_prompt(cx, "keep:".into(), Some(reg), move |view, doc, regex| {
let text = doc.text().slice(..); let text = doc.text().slice(..);
if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) { if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
@ -4103,7 +4105,7 @@ fn wclose(cx: &mut Context) {
fn select_register(cx: &mut Context) { fn select_register(cx: &mut Context) {
cx.on_next_key(move |cx, event| { cx.on_next_key(move |cx, event| {
if let Some(ch) = event.char() { if let Some(ch) = event.char() {
cx.editor.selected_register.select(ch); cx.editor.selected_register = Some(ch);
} }
}) })
} }

@ -710,7 +710,7 @@ impl EditorView {
// debug_assert!(cxt.count != 0); // debug_assert!(cxt.count != 0);
// set the register // set the register
cxt.selected_register = cxt.editor.selected_register.take(); cxt.register = cxt.editor.selected_register.take();
self.handle_keymap_event(mode, cxt, event); self.handle_keymap_event(mode, cxt, event);
if self.keymaps.pending().is_empty() { if self.keymaps.pending().is_empty() {
@ -887,9 +887,9 @@ impl EditorView {
impl Component for EditorView { impl Component for EditorView {
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult { fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
let mut cxt = commands::Context { let mut cxt = commands::Context {
selected_register: helix_view::RegisterSelection::default(),
editor: &mut cx.editor, editor: &mut cx.editor,
count: None, count: None,
register: None,
callback: None, callback: None,
on_next_key_callback: None, on_next_key_callback: None,
jobs: cx.jobs, jobs: cx.jobs,

@ -20,7 +20,6 @@ pub use spinner::{ProgressSpinners, Spinner};
pub use text::Text; pub use text::Text;
use helix_core::regex::Regex; use helix_core::regex::Regex;
use helix_core::register::Registers;
use helix_view::{Document, Editor, View}; use helix_view::{Document, Editor, View};
use std::path::PathBuf; use std::path::PathBuf;
@ -28,7 +27,8 @@ use std::path::PathBuf;
pub fn regex_prompt( pub fn regex_prompt(
cx: &mut crate::commands::Context, cx: &mut crate::commands::Context,
prompt: std::borrow::Cow<'static, str>, prompt: std::borrow::Cow<'static, str>,
fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static, history_register: Option<char>,
fun: impl Fn(&mut View, &mut Document, Regex) + 'static,
) -> Prompt { ) -> Prompt {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let view_id = view.id; let view_id = view.id;
@ -36,7 +36,7 @@ pub fn regex_prompt(
Prompt::new( Prompt::new(
prompt, prompt,
None, history_register,
|_input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate |_input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate
move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| { move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
match event { match event {
@ -56,12 +56,11 @@ pub fn regex_prompt(
match Regex::new(input) { match Regex::new(input) {
Ok(regex) => { Ok(regex) => {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers;
// revert state to what it was before the last update // revert state to what it was before the last update
doc.set_selection(view.id, snapshot.clone()); doc.set_selection(view.id, snapshot.clone());
fun(view, doc, registers, regex); fun(view, doc, regex);
view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff); view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff);
} }

@ -3,7 +3,7 @@ use crate::{
graphics::{CursorKind, Rect}, graphics::{CursorKind, Rect},
theme::{self, Theme}, theme::{self, Theme},
tree::Tree, tree::Tree,
Document, DocumentId, RegisterSelection, View, ViewId, Document, DocumentId, View, ViewId,
}; };
use futures_util::future; use futures_util::future;
@ -73,7 +73,7 @@ pub struct Editor {
pub tree: Tree, pub tree: Tree,
pub documents: SlotMap<DocumentId, Document>, pub documents: SlotMap<DocumentId, Document>,
pub count: Option<std::num::NonZeroUsize>, pub count: Option<std::num::NonZeroUsize>,
pub selected_register: RegisterSelection, pub selected_register: Option<char>,
pub registers: Registers, pub registers: Registers,
pub theme: Theme, pub theme: Theme,
pub language_servers: helix_lsp::Registry, pub language_servers: helix_lsp::Registry,
@ -111,7 +111,7 @@ impl Editor {
tree: Tree::new(area), tree: Tree::new(area),
documents: SlotMap::with_key(), documents: SlotMap::with_key(),
count: None, count: None,
selected_register: RegisterSelection::default(), selected_register: None,
theme: themes.default(), theme: themes.default(),
language_servers, language_servers,
syn_loader: config_loader, syn_loader: config_loader,

@ -8,7 +8,6 @@ pub mod graphics;
pub mod info; pub mod info;
pub mod input; pub mod input;
pub mod keyboard; pub mod keyboard;
pub mod register_selection;
pub mod theme; pub mod theme;
pub mod tree; pub mod tree;
pub mod view; pub mod view;
@ -20,6 +19,5 @@ slotmap::new_key_type! {
pub use document::Document; pub use document::Document;
pub use editor::Editor; pub use editor::Editor;
pub use register_selection::RegisterSelection;
pub use theme::Theme; pub use theme::Theme;
pub use view::View; pub use view::View;

@ -1,48 +0,0 @@
/// Register selection and configuration
///
/// This is a kind a of specialized `Option<char>` for register selection.
/// Point is to keep whether the register selection has been explicitely
/// set or not while being convenient by knowing the default register name.
#[derive(Debug)]
pub struct RegisterSelection {
selected: char,
default_name: char,
}
impl RegisterSelection {
pub fn new(default_name: char) -> Self {
Self {
selected: default_name,
default_name,
}
}
pub fn select(&mut self, name: char) {
self.selected = name;
}
pub fn take(&mut self) -> Self {
Self {
selected: std::mem::replace(&mut self.selected, self.default_name),
default_name: self.default_name,
}
}
pub fn is_default(&self) -> bool {
self.selected == self.default_name
}
pub fn name(&self) -> char {
self.selected
}
}
impl Default for RegisterSelection {
fn default() -> Self {
let default_name = '"';
Self {
selected: default_name,
default_name,
}
}
}
Loading…
Cancel
Save