Skip to content

Commit eb2aafc

Browse files
committed
feat: Use ThemeKey everywhere when getting Styles
This also means we can avoid allocating `String`s in a lot of cases but means we allocate a `Vec` more often. Comparing the perf difference is hard but I have not seen Helix slow down because of this.
1 parent 73497db commit eb2aafc

File tree

17 files changed

+174
-138
lines changed

17 files changed

+174
-138
lines changed

helix-core/src/syntax.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,10 @@ pub fn read_query(language: &str, filename: &str) -> String {
352352
}
353353

354354
impl LanguageConfiguration {
355-
fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
355+
fn initialize_highlight<T: AsRef<str>>(
356+
&self,
357+
scopes: &[T],
358+
) -> Option<Arc<HighlightConfiguration>> {
356359
let language = self.language_id.to_ascii_lowercase();
357360

358361
let highlights_query = read_query(&language, "highlights.scm");
@@ -382,13 +385,16 @@ impl LanguageConfiguration {
382385
}
383386
}
384387

385-
pub fn reconfigure(&self, scopes: &[String]) {
388+
pub fn reconfigure(&self, scopes: &[&str]) {
386389
if let Some(Some(config)) = self.highlight_config.get() {
387390
config.configure(scopes);
388391
}
389392
}
390393

391-
pub fn highlight_config(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
394+
pub fn highlight_config<T: AsRef<str>>(
395+
&self,
396+
scopes: &[T],
397+
) -> Option<Arc<HighlightConfiguration>> {
392398
self.highlight_config
393399
.get_or_init(|| self.initialize_highlight(scopes))
394400
.clone()
@@ -546,16 +552,17 @@ impl Loader {
546552
self.language_configs.iter()
547553
}
548554

549-
pub fn set_scopes(&self, scopes: Vec<String>) {
550-
self.scopes.store(Arc::new(scopes));
555+
pub fn set_scopes(&self, scopes: &[&str]) {
556+
self.scopes
557+
.store(Arc::new(scopes.iter().map(ToString::to_string).collect()));
551558

552559
// Reconfigure existing grammars
553560
for config in self
554561
.language_configs
555562
.iter()
556563
.filter(|cfg| cfg.is_highlight_initialized())
557564
{
558-
config.reconfigure(&self.scopes());
565+
config.reconfigure(scopes);
559566
}
560567
}
561568

@@ -1361,7 +1368,7 @@ impl HighlightConfiguration {
13611368
///
13621369
/// When highlighting, results are returned as `Highlight` values, which contain the index
13631370
/// of the matched highlight this list of highlight names.
1364-
pub fn configure(&self, recognized_names: &[String]) {
1371+
pub fn configure<T: AsRef<str>>(&self, recognized_names: &[T]) {
13651372
let mut capture_parts = Vec::new();
13661373
let indices: Vec<_> = self
13671374
.query
@@ -1377,7 +1384,7 @@ impl HighlightConfiguration {
13771384
let recognized_name = recognized_name;
13781385
let mut len = 0;
13791386
let mut matches = true;
1380-
for (i, part) in recognized_name.split('.').enumerate() {
1387+
for (i, part) in recognized_name.as_ref().split('.').enumerate() {
13811388
match capture_parts.get(i) {
13821389
Some(capture_part) if *capture_part == part => len += 1,
13831390
_ => {
@@ -2043,7 +2050,7 @@ mod test {
20432050

20442051
#[test]
20452052
fn test_parser() {
2046-
let highlight_names: Vec<String> = [
2053+
let highlight_names: &[&str] = &[
20472054
"attribute",
20482055
"constant",
20492056
"function.builtin",
@@ -2062,11 +2069,7 @@ mod test {
20622069
"variable",
20632070
"variable.builtin",
20642071
"variable.parameter",
2065-
]
2066-
.iter()
2067-
.cloned()
2068-
.map(String::from)
2069-
.collect();
2072+
];
20702073

20712074
let loader = Loader::new(Configuration { language: vec![] });
20722075

@@ -2080,7 +2083,7 @@ mod test {
20802083
"", // locals.scm
20812084
)
20822085
.unwrap();
2083-
config.configure(&highlight_names);
2086+
config.configure(highlight_names);
20842087

20852088
let source = Rope::from_str(
20862089
"

helix-core/tests/indent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fn test_treesitter_indent(file_name: &str, lang_scope: &str) {
3838
std::env::set_var("HELIX_RUNTIME", runtime.to_str().unwrap());
3939

4040
let language_config = loader.language_config_for_scope(lang_scope).unwrap();
41-
let highlight_config = language_config.highlight_config(&[]).unwrap();
41+
let highlight_config = language_config.highlight_config::<String>(&[]).unwrap();
4242
let syntax = Syntax::new(&doc, highlight_config, std::sync::Arc::new(loader));
4343
let indent_query = language_config.indent_query().unwrap();
4444
let text = doc.slice(..);

helix-term/src/commands/dap.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use dap::{StackFrame, Thread, ThreadStates};
88
use helix_core::syntax::{DebugArgumentValue, DebugConfigCompletion, DebugTemplate};
99
use helix_dap::{self as dap, Client};
1010
use helix_lsp::block_on;
11-
use helix_view::editor::Breakpoint;
11+
use helix_view::{editor::Breakpoint, theme::ThemeKey};
1212

1313
use serde_json::{to_value, Value};
1414
use tokio_stream::wrappers::UnboundedReceiverStream;
@@ -495,9 +495,9 @@ pub fn dap_variables(cx: &mut Context) {
495495
let mut variables = Vec::new();
496496

497497
let theme = &cx.editor.theme;
498-
let scope_style = theme.get("ui.linenr.selected");
499-
let type_style = theme.get("ui.text");
500-
let text_style = theme.get("ui.text.focus");
498+
let scope_style = theme.get(ThemeKey::UiLinenrSelected);
499+
let type_style = theme.get(ThemeKey::UiText);
500+
let text_style = theme.get(ThemeKey::UiTextFocus);
501501

502502
for scope in scopes.iter() {
503503
// use helix_view::graphics::Style;

helix-term/src/commands/lsp.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use tui::text::{Span, Spans};
99
use super::{align_view, push_jump, Align, Context, Editor, Open};
1010

1111
use helix_core::{path, Selection};
12-
use helix_view::{editor::Action, theme::Style};
12+
use helix_view::{
13+
editor::Action,
14+
theme::{Style, ThemeKey},
15+
};
1316

1417
use crate::{
1518
compositor::{self, Compositor},
@@ -268,10 +271,10 @@ fn diag_picker(
268271
}
269272

270273
let styles = DiagnosticStyles {
271-
hint: cx.editor.theme.get("hint"),
272-
info: cx.editor.theme.get("info"),
273-
warning: cx.editor.theme.get("warning"),
274-
error: cx.editor.theme.get("error"),
274+
hint: cx.editor.theme.get(ThemeKey::Hint),
275+
info: cx.editor.theme.get(ThemeKey::Info),
276+
warning: cx.editor.theme.get(ThemeKey::Warning),
277+
error: cx.editor.theme.get(ThemeKey::Error),
275278
};
276279

277280
FilePicker::new(

helix-term/src/ui/completion.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::compositor::{Component, Context, Event, EventResult};
2-
use helix_view::editor::CompleteAction;
2+
use helix_view::{editor::CompleteAction, theme::ThemeKey};
33
use tui::buffer::Buffer as Surface;
44
use tui::text::Spans;
55

@@ -411,7 +411,7 @@ impl Component for Completion {
411411
};
412412

413413
// clear area
414-
let background = cx.editor.theme.get("ui.popup");
414+
let background = cx.editor.theme.get(ThemeKey::UiPopup);
415415
surface.clear_with(area, background);
416416
markdown_doc.render(area, surface, cx);
417417
}

helix-term/src/ui/editor.rs

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use helix_view::{
2121
graphics::{Color, CursorKind, Modifier, Rect, Style},
2222
input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind},
2323
keyboard::{KeyCode, KeyModifiers},
24+
theme::ThemeKey,
2425
Document, Editor, Theme, View,
2526
};
2627
use std::{borrow::Cow, path::PathBuf};
@@ -107,7 +108,7 @@ impl EditorView {
107108
area.width,
108109
1,
109110
),
110-
theme.get("ui.highlight"),
111+
theme.get(ThemeKey::UiHighlight),
111112
);
112113
}
113114
}
@@ -153,7 +154,7 @@ impl EditorView {
153154
// if we're not at the edge of the screen, draw a right border
154155
if viewport.right() != view.area.right() {
155156
let x = area.right();
156-
let border_style = theme.get("ui.window");
157+
let border_style = theme.get(ThemeKey::UiWindow);
157158
for y in area.top()..area.bottom() {
158159
surface[(x, y)]
159160
.set_symbol(tui::symbols::line::VERTICAL)
@@ -185,7 +186,7 @@ impl EditorView {
185186
) {
186187
let editor_rulers = &editor.config().rulers;
187188
let ruler_theme = theme
188-
.try_get("ui.virtual.ruler")
189+
.try_get(ThemeKey::UiVirtualRuler)
189190
.unwrap_or_else(|| Style::default().bg(Color::Red));
190191

191192
let rulers = doc
@@ -268,20 +269,20 @@ impl EditorView {
268269
theme
269270
.find_scope_index(scope)
270271
// get one of the themes below as fallback values
271-
.or_else(|| theme.find_scope_index("diagnostic"))
272-
.or_else(|| theme.find_scope_index("ui.cursor"))
273-
.or_else(|| theme.find_scope_index("ui.selection"))
272+
.or_else(|| theme.find_scope_index(ThemeKey::Diagnostic))
273+
.or_else(|| theme.find_scope_index(ThemeKey::UiCursor))
274+
.or_else(|| theme.find_scope_index(ThemeKey::UiSelection))
274275
.expect(
275276
"at least one of the following scopes must be defined in the theme: `diagnostic`, `ui.cursor`, or `ui.selection`",
276277
)
277278
};
278279

279280
// basically just queries the theme color defined in the config
280-
let hint = get_scope_of("diagnostic.hint");
281-
let info = get_scope_of("diagnostic.info");
282-
let warning = get_scope_of("diagnostic.warning");
283-
let error = get_scope_of("diagnostic.error");
284-
let r#default = get_scope_of("diagnostic"); // this is a bit redundant but should be fine
281+
let hint = get_scope_of(ThemeKey::DiagnosticHint);
282+
let info = get_scope_of(ThemeKey::DiagnosticInfo);
283+
let warning = get_scope_of(ThemeKey::DiagnosticWarning);
284+
let error = get_scope_of(ThemeKey::DiagnosticError);
285+
let r#default = get_scope_of(ThemeKey::Diagnostic); // this is a bit redundant but should be fine
285286

286287
doc.diagnostics()
287288
.iter()
@@ -317,24 +318,24 @@ impl EditorView {
317318
let cursor_is_block = cursorkind == CursorKind::Block;
318319

319320
let selection_scope = theme
320-
.find_scope_index("ui.selection")
321+
.find_scope_index(ThemeKey::UiSelection)
321322
.expect("could not find `ui.selection` scope in the theme!");
322323
let base_cursor_scope = theme
323-
.find_scope_index("ui.cursor")
324+
.find_scope_index(ThemeKey::UiCursor)
324325
.unwrap_or(selection_scope);
325326

326327
let cursor_scope = match mode {
327-
Mode::Insert => theme.find_scope_index("ui.cursor.insert"),
328-
Mode::Select => theme.find_scope_index("ui.cursor.select"),
328+
Mode::Insert => theme.find_scope_index(ThemeKey::UiCursorInsert),
329+
Mode::Select => theme.find_scope_index(ThemeKey::UiCursorSelect),
329330
Mode::Normal => Some(base_cursor_scope),
330331
}
331332
.unwrap_or(base_cursor_scope);
332333

333334
let primary_cursor_scope = theme
334-
.find_scope_index("ui.cursor.primary")
335+
.find_scope_index(ThemeKey::UiCursorPrimary)
335336
.unwrap_or(cursor_scope);
336337
let primary_selection_scope = theme
337-
.find_scope_index("ui.selection.primary")
338+
.find_scope_index(ThemeKey::UiSelectionPrimary)
338339
.unwrap_or(selection_scope);
339340

340341
let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
@@ -418,17 +419,17 @@ impl EditorView {
418419
};
419420
let indent_guide_char = config.indent_guides.character.to_string();
420421

421-
let text_style = theme.get("ui.text");
422-
let whitespace_style = theme.get("ui.virtual.whitespace");
422+
let text_style = theme.get(ThemeKey::UiText);
423+
let whitespace_style = theme.get(ThemeKey::UiVirtualWhitespace);
423424

424425
let mut is_in_indent_area = true;
425426
let mut last_line_indent_level = 0;
426427

427428
// use whitespace style as fallback for indent-guide
428429
let indent_guide_style = text_style.patch(
429430
theme
430-
.try_get("ui.virtual.indent-guide")
431-
.unwrap_or_else(|| theme.get("ui.virtual.whitespace")),
431+
.try_get(ThemeKey::UiVirtualIndentguide)
432+
.unwrap_or_else(|| theme.get(ThemeKey::UiVirtualWhitespace)),
432433
);
433434

434435
let draw_indent_guides = |indent_level, line, surface: &mut Surface| {
@@ -606,7 +607,7 @@ impl EditorView {
606607
if (pos.col as u16) < viewport.width + view.offset.col as u16
607608
&& pos.col >= view.offset.col
608609
{
609-
let style = theme.try_get("ui.cursor.match").unwrap_or_else(|| {
610+
let style = theme.try_get(ThemeKey::UiCursorMatch).unwrap_or_else(|| {
610611
Style::default()
611612
.add_modifier(Modifier::REVERSED)
612613
.add_modifier(Modifier::DIM)
@@ -626,19 +627,19 @@ impl EditorView {
626627
viewport,
627628
editor
628629
.theme
629-
.try_get("ui.bufferline.background")
630-
.unwrap_or_else(|| editor.theme.get("ui.statusline")),
630+
.try_get(ThemeKey::UiBufferlineBackground)
631+
.unwrap_or_else(|| editor.theme.get(ThemeKey::UiStatusline)),
631632
);
632633

633634
let bufferline_active = editor
634635
.theme
635-
.try_get("ui.bufferline.active")
636-
.unwrap_or_else(|| editor.theme.get("ui.statusline.active"));
636+
.try_get(ThemeKey::UiBufferlineActive)
637+
.unwrap_or_else(|| editor.theme.get(ThemeKey::UiStatuslineActive));
637638

638639
let bufferline_inactive = editor
639640
.theme
640-
.try_get("ui.bufferline")
641-
.unwrap_or_else(|| editor.theme.get("ui.statusline.inactive"));
641+
.try_get(ThemeKey::UiBufferline)
642+
.unwrap_or_else(|| editor.theme.get(ThemeKey::UiStatuslineInactive));
642643

643644
let mut x = viewport.x;
644645
let current_doc = view!(editor).doc;
@@ -695,7 +696,7 @@ impl EditorView {
695696

696697
let mut offset = 0;
697698

698-
let gutter_style = theme.get("ui.gutter");
699+
let gutter_style = theme.get(ThemeKey::UiGutter);
699700

700701
// avoid lots of small allocations by reusing a text buffer for each line
701702
let mut text = String::with_capacity(8);
@@ -752,13 +753,13 @@ impl EditorView {
752753
diagnostic.range.start <= cursor && diagnostic.range.end >= cursor
753754
});
754755

755-
let warning = theme.get("warning");
756-
let error = theme.get("error");
757-
let info = theme.get("info");
758-
let hint = theme.get("hint");
756+
let warning = theme.get(ThemeKey::Warning);
757+
let error = theme.get(ThemeKey::Error);
758+
let info = theme.get(ThemeKey::Info);
759+
let hint = theme.get(ThemeKey::Hint);
759760

760761
let mut lines = Vec::new();
761-
let background_style = theme.get("ui.background");
762+
let background_style = theme.get(ThemeKey::UiBackground);
762763
for diagnostic in diagnostics {
763764
let style = Style::reset()
764765
.patch(background_style)
@@ -802,8 +803,8 @@ impl EditorView {
802803
.map(|range| range.cursor_line(text))
803804
.collect();
804805

805-
let primary_style = theme.get("ui.cursorline.primary");
806-
let secondary_style = theme.get("ui.cursorline.secondary");
806+
let primary_style = theme.get(ThemeKey::UiCursorlinePrimary);
807+
let secondary_style = theme.get(ThemeKey::UiCursorlineSecondary);
807808

808809
for line in view.offset.row..(last_line + 1) {
809810
let area = Rect::new(
@@ -1342,7 +1343,7 @@ impl Component for EditorView {
13421343

13431344
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
13441345
// clear with background color
1345-
surface.set_style(area, cx.editor.theme.get("ui.background"));
1346+
surface.set_style(area, cx.editor.theme.get(ThemeKey::UiBackground));
13461347
let config = cx.editor.config();
13471348

13481349
// check if bufferline should be rendered
@@ -1386,9 +1387,9 @@ impl Component for EditorView {
13861387
status_msg_width = status_msg.width();
13871388
use helix_view::editor::Severity;
13881389
let style = if *severity == Severity::Error {
1389-
cx.editor.theme.get("error")
1390+
cx.editor.theme.get(ThemeKey::Error)
13901391
} else {
1391-
cx.editor.theme.get("ui.text")
1392+
cx.editor.theme.get(ThemeKey::UiText)
13921393
};
13931394

13941395
surface.set_string(
@@ -1410,7 +1411,7 @@ impl Component for EditorView {
14101411
if let Some(pseudo_pending) = &cx.editor.pseudo_pending {
14111412
disp.push_str(pseudo_pending.as_str())
14121413
}
1413-
let style = cx.editor.theme.get("ui.text");
1414+
let style = cx.editor.theme.get(ThemeKey::UiText);
14141415
let macro_width = if cx.editor.macro_recording.is_some() {
14151416
3
14161417
} else {

0 commit comments

Comments
 (0)