Implement register selection

User can select register to yank into with the " command.
A new state is added to `Editor` and `commands::Context` structs.
This state is managed by leveraging a new struct `RegisterSelection`.
imgbot
Benoît CORTIER 3 years ago committed by Blaž Hrastnik
parent d5de9183ef
commit 68affa3c59

@ -6,16 +6,15 @@ use std::{collections::HashMap, sync::RwLock};
static REGISTRY: Lazy<RwLock<HashMap<char, Vec<String>>>> = static REGISTRY: Lazy<RwLock<HashMap<char, Vec<String>>>> =
Lazy::new(|| RwLock::new(HashMap::new())); Lazy::new(|| RwLock::new(HashMap::new()));
pub fn get(register: char) -> Option<Vec<String>> { /// Read register values.
pub fn get(register_name: char) -> Option<Vec<String>> {
let registry = REGISTRY.read().unwrap(); let registry = REGISTRY.read().unwrap();
registry.get(&register_name).cloned() // TODO: no cloning
// TODO: no cloning
registry.get(&register).cloned()
} }
/// Read register values.
// restoring: bool // restoring: bool
pub fn set(register: char, values: Vec<String>) { pub fn set(register_name: char, values: Vec<String>) {
let mut registry = REGISTRY.write().unwrap(); let mut registry = REGISTRY.write().unwrap();
registry.insert(register_name, values);
registry.insert(register, values);
} }

