on master help sign in
ui: Optimize tree-sitter style lookups

Tree sitter returns an index referring to the position of the scope in the scopes array. We can use that same index to avoid a hashmap lookup and instead store the styles in an array. This currently stores the styles in both a map and an array because the UI still uses hashmap lookups, but it's a reasonable tradeoff.

commit259678585c3410044683bf4d2619b2d024a04514 authorBlaž Hrastnik <blaz@mxxn.io> dateWed 1 Dec 2021, 13:08 (5 years ago) parent7bbf4c5
2 files changed+25−8
helix-term/src/ui/editor.rs+2−4 helix-view/src/theme.rs+23−4
helix-term/src/ui/editor.rs+2−4
@@ -311,8 +311,7 @@ impl EditorView {
311311 if LineEnding::from_rope_slice(&grapheme).is_some() {
312312 if !out_of_bounds {
313313 let style = spans.iter().fold(text_style, |acc, span| {
314− let style = theme.get(theme.scopes()[span.0].as_str());
315− acc.patch(style)
314+ acc.patch(theme.highlight(span.0))
316315 });
317316
318317 // we still want to render an empty cell with the style
@@ -346,8 +345,7 @@ impl EditorView {
346345
347346 if !out_of_bounds {
348347 let style = spans.iter().fold(text_style, |acc, span| {
349− let style = theme.get(theme.scopes()[span.0].as_str());
350− acc.patch(style)
348+ acc.patch(theme.highlight(span.0))
351349 });
352350
353351 // if we're offscreen just keep going until we hit a new line
helix-view/src/theme.rs+23−4
@@ -78,8 +78,11 @@ impl Loader {
7878
7979#[derive(Clone, Debug)]
8080pub struct Theme {
81− scopes: Vec<String>,
81+ // UI styles are stored in a HashMap
8282 styles: HashMap<String, Style>,
83+ // tree-sitter highlight styles are stored in a Vec to optimize lookups
84+ scopes: Vec<String>,
85+ highlights: Vec<Style>,
8386}
8487
8588impl<'de> Deserialize<'de> for Theme {
@@ -88,6 +91,8 @@ impl<'de> Deserialize<'de> for Theme {
8891 D: Deserializer<'de>,
8992 {
9093 let mut styles = HashMap::new();
94+ let mut scopes = Vec::new();
95+ let mut highlights = Vec::new();
9196
9297 if let Ok(mut colors) = HashMap::<String, Value>::deserialize(deserializer) {
9398 // TODO: alert user of parsing failures in editor
@@ -102,21 +107,35 @@ impl<'de> Deserialize<'de> for Theme {
102107 .unwrap_or_default();
103108
104109 styles.reserve(colors.len());
110+ scopes.reserve(colors.len());
111+ highlights.reserve(colors.len());
112+
105113 for (name, style_value) in colors {
106114 let mut style = Style::default();
107115 if let Err(err) = palette.parse_style(&mut style, style_value) {
108116 warn!("{}", err);
109117 }
110− styles.insert(name, style);
118+
119+ // these are used both as UI and as highlights
120+ styles.insert(name.clone(), style);
121+ scopes.push(name);
122+ highlights.push(style);
111123 }
112124 }
113125
114− let scopes = styles.keys().map(ToString::to_string).collect();
115− Ok(Self { scopes, styles })
126+ Ok(Self {
127+ scopes,
128+ styles,
129+ highlights,
130+ })
116131 }
117132}
118133
119134impl Theme {
135+ pub fn highlight(&self, index: usize) -> Style {
136+ self.highlights[index]
137+ }
138+
120139 pub fn get(&self, scope: &str) -> Style {
121140 self.try_get(scope)
122141 .unwrap_or_else(|| Style::default().fg(Color::Rgb(0, 0, 255)))