on master help sign in
ui: Optimize tree-sitter style lookups
2596785Blaž Hrastnik
helix-term/src/ui/editor.rs+2−4
@@ -311,8 +311,7 @@ impl EditorView {
311 if LineEnding::from_rope_slice(&grapheme).is_some() {
311 if LineEnding::from_rope_slice(&grapheme).is_some() {
312 if !out_of_bounds {
312 if !out_of_bounds {
313 let style = spans.iter().fold(text_style, |acc, span| {
313 let style = spans.iter().fold(text_style, |acc, span| {
314− let style = theme.get(theme.scopes()[span.0].as_str());
314+ acc.patch(theme.highlight(span.0))
315− acc.patch(style)
316 });
315 });
317
316
318 // we still want to render an empty cell with the style
317 // we still want to render an empty cell with the style
@@ -346,8 +345,7 @@ impl EditorView {
346
345
347 if !out_of_bounds {
346 if !out_of_bounds {
348 let style = spans.iter().fold(text_style, |acc, span| {
347 let style = spans.iter().fold(text_style, |acc, span| {
349− let style = theme.get(theme.scopes()[span.0].as_str());
348+ acc.patch(theme.highlight(span.0))
350− acc.patch(style)
351 });
349 });
352
350
353 // if we're offscreen just keep going until we hit a new line
351 // 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 {
78
78
79#[derive(Clone, Debug)]
79#[derive(Clone, Debug)]
80pub struct Theme {
80pub struct Theme {
81− scopes: Vec<String>,
81+ // UI styles are stored in a HashMap
82 styles: HashMap<String, Style>,
82 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>,
83}
86}
84
87
85impl<'de> Deserialize<'de> for Theme {
88impl<'de> Deserialize<'de> for Theme {
@@ -88,6 +91,8 @@ impl<'de> Deserialize<'de> for Theme {
88 D: Deserializer<'de>,
91 D: Deserializer<'de>,
89 {
92 {
90 let mut styles = HashMap::new();
93 let mut styles = HashMap::new();
94+ let mut scopes = Vec::new();
95+ let mut highlights = Vec::new();
91
96
92 if let Ok(mut colors) = HashMap::<String, Value>::deserialize(deserializer) {
97 if let Ok(mut colors) = HashMap::<String, Value>::deserialize(deserializer) {
93 // TODO: alert user of parsing failures in editor
98 // TODO: alert user of parsing failures in editor
@@ -102,21 +107,35 @@ impl<'de> Deserialize<'de> for Theme {
102 .unwrap_or_default();
107 .unwrap_or_default();
103
108
104 styles.reserve(colors.len());
109 styles.reserve(colors.len());
110+ scopes.reserve(colors.len());
111+ highlights.reserve(colors.len());
112+
105 for (name, style_value) in colors {
113 for (name, style_value) in colors {
106 let mut style = Style::default();
114 let mut style = Style::default();
107 if let Err(err) = palette.parse_style(&mut style, style_value) {
115 if let Err(err) = palette.parse_style(&mut style, style_value) {
108 warn!("{}", err);
116 warn!("{}", err);
109 }
117 }
118+
119+ // these are used both as UI and as highlights
110− styles.insert(name, style);
120+ styles.insert(name.clone(), style);
121+ scopes.push(name);
122+ highlights.push(style);
111 }
123 }
112 }
124 }
113
125
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+ })
116 }
131 }
117}
132}
118
133
119impl Theme {
134impl Theme {
135+ pub fn highlight(&self, index: usize) -> Style {
136+ self.highlights[index]
137+ }
138+
120 pub fn get(&self, scope: &str) -> Style {
139 pub fn get(&self, scope: &str) -> Style {
121 self.try_get(scope)
140 self.try_get(scope)
122 .unwrap_or_else(|| Style::default().fg(Color::Rgb(0, 0, 255)))
141 .unwrap_or_else(|| Style::default().fg(Color::Rgb(0, 0, 255)))