Regex prompts should have a history with a specifiable register
This commit is contained in:
parent
011f9aa47f
commit
72cf86e462
6 changed files with 29 additions and 78 deletions
|
@ -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), ®ex)
|
if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), ®ex)
|
||||||
{
|
{
|
||||||
|
@ -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), ®ex);
|
let selection = selection::split_on_matches(text, doc.selection(view.id), ®ex);
|
||||||
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, ®ex, false);
|
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));
|
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, ®ex, extend);
|
search_impl(doc, view, &contents, ®ex, 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), ®ex) {
|
if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), ®ex) {
|
||||||
|
@ -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…
Add table
Reference in a new issue