on master help sign in
Remove ui.cursor.primary and hashmap lookups
016640fGokul Soumya
book/src/themes.md−1
@@ -173,7 +173,6 @@ These scopes are used for theming the editor interface.
173| `ui.cursor.insert` | |
173| `ui.cursor.insert` | |
174| `ui.cursor.select` | |
174| `ui.cursor.select` | |
175| `ui.cursor.match` | Matching bracket etc. |
175| `ui.cursor.match` | Matching bracket etc. |
176−| `ui.cursor.primary` | Cursor with primary selection |
177| `ui.linenr` | |
176| `ui.linenr` | |
178| `ui.linenr.selected` | |
177| `ui.linenr.selected` | |
179| `ui.statusline` | Statusline |
178| `ui.statusline` | Statusline |
helix-term/src/ui/editor.rs+8−9
@@ -233,24 +233,23 @@ impl EditorView {
233 }
233 }
234 .unwrap_or(base_cursor_scope);
234 .unwrap_or(base_cursor_scope);
235
235
236− let primary_cursor_scope = theme
237− .find_scope_index("ui.cursor.primary")
238− .unwrap_or(cursor_scope);
239 let primary_selection_scope = theme
236 let primary_selection_scope = theme
240 .find_scope_index("ui.selection.primary")
237 .find_scope_index("ui.selection.primary")
241 .unwrap_or(selection_scope);
238 .unwrap_or(selection_scope);
242
239
243 let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
240 let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
244 for (i, range) in selection.iter().enumerate() {
241 for (i, range) in selection.iter().enumerate() {
245− let (cursor_scope, selection_scope) = if i == primary_idx {
242+ let selection_is_primary = i == primary_idx;
243+ let selection_scope = if selection_is_primary {
246− (primary_cursor_scope, primary_selection_scope)
244+ primary_selection_scope
247 } else {
245 } else {
248− (cursor_scope, selection_scope)
246+ selection_scope
249 };
247 };
250
248
251 // Special-case: cursor at end of the rope.
249 // Special-case: cursor at end of the rope.
252 if range.head == range.anchor && range.head == text.len_chars() {
250 if range.head == range.anchor && range.head == text.len_chars() {
253− if i != primary_idx {
251+ if !selection_is_primary {
252+ // Terminal cursor acts as the primary cursor
254 spans.push((cursor_scope, range.head..range.head + 1));
253 spans.push((cursor_scope, range.head..range.head + 1));
255 }
254 }
256 continue;
255 continue;
@@ -261,13 +260,13 @@ impl EditorView {
261 // Standard case.
260 // Standard case.
262 let cursor_start = prev_grapheme_boundary(text, range.head);
261 let cursor_start = prev_grapheme_boundary(text, range.head);
263 spans.push((selection_scope, range.anchor..cursor_start));
262 spans.push((selection_scope, range.anchor..cursor_start));
264− if i != primary_idx {
263+ if !selection_is_primary {
265 spans.push((cursor_scope, cursor_start..range.head));
264 spans.push((cursor_scope, cursor_start..range.head));
266 }
265 }
267 } else {
266 } else {
268 // Reverse case.
267 // Reverse case.
269 let cursor_end = next_grapheme_boundary(text, range.head);
268 let cursor_end = next_grapheme_boundary(text, range.head);
270− if i != primary_idx {
269+ if !selection_is_primary {
271 spans.push((cursor_scope, range.head..cursor_end));
270 spans.push((cursor_scope, range.head..cursor_end));
272 }
271 }
273 spans.push((selection_scope, cursor_end..range.anchor));
272 spans.push((selection_scope, cursor_end..range.anchor));
helix-view/src/document.rs+3−3
@@ -29,9 +29,9 @@ pub const SCRATCH_BUFFER_NAME: &str = "[scratch]";
29
29
30#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
30#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
31pub enum Mode {
31pub enum Mode {
32− Normal,
32+ Normal = 0,
33− Select,
33+ Select = 1,
34− Insert,
34+ Insert = 2,
35}
35}
36
36
37impl Display for Mode {
37impl Display for Mode {
helix-view/src/editor.rs+24−12
@@ -22,10 +22,10 @@ use anyhow::Error;
22
22
23pub use helix_core::diagnostic::Severity;
23pub use helix_core::diagnostic::Severity;
24pub use helix_core::register::Registers;
24pub use helix_core::register::Registers;
25−use helix_core::{hashmap, syntax};
25+use helix_core::syntax;
26use helix_core::{Position, Selection};
26use helix_core::{Position, Selection};
27
27
28−use serde::Deserialize;
28+use serde::{Deserialize, Deserializer};
29
29
30fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
30fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
31where
31where
@@ -107,12 +107,28 @@ pub struct Config {
107 pub cursor_shape: CursorShapeConfig,
107 pub cursor_shape: CursorShapeConfig,
108}
108}
109
109
110+// Cursor shape is read and used on every rendered frame and so needs
111+// to be fast. Therefore we avoid a hashmap and use an enum indexed array.
110−#[derive(Debug, Clone, PartialEq, Deserialize)]
112+#[derive(Debug, Clone, PartialEq)]
111−#[serde(transparent)]
112−pub struct CursorShapeConfig(HashMap<Mode, CursorKind>);
113+pub struct CursorShapeConfig([CursorKind; 3]);
114+
115+impl<'de> Deserialize<'de> for CursorShapeConfig {
116+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117+ where
118+ D: Deserializer<'de>,
119+ {
120+ let m = HashMap::<Mode, CursorKind>::deserialize(deserializer)?;
121+ let into_cursor = |mode: Mode| m.get(&mode).copied().unwrap_or_default();
122+ Ok(CursorShapeConfig([
123+ into_cursor(Mode::Normal),
124+ into_cursor(Mode::Select),
125+ into_cursor(Mode::Insert),
126+ ]))
127+ }
128+}
113
129
114impl std::ops::Deref for CursorShapeConfig {
130impl std::ops::Deref for CursorShapeConfig {
115− type Target = HashMap<Mode, CursorKind>;
131+ type Target = [CursorKind; 3];
116
132
117 fn deref(&self) -> &Self::Target {
133 fn deref(&self) -> &Self::Target {
118 &self.0
134 &self.0
@@ -121,11 +137,7 @@ impl std::ops::Deref for CursorShapeConfig {
121
137
122impl Default for CursorShapeConfig {
138impl Default for CursorShapeConfig {
123 fn default() -> Self {
139 fn default() -> Self {
124− Self(hashmap!(
125− Mode::Insert => CursorKind::Block,
140+ Self([CursorKind::Block; 3])
126− Mode::Normal => CursorKind::Block,
127− Mode::Select => CursorKind::Block,
128− ))
129 }
141 }
130}
142}
131
143
@@ -621,7 +633,7 @@ impl Editor {
621 let cursorkind = self
633 let cursorkind = self
622 .config
634 .config
623 .cursor_shape
635 .cursor_shape
624− .get(&doc.mode())
636+ .get(doc.mode() as usize)
625 .copied()
637 .copied()
626 .unwrap_or_default();
638 .unwrap_or_default();
627 (Some(pos), cursorkind)
639 (Some(pos), cursorkind)