mirror of
https://github.com/helix-editor/helix.git
synced 2025-01-19 13:37:06 +04:00
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`.
This commit is contained in:
parent
d5de9183ef
commit
68affa3c59
@ -6,16 +6,15 @@
|
|||||||
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(®ister_name).cloned() // TODO: no cloning
|
||||||
// TODO: no cloning
|
|
||||||
registry.get(®ister).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 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 @@ fn command_mode(&self, mode: Mode, cxt: &mut commands::Context, event: KeyEvent)
|
|||||||
// 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 @@ fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
|
|||||||
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 @@ pub fn new(mut area: tui::layout::Rect) -> Self {
|
|||||||
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 @@
|
|||||||
|
|
||||||
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;
|
||||||
|
47
helix-view/src/register_selection.rs
Normal file
47
helix-view/src/register_selection.rs
Normal file
@ -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…
Reference in New Issue
Block a user