on master help sign in
Remove RwLock for registers
6bdf609Benoît CORTIER
helix-core/src/register.rs+60−17
@@ -1,20 +1,63 @@
1−use crate::Tendril;
2−use once_cell::sync::Lazy;
3−use std::{collections::HashMap, sync::RwLock};
1+use std::collections::HashMap;
4−
2+
5−// TODO: could be an instance on Editor
3+#[derive(Debug)]
6−static REGISTRY: Lazy<RwLock<HashMap<char, Vec<String>>>> =
4+pub struct Register {
7− Lazy::new(|| RwLock::new(HashMap::new()));
5+ name: char,
8−
6+ values: Vec<String>,
9−/// Read register values.
7+}
8+
9+impl Register {
10+ pub fn new(name: char) -> Self {
11+ Self {
12+ name,
13+ values: Vec::new(),
14+ }
15+ }
16+
10−pub fn get(register_name: char) -> Option<Vec<String>> {
17+ pub fn new_with_values(name: char, values: Vec<String>) -> Self {
11− let registry = REGISTRY.read().unwrap();
18+ Self { name, values }
12− registry.get(&register_name).cloned() // TODO: no cloning
19+ }
20+
21+ pub fn name(&self) -> char {
22+ self.name
23+ }
24+
25+ pub fn read(&self) -> &Vec<String> {
26+ &self.values
27+ }
28+
29+ pub fn write(&mut self, values: Vec<String>) {
30+ self.values = values;
31+ }
13}
32}
14
33
15−/// Read register values.
34+/// Currently just wraps a `HashMap` of `Register`s
16−// restoring: bool
35+#[derive(Debug, Default)]
17−pub fn set(register_name: char, values: Vec<String>) {
36+pub struct Registers {
18− let mut registry = REGISTRY.write().unwrap();
37+ inner: HashMap<char, Register>,
19− registry.insert(register_name, values);
38+}
39+
40+impl Registers {
41+ pub fn get(&self, name: char) -> Option<&Register> {
42+ self.inner.get(&name)
43+ }
44+
45+ pub fn get_mut(&mut self, name: char) -> Option<&mut Register> {
46+ self.inner.get_mut(&name)
47+ }
48+
49+ pub fn get_or_insert(&mut self, name: char) -> &mut Register {
50+ self.inner
51+ .entry(name)
52+ .or_insert_with(|| Register::new(name))
53+ }
54+
55+ pub fn write(&mut self, name: char, values: Vec<String>) {
56+ self.inner
57+ .insert(name, Register::new_with_values(name, values));
58+ }
59+
60+ pub fn read(&self, name: char) -> Option<&Vec<String>> {
61+ self.get(name).map(|reg| reg.read())
62+ }
20}
63}
helix-term/src/commands.rs+83−62
@@ -4,8 +4,9 @@ use helix_core::{
4 movement::{self, Direction},
4 movement::{self, Direction},
5 object, pos_at_coords,
5 object, pos_at_coords,
6 regex::{self, Regex},
6 regex::{self, Regex},
7+ register::{self, Register, Registers},
7− register, search, selection, Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection,
8+ search, selection, Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection, SmallVec,
8− SmallVec, Tendril, Transaction,
9+ Tendril, Transaction,
9};
10};
10
11
11use helix_view::{
12use helix_view::{
@@ -39,7 +40,7 @@ use crossterm::event::{KeyCode, KeyEvent};
39use once_cell::sync::Lazy;
40use once_cell::sync::Lazy;
40
41
41pub struct Context<'a> {
42pub struct Context<'a> {
42− pub register: helix_view::RegisterSelection,
43+ pub selected_register: helix_view::RegisterSelection,
43 pub count: Option<std::num::NonZeroUsize>,
44 pub count: Option<std::num::NonZeroUsize>,
44 pub editor: &'a mut Editor,
45 pub editor: &'a mut Editor,
45
46
@@ -65,6 +66,11 @@ impl<'a> Context<'a> {
65 self.editor.current()
66 self.editor.current()
66 }
67 }
67
68
69+ #[inline]
70+ pub fn current_with_registers(&mut self) -> (&mut View, &mut Document, &mut Registers) {
71+ self.editor.current_with_registers()
72+ }
73+
68 /// Push a new component onto the compositor.
74 /// Push a new component onto the compositor.
69 pub fn push_layer(&mut self, mut component: Box<dyn Component>) {
75 pub fn push_layer(&mut self, mut component: Box<dyn Component>) {
70 self.callback = Some(Box::new(|compositor: &mut Compositor| {
76 self.callback = Some(Box::new(|compositor: &mut Compositor| {
@@ -631,7 +637,7 @@ pub fn select_all(cx: &mut Context) {
631}
637}
632
638
633pub fn select_regex(cx: &mut Context) {
639pub fn select_regex(cx: &mut Context) {
634− let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, regex| {
640+ let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, _, regex| {
635 let text = doc.text().slice(..);
641 let text = doc.text().slice(..);
636 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
642 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
637 {
643 {
@@ -643,7 +649,7 @@ pub fn select_regex(cx: &mut Context) {
643}
649}
644
650
645pub fn split_selection(cx: &mut Context) {
651pub fn split_selection(cx: &mut Context) {
646− let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, regex| {
652+ let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, _, regex| {
647 let text = doc.text().slice(..);
653 let text = doc.text().slice(..);
648 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
654 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
649 doc.set_selection(view.id, selection);
655 doc.set_selection(view.id, selection);
@@ -714,20 +720,24 @@ pub fn search(cx: &mut Context) {
714 let contents = doc.text().slice(..).to_string();
720 let contents = doc.text().slice(..).to_string();
715
721
716 let view_id = view.id;
722 let view_id = view.id;
717− let prompt = ui::regex_prompt(cx, "search:".to_string(), move |view, doc, regex| {
723+ let prompt = ui::regex_prompt(
724+ cx,
725+ "search:".to_string(),
726+ move |view, doc, registers, regex| {
718− search_impl(doc, view, &contents, &regex, false);
727+ search_impl(doc, view, &contents, &regex, false);
719− // TODO: only store on enter (accept), not update
728+ // TODO: only store on enter (accept), not update
720− register::set('\\', vec![regex.as_str().to_string()]);
729+ registers.write('\\', vec![regex.as_str().to_string()]);
730+ },
721− });
731+ );
722
732
723 cx.push_layer(Box::new(prompt));
733 cx.push_layer(Box::new(prompt));
724}
734}
725// can't search next for ""compose"" for some reason
735// can't search next for ""compose"" for some reason
726
736
727pub fn search_next_impl(cx: &mut Context, extend: bool) {
737pub fn search_next_impl(cx: &mut Context, extend: bool) {
738+ let (view, doc, registers) = cx.current_with_registers();
728− if let Some(query) = register::get('\\') {
739+ if let Some(query) = registers.read('\\') {
729 let query = query.first().unwrap();
740 let query = query.first().unwrap();
730− let (view, doc) = cx.current();
731 let contents = doc.text().slice(..).to_string();
741 let contents = doc.text().slice(..).to_string();
732 let regex = Regex::new(query).unwrap();
742 let regex = Regex::new(query).unwrap();
733 search_impl(doc, view, &contents, &regex, extend);
743 search_impl(doc, view, &contents, &regex, extend);
@@ -747,7 +757,7 @@ pub fn search_selection(cx: &mut Context) {
747 let contents = doc.text().slice(..);
757 let contents = doc.text().slice(..);
748 let query = doc.selection(view.id).primary().fragment(contents);
758 let query = doc.selection(view.id).primary().fragment(contents);
749 let regex = regex::escape(&query);
759 let regex = regex::escape(&query);
750− register::set('\\', vec![regex]);
760+ cx.editor.registers.write('\\', vec![regex]);
751 search_next(cx);
761 search_next(cx);
752}
762}
753
763
@@ -794,7 +804,7 @@ pub fn extend_line(cx: &mut Context) {
794
804
795// heuristic: append changes to history after each command, unless we're in insert mode
805// heuristic: append changes to history after each command, unless we're in insert mode
796
806
797−fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) {
807+fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId) {
798 // first yank the selection
808 // first yank the selection
799 let values: Vec<String> = doc
809 let values: Vec<String> = doc
800 .selection(view_id)
810 .selection(view_id)
@@ -802,7 +812,7 @@ fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) {
802 .map(Cow::into_owned)
812 .map(Cow::into_owned)
803 .collect();
813 .collect();
804
814
805− register::set(reg, values);
815+ reg.write(values);
806
816
807 // then delete
817 // then delete
808 let transaction =
818 let transaction =
@@ -815,8 +825,9 @@ fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) {
815}
825}
816
826
817pub fn delete_selection(cx: &mut Context) {
827pub fn delete_selection(cx: &mut Context) {
818− let reg = cx.register.name();
828+ let reg_name = cx.selected_register.name();
819− let (view, doc) = cx.current();
829+ let (view, doc, registers) = cx.current_with_registers();
830+ let reg = registers.get_or_insert(reg_name);
820 delete_selection_impl(reg, doc, view.id);
831 delete_selection_impl(reg, doc, view.id);
821
832
822 doc.append_changes_to_history(view.id);
833 doc.append_changes_to_history(view.id);
@@ -826,8 +837,9 @@ pub fn delete_selection(cx: &mut Context) {
826}
837}
827
838
828pub fn change_selection(cx: &mut Context) {
839pub fn change_selection(cx: &mut Context) {
829− let reg = cx.register.name();
840+ let reg_name = cx.selected_register.name();
830− let (view, doc) = cx.current();
841+ let (view, doc, registers) = cx.current_with_registers();
842+ let reg = registers.get_or_insert(reg_name);
831 delete_selection_impl(reg, doc, view.id);
843 delete_selection_impl(reg, doc, view.id);
832 enter_insert_mode(doc);
844 enter_insert_mode(doc);
833}
845}
@@ -2227,10 +2239,12 @@ pub fn yank(cx: &mut Context) {
2227 let msg = format!(
2239 let msg = format!(
2228 "yanked {} selection(s) to register {}",
2240 "yanked {} selection(s) to register {}",
2229 values.len(),
2241 values.len(),
2230− cx.register.name()
2242+ cx.selected_register.name()
2231 );
2243 );
2232
2244
2233− register::set(cx.register.name(), values);
2245+ cx.editor
2246+ .registers
2247+ .write(cx.selected_register.name(), values);
2234
2248
2235 cx.editor.set_status(msg)
2249 cx.editor.set_status(msg)
2236}
2250}
@@ -2241,47 +2255,48 @@ enum Paste {
2241 After,
2255 After,
2242}
2256}
2243
2257
2244−fn paste_impl(reg: char, doc: &mut Document, view: &View, action: Paste) -> Option<Transaction> {
2258+fn paste_impl(
2245− if let Some(values) = register::get(reg) {
2259+ values: &[String],
2260+ doc: &mut Document,
2261+ view: &View,
2262+ action: Paste,
2263+) -> Option<Transaction> {
2246− let repeat = std::iter::repeat(
2264+ let repeat = std::iter::repeat(
2247− values
2265+ values
2248− .last()
2266+ .last()
2249− .map(|value| Tendril::from_slice(value))
2267+ .map(|value| Tendril::from_slice(value))
2250− .unwrap(),
2268+ .unwrap(),
2251− );
2269+ );
2252
2270
2253− // if any of values ends \n it's linewise paste
2271+ // if any of values ends \n it's linewise paste
2254− let linewise = values.iter().any(|value| value.ends_with('\n'));
2272+ let linewise = values.iter().any(|value| value.ends_with('\n'));
2255
2273
2256− let mut values = values.into_iter().map(Tendril::from).chain(repeat);
2274+ let mut values = values.iter().cloned().map(Tendril::from).chain(repeat);
2257
2275
2258− let text = doc.text();
2276+ let text = doc.text();
2259
2277
2260− let transaction =
2261− Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
2278+ let transaction = Transaction::change_by_selection(text, doc.selection(view.id), |range| {
2262− let pos = match (action, linewise) {
2279+ let pos = match (action, linewise) {
2263− // paste linewise before
2280+ // paste linewise before
2264− (Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
2281+ (Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
2265− // paste linewise after
2282+ // paste linewise after
2266− (Paste::After, true) => text.line_to_char(text.char_to_line(range.to()) + 1),
2283+ (Paste::After, true) => text.line_to_char(text.char_to_line(range.to()) + 1),
2267− // paste insert
2284+ // paste insert
2268− (Paste::Before, false) => range.from(),
2285+ (Paste::Before, false) => range.from(),
2269− // paste append
2286+ // paste append
2270− (Paste::After, false) => range.to() + 1,
2287+ (Paste::After, false) => range.to() + 1,
2271− };
2288+ };
2272− (pos, pos, Some(values.next().unwrap()))
2289+ (pos, pos, Some(values.next().unwrap()))
2273− });
2290+ });
2291+
2274− return Some(transaction);
2292+ Some(transaction)
2275− }
2276− None
2277}
2293}
2278
2294
2279pub fn replace_with_yanked(cx: &mut Context) {
2295pub fn replace_with_yanked(cx: &mut Context) {
2280− let reg = cx.register.name();
2296+ let reg_name = cx.selected_register.name();
2281−
2297+ let (view, doc, registers) = cx.current_with_registers();
2282− if let Some(values) = register::get(reg) {
2283− let (view, doc) = cx.current();
2284
2298
2299+ if let Some(values) = registers.read(reg_name) {
2285 if let Some(yank) = values.first() {
2300 if let Some(yank) = values.first() {
2286 let transaction =
2301 let transaction =
2287 Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
2302 Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
@@ -2307,20 +2322,26 @@ pub fn replace_with_yanked(cx: &mut Context) {
2307// default insert
2322// default insert
2308
2323
2309pub fn paste_after(cx: &mut Context) {
2324pub fn paste_after(cx: &mut Context) {
2310− let reg = cx.register.name();
2325+ let reg_name = cx.selected_register.name();
2311− let (view, doc) = cx.current();
2326+ let (view, doc, registers) = cx.current_with_registers();
2312
2327
2328+ if let Some(transaction) = registers
2329+ .read(reg_name)
2313− if let Some(transaction) = paste_impl(reg, doc, view, Paste::After) {
2330+ .and_then(|values| paste_impl(values, doc, view, Paste::After))
2331+ {
2314 doc.apply(&transaction, view.id);
2332 doc.apply(&transaction, view.id);
2315 doc.append_changes_to_history(view.id);
2333 doc.append_changes_to_history(view.id);
2316 }
2334 }
2317}
2335}
2318
2336
2319pub fn paste_before(cx: &mut Context) {
2337pub fn paste_before(cx: &mut Context) {
2320− let reg = cx.register.name();
2338+ let reg_name = cx.selected_register.name();
2321− let (view, doc) = cx.current();
2339+ let (view, doc, registers) = cx.current_with_registers();
2322
2340
2341+ if let Some(transaction) = registers
2342+ .read(reg_name)
2323− if let Some(transaction) = paste_impl(reg, doc, view, Paste::Before) {
2343+ .and_then(|values| paste_impl(values, doc, view, Paste::Before))
2344+ {
2324 doc.apply(&transaction, view.id);
2345 doc.apply(&transaction, view.id);
2325 doc.append_changes_to_history(view.id);
2346 doc.append_changes_to_history(view.id);
2326 }
2347 }
@@ -2494,7 +2515,7 @@ pub fn join_selections(cx: &mut Context) {
2494
2515
2495pub fn keep_selections(cx: &mut Context) {
2516pub fn keep_selections(cx: &mut Context) {
2496 // keep selections matching regex
2517 // keep selections matching regex
2497− let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, regex| {
2518+ let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, _, regex| {
2498 let text = doc.text().slice(..);
2519 let text = doc.text().slice(..);
2499
2520
2500 if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
2521 if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
@@ -2788,7 +2809,7 @@ pub fn select_register(cx: &mut Context) {
2788 ..
2809 ..
2789 } = event
2810 } = event
2790 {
2811 {
2791− cx.editor.register.select(ch);
2812+ cx.editor.selected_register.select(ch);
2792 }
2813 }
2793 })
2814 })
2794}
2815}
helix-term/src/ui/editor.rs+2−2
@@ -570,7 +570,7 @@ impl EditorView {
570 // debug_assert!(cxt.count != 0);
570 // debug_assert!(cxt.count != 0);
571
571
572 // set the register
572 // set the register
573− cxt.register = cxt.editor.register.take();
573+ cxt.selected_register = cxt.editor.selected_register.take();
574
574
575 if let Some(command) = self.keymap[&mode].get(&event) {
575 if let Some(command) = self.keymap[&mode].get(&event) {
576 command(cxt);
576 command(cxt);
@@ -610,7 +610,7 @@ impl Component for EditorView {
610 let mode = doc.mode();
610 let mode = doc.mode();
611
611
612 let mut cxt = commands::Context {
612 let mut cxt = commands::Context {
613− register: helix_view::RegisterSelection::default(),
613+ selected_register: helix_view::RegisterSelection::default(),
614 editor: &mut cx.editor,
614 editor: &mut cx.editor,
615 count: None,
615 count: None,
616 callback: None,
616 callback: None,
helix-term/src/ui/mod.rs+4−3
@@ -20,6 +20,7 @@ pub use tui::layout::Rect;
20pub use tui::style::{Color, Modifier, Style};
20pub use tui::style::{Color, Modifier, Style};
21
21
22use helix_core::regex::Regex;
22use helix_core::regex::Regex;
23+use helix_core::register::Registers;
23use helix_view::{Document, Editor, View};
24use helix_view::{Document, Editor, View};
24
25
25use std::path::{Path, PathBuf};
26use std::path::{Path, PathBuf};
@@ -27,7 +28,7 @@ use std::path::{Path, PathBuf};
27pub fn regex_prompt(
28pub fn regex_prompt(
28 cx: &mut crate::commands::Context,
29 cx: &mut crate::commands::Context,
29 prompt: String,
30 prompt: String,
30− fun: impl Fn(&mut View, &mut Document, Regex) + 'static,
31+ fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static,
31) -> Prompt {
32) -> Prompt {
32 let view_id = cx.view().id;
33 let view_id = cx.view().id;
33 let snapshot = cx.doc().selection(view_id).clone();
34 let snapshot = cx.doc().selection(view_id).clone();
@@ -53,13 +54,13 @@ pub fn regex_prompt(
53
54
54 match Regex::new(input) {
55 match Regex::new(input) {
55 Ok(regex) => {
56 Ok(regex) => {
56− let (view, doc) = editor.current();
57+ let (view, doc, registers) = editor.current_with_registers();
57
58
58 // revert state to what it was before the last update
59 // revert state to what it was before the last update
59 // TODO: also revert text
60 // TODO: also revert text
60 doc.set_selection(view.id, snapshot.clone());
61 doc.set_selection(view.id, snapshot.clone());
61
62
62− fun(view, doc, regex);
63+ fun(view, doc, registers, regex);
63
64
64 view.ensure_cursor_in_view(doc);
65 view.ensure_cursor_in_view(doc);
65 }
66 }
helix-view/src/editor.rs+12−2
@@ -8,13 +8,15 @@ use slotmap::SlotMap;
8use anyhow::Error;
8use anyhow::Error;
9
9
10pub use helix_core::diagnostic::Severity;
10pub use helix_core::diagnostic::Severity;
11+pub use helix_core::register::Registers;
11
12
12#[derive(Debug)]
13#[derive(Debug)]
13pub struct Editor {
14pub struct Editor {
14 pub tree: Tree,
15 pub tree: Tree,
15 pub documents: SlotMap<DocumentId, Document>,
16 pub documents: SlotMap<DocumentId, Document>,
16 pub count: Option<std::num::NonZeroUsize>,
17 pub count: Option<std::num::NonZeroUsize>,
17− pub register: RegisterSelection,
18+ pub selected_register: RegisterSelection,
19+ pub registers: Registers,
18 pub theme: Theme,
20 pub theme: Theme,
19 pub language_servers: helix_lsp::Registry,
21 pub language_servers: helix_lsp::Registry,
20
22
@@ -59,9 +61,10 @@ impl Editor {
59 tree: Tree::new(area),
61 tree: Tree::new(area),
60 documents: SlotMap::with_key(),
62 documents: SlotMap::with_key(),
61 count: None,
63 count: None,
62− register: RegisterSelection::default(),
64+ selected_register: RegisterSelection::default(),
63 theme,
65 theme,
64 language_servers,
66 language_servers,
67+ registers: Registers::default(),
65 status_msg: None,
68 status_msg: None,
66 }
69 }
67 }
70 }
@@ -239,6 +242,13 @@ impl Editor {
239 (view, doc)
242 (view, doc)
240 }
243 }
241
244
245+ pub fn current_with_registers(&mut self) -> (&mut View, &mut Document, &mut Registers) {
246+ let view = self.tree.get_mut(self.tree.focus);
247+ let doc = &mut self.documents[view.doc];
248+ let registers = &mut self.registers;
249+ (view, doc, registers)
250+ }
251+
242 pub fn view(&self) -> &View {
252 pub fn view(&self) -> &View {
243 self.tree.get(self.tree.focus)
253 self.tree.get(self.tree.focus)
244 }
254 }