@ -35,6 +35,7 @@ use crossterm::event::{KeyCode, KeyEvent};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub struct Context<'a> { pub struct Context<'a> {
pub register: helix_view::RegisterSelection,
pub count: usize, pub count: usize,
pub editor: &'a mut Editor, pub editor: &'a mut Editor,
@ -777,7 +778,7 @@ pub fn extend_line(cx: &mut Context) {
// heuristic: append changes to history after each command, unless we're in insert mode // heuristic: append changes to history after each command, unless we're in insert mode
fn _delete_selection(doc: &mut Document, view_id: ViewId) { fn _delete_selection(reg: char, doc: &mut Document, view_id: ViewId) {
// first yank the selection // first yank the selection
let values: Vec<String> = doc let values: Vec<String> = doc
.selection(view_id) .selection(view_id)
@ -785,8 +786,6 @@ fn _delete_selection(doc: &mut Document, view_id: ViewId) {
.map(Cow::into_owned) .map(Cow::into_owned)
.collect(); .collect();
// TODO: allow specifying reg
let reg = '"';
register::set(reg, values); register::set(reg, values);
// then delete // then delete
@ -800,8 +799,9 @@ fn _delete_selection(doc: &mut Document, view_id: ViewId) {
} }
pub fn delete_selection(cx: &mut Context) { pub fn delete_selection(cx: &mut Context) {
let reg = cx.register.name();
let (view, doc) = cx.current(); let (view, doc) = cx.current();
_delete_selection(doc, view.id); _delete_selection(reg, doc, view.id);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
@ -810,8 +810,9 @@ pub fn delete_selection(cx: &mut Context) {
} }
pub fn change_selection(cx: &mut Context) { pub fn change_selection(cx: &mut Context) {
let reg = cx.register.name();
let (view, doc) = cx.current(); let (view, doc) = cx.current();
_delete_selection(doc, view.id); _delete_selection(reg, doc, view.id);
enter_insert_mode(doc); enter_insert_mode(doc);
} }
@ -1893,11 +1894,13 @@ pub fn yank(cx: &mut Context) {
.map(Cow::into_owned) .map(Cow::into_owned)
.collect(); .collect();
// TODO: allow specifying reg let msg = format!(
let reg = '"'; "yanked {} selection(s) to register {}",
let msg = format!("yanked {} selection(s) to register {}", values.len(), reg); values.len(),
cx.register.name()
);
register::set(reg, values); register::set(cx.register.name(), values);
cx.editor.set_status(msg) cx.editor.set_status(msg)
} }
@ -1908,9 +1911,7 @@ enum Paste {
After, After,
} }
fn _paste(doc: &mut Document, view: &View, action: Paste) -> Option<Transaction> { fn _paste(reg: char, doc: &mut Document, view: &View, action: Paste) -> Option<Transaction> {
// TODO: allow specifying reg
let reg = '"';
if let Some(values) = register::get(reg) { if let Some(values) = register::get(reg) {
let repeat = std::iter::repeat( let repeat = std::iter::repeat(
values values
@ -1956,18 +1957,20 @@ fn _paste(doc: &mut Document, view: &View, action: Paste) -> Option<Transaction>
// default insert // default insert
pub fn paste_after(cx: &mut Context) { pub fn paste_after(cx: &mut Context) {
let reg = cx.register.name();
let (view, doc) = cx.current(); let (view, doc) = cx.current();
if let Some(transaction) = _paste(doc, view, Paste::After) { if let Some(transaction) = _paste(reg, doc, view, Paste::After) {
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
} }
} }
pub fn paste_before(cx: &mut Context) { pub fn paste_before(cx: &mut Context) {
let reg = cx.register.name();
let (view, doc) = cx.current(); let (view, doc) = cx.current();
if let Some(transaction) = _paste(doc, view, Paste::Before) { if let Some(transaction) = _paste(reg, doc, view, Paste::Before) {
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
} }
@ -2426,6 +2429,18 @@ pub fn wclose(cx: &mut Context) {
cx.editor.close(view_id, /* close_buffer */ false); cx.editor.close(view_id, /* close_buffer */ false);
} }
pub fn select_register(cx: &mut Context) {
cx.on_next_key(move |cx, event| {
if let KeyEvent {
code: KeyCode::Char(ch),
..
} = event
{
cx.editor.register.select(ch);
}
})
}
pub fn space_mode(cx: &mut Context) { pub fn space_mode(cx: &mut Context) {
cx.on_next_key(move |cx, event| { cx.on_next_key(move |cx, event| {
if let KeyEvent { if let KeyEvent {
@ -2439,7 +2454,7 @@ pub fn space_mode(cx: &mut Context) {
'b' => buffer_picker(cx), 'b' => buffer_picker(cx),
'w' => window_mode(cx), 'w' => window_mode(cx),
// ' ' => toggle_alternate_buffer(cx), // ' ' => toggle_alternate_buffer(cx),
// TODO: temporary since space mode took it's old key // TODO: temporary since space mode took its old key
' ' => keep_primary_selection(cx), ' ' => keep_primary_selection(cx),
_ => (), _ => (),
} }

@ -284,6 +284,8 @@ pub fn default() -> Keymaps {
key!(' ') => commands::space_mode, key!(' ') => commands::space_mode,
key!('z') => commands::view_mode, key!('z') => commands::view_mode,
key!('"') => commands::select_register,
); );
// TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether
// we keep this separate select mode. More keys can fit into normal mode then, but it's weird // we keep this separate select mode. More keys can fit into normal mode then, but it's weird

@ -537,6 +537,9 @@ impl EditorView {
// if this fails, count was Some(0) // if this fails, count was Some(0)
// debug_assert!(cxt.count != 0); // debug_assert!(cxt.count != 0);
// set the register
cxt.register = cxt.editor.register.take();
if let Some(command) = self.keymap[&mode].get(&event) { if let Some(command) = self.keymap[&mode].get(&event) {
command(cxt); command(cxt);
} }
@ -575,11 +578,12 @@ impl Component for EditorView {
let mode = doc.mode(); let mode = doc.mode();
let mut cxt = commands::Context { let mut cxt = commands::Context {
editor: &mut cx.editor, register: helix_view::RegisterSelection::default(),
count: 1, count: 1,
editor: &mut cx.editor,
callback: None, callback: None,
callbacks: cx.callbacks,
on_next_key_callback: None, on_next_key_callback: None,
callbacks: cx.callbacks,
}; };
if let Some(on_next_key) = self.on_next_key.take() { if let Some(on_next_key) = self.on_next_key.take() {

@ -1,4 +1,4 @@
use crate::{theme::Theme, tree::Tree, Document, DocumentId, View, ViewId}; use crate::{theme::Theme, tree::Tree, Document, DocumentId, RegisterSelection, View, ViewId};
use tui::layout::Rect; use tui::layout::Rect;
use std::path::PathBuf; use std::path::PathBuf;
@ -13,6 +13,7 @@ pub struct Editor {
pub tree: Tree, pub tree: Tree,
pub documents: SlotMap<DocumentId, Document>, pub documents: SlotMap<DocumentId, Document>,
pub count: Option<usize>, pub count: Option<usize>,
pub register: RegisterSelection,
pub theme: Theme, pub theme: Theme,
pub language_servers: helix_lsp::Registry, pub language_servers: helix_lsp::Registry,
@ -57,6 +58,7 @@ impl Editor {
tree: Tree::new(area), tree: Tree::new(area),
documents: SlotMap::with_key(), documents: SlotMap::with_key(),
count: None, count: None,
register: RegisterSelection::default(),
theme, theme,
language_servers, language_servers,
status_msg: None, status_msg: None,

@ -1,5 +1,6 @@
pub mod document; pub mod document;
pub mod editor; pub mod editor;
pub mod register_selection;
pub mod theme; pub mod theme;
pub mod tree; pub mod tree;
pub mod view; pub mod view;
@ -10,5 +11,6 @@ new_key_type! { pub struct ViewId; }
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;

@ -0,0 +1,47 @@
/// 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.
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