Skip to content

Commit 3eb6c5a

Browse files
chtenbmtoohey31
authored andcommitted
Fix precedence of ui.virtual.whitespace (helix-editor#8750)
1 parent 02d8583 commit 3eb6c5a

File tree

3 files changed

+125
-65
lines changed

3 files changed

+125
-65
lines changed

helix-term/src/ui/document.rs

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ pub fn render_document(
9797
doc: &Document,
9898
offset: ViewPosition,
9999
doc_annotations: &TextAnnotations,
100-
highlight_iter: impl Iterator<Item = HighlightEvent>,
100+
syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
101+
overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
101102
theme: &Theme,
102103
line_decoration: &mut [Box<dyn LineDecoration + '_>],
103104
translated_positions: &mut [TranslatedPosition],
@@ -109,7 +110,8 @@ pub fn render_document(
109110
offset,
110111
&doc.text_format(viewport.width, Some(theme)),
111112
doc_annotations,
112-
highlight_iter,
113+
syntax_highlight_iter,
114+
overlay_highlight_iter,
113115
theme,
114116
line_decoration,
115117
translated_positions,
@@ -157,7 +159,8 @@ pub fn render_text<'t>(
157159
offset: ViewPosition,
158160
text_fmt: &TextFormat,
159161
text_annotations: &TextAnnotations,
160-
highlight_iter: impl Iterator<Item = HighlightEvent>,
162+
syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
163+
overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
161164
theme: &Theme,
162165
line_decorations: &mut [Box<dyn LineDecoration + '_>],
163166
translated_positions: &mut [TranslatedPosition],
@@ -178,10 +181,16 @@ pub fn render_text<'t>(
178181

179182
let (mut formatter, mut first_visible_char_idx) =
180183
DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, text_annotations, offset.anchor);
181-
let mut styles = StyleIter {
184+
let mut syntax_styles = StyleIter {
182185
text_style: renderer.text_style,
183186
active_highlights: Vec::with_capacity(64),
184-
highlight_iter,
187+
highlight_iter: syntax_highlight_iter,
188+
theme,
189+
};
190+
let mut overlay_styles = StyleIter {
191+
text_style: renderer.text_style,
192+
active_highlights: Vec::with_capacity(64),
193+
highlight_iter: overlay_highlight_iter,
185194
theme,
186195
};
187196

@@ -193,7 +202,10 @@ pub fn render_text<'t>(
193202
};
194203
let mut is_in_indent_area = true;
195204
let mut last_line_indent_level = 0;
196-
let mut style_span = styles
205+
let mut syntax_style_span = syntax_styles
206+
.next()
207+
.unwrap_or_else(|| (Style::default(), usize::MAX));
208+
let mut overlay_style_span = overlay_styles
197209
.next()
198210
.unwrap_or_else(|| (Style::default(), usize::MAX));
199211

@@ -221,9 +233,16 @@ pub fn render_text<'t>(
221233

222234
// skip any graphemes on visual lines before the block start
223235
if pos.row < row_off {
224-
if char_pos >= style_span.1 {
225-
style_span = if let Some(style_span) = styles.next() {
226-
style_span
236+
if char_pos >= syntax_style_span.1 {
237+
syntax_style_span = if let Some(syntax_style_span) = syntax_styles.next() {
238+
syntax_style_span
239+
} else {
240+
break;
241+
}
242+
}
243+
if char_pos >= overlay_style_span.1 {
244+
overlay_style_span = if let Some(overlay_style_span) = overlay_styles.next() {
245+
overlay_style_span
227246
} else {
228247
break;
229248
}
@@ -260,8 +279,15 @@ pub fn render_text<'t>(
260279
}
261280

262281
// acquire the correct grapheme style
263-
if char_pos >= style_span.1 {
264-
style_span = styles.next().unwrap_or((Style::default(), usize::MAX));
282+
if char_pos >= syntax_style_span.1 {
283+
syntax_style_span = syntax_styles
284+
.next()
285+
.unwrap_or((Style::default(), usize::MAX));
286+
}
287+
if char_pos >= overlay_style_span.1 {
288+
overlay_style_span = overlay_styles
289+
.next()
290+
.unwrap_or((Style::default(), usize::MAX));
265291
}
266292
char_pos += grapheme.doc_chars();
267293

@@ -275,22 +301,25 @@ pub fn render_text<'t>(
275301
pos,
276302
);
277303

278-
let grapheme_style = if let GraphemeSource::VirtualText { highlight } = grapheme.source {
279-
let style = renderer.text_style;
280-
if let Some(highlight) = highlight {
281-
style.patch(theme.highlight(highlight.0))
304+
let (syntax_style, overlay_style) =
305+
if let GraphemeSource::VirtualText { highlight } = grapheme.source {
306+
let mut style = renderer.text_style;
307+
if let Some(highlight) = highlight {
308+
style = style.patch(theme.highlight(highlight.0))
309+
}
310+
(style, Style::default())
282311
} else {
283-
style
284-
}
285-
} else {
286-
style_span.0
287-
};
312+
(syntax_style_span.0, overlay_style_span.0)
313+
};
288314

289-
let virt = grapheme.is_virtual();
315+
let is_virtual = grapheme.is_virtual();
290316
renderer.draw_grapheme(
291317
grapheme.grapheme,
292-
grapheme_style,
293-
virt,
318+
GraphemeStyle {
319+
syntax_style,
320+
overlay_style,
321+
},
322+
is_virtual,
294323
&mut last_line_indent_level,
295324
&mut is_in_indent_area,
296325
pos,
@@ -322,6 +351,11 @@ pub struct TextRenderer<'a> {
322351
pub viewport: Rect,
323352
}
324353

354+
pub struct GraphemeStyle {
355+
syntax_style: Style,
356+
overlay_style: Style,
357+
}
358+
325359
impl<'a> TextRenderer<'a> {
326360
pub fn new(
327361
surface: &'a mut Surface,
@@ -395,7 +429,7 @@ impl<'a> TextRenderer<'a> {
395429
pub fn draw_grapheme(
396430
&mut self,
397431
grapheme: Grapheme,
398-
mut style: Style,
432+
grapheme_style: GraphemeStyle,
399433
is_virtual: bool,
400434
last_indent_level: &mut usize,
401435
is_in_indent_area: &mut bool,
@@ -405,9 +439,11 @@ impl<'a> TextRenderer<'a> {
405439
let is_whitespace = grapheme.is_whitespace();
406440

407441
// TODO is it correct to apply the whitespace style to all unicode white spaces?
442+
let mut style = grapheme_style.syntax_style;
408443
if is_whitespace {
409444
style = style.patch(self.whitespace_style);
410445
}
446+
style = style.patch(grapheme_style.overlay_style);
411447

412448
let width = grapheme.width();
413449
let space = if is_virtual { " " } else { &self.space };

helix-term/src/ui/editor.rs

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,20 @@ impl EditorView {
124124
line_decorations.push(Box::new(line_decoration));
125125
}
126126

127-
let mut highlights =
127+
let syntax_highlights =
128128
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
129-
let overlay_highlights = Self::overlay_syntax_highlights(
129+
130+
let mut overlay_highlights =
131+
Self::empty_highlight_iter(doc, view.offset.anchor, inner.height);
132+
let overlay_syntax_highlights = Self::overlay_syntax_highlights(
130133
doc,
131134
view.offset.anchor,
132135
inner.height,
133136
&text_annotations,
134137
);
135-
if !overlay_highlights.is_empty() {
136-
highlights = Box::new(syntax::merge(highlights, overlay_highlights));
138+
if !overlay_syntax_highlights.is_empty() {
139+
overlay_highlights =
140+
Box::new(syntax::merge(overlay_highlights, overlay_syntax_highlights));
137141
}
138142

139143
for diagnostic in Self::doc_diagnostics_highlights(doc, theme) {
@@ -142,12 +146,12 @@ impl EditorView {
142146
if diagnostic.is_empty() {
143147
continue;
144148
}
145-
highlights = Box::new(syntax::merge(highlights, diagnostic));
149+
overlay_highlights = Box::new(syntax::merge(overlay_highlights, diagnostic));
146150
}
147151

148-
let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
152+
if is_focused {
149153
let highlights = syntax::merge(
150-
highlights,
154+
overlay_highlights,
151155
Self::doc_selection_highlights(
152156
editor.mode(),
153157
doc,
@@ -158,13 +162,11 @@ impl EditorView {
158162
);
159163
let focused_view_elements = Self::highlight_focused_view_elements(view, doc, theme);
160164
if focused_view_elements.is_empty() {
161-
Box::new(highlights)
165+
overlay_highlights = Box::new(highlights)
162166
} else {
163-
Box::new(syntax::merge(highlights, focused_view_elements))
167+
overlay_highlights = Box::new(syntax::merge(highlights, focused_view_elements))
164168
}
165-
} else {
166-
Box::new(highlights)
167-
};
169+
}
168170

169171
let gutter_overflow = view.gutter_offset(doc) == 0;
170172
if !gutter_overflow {
@@ -197,7 +199,8 @@ impl EditorView {
197199
doc,
198200
view.offset,
199201
&text_annotations,
200-
highlights,
202+
syntax_highlights,
203+
overlay_highlights,
201204
theme,
202205
&mut line_decorations,
203206
&mut translated_positions,
@@ -257,27 +260,39 @@ impl EditorView {
257260
.for_each(|area| surface.set_style(area, ruler_theme))
258261
}
259262

260-
pub fn overlay_syntax_highlights(
263+
fn viewport_byte_range(
264+
text: helix_core::RopeSlice,
265+
row: usize,
266+
height: u16,
267+
) -> std::ops::Range<usize> {
268+
// Calculate viewport byte ranges:
269+
// Saturating subs to make it inclusive zero indexing.
270+
let last_line = text.len_lines().saturating_sub(1);
271+
let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
272+
let start = text.line_to_byte(row.min(last_line));
273+
let end = text.line_to_byte(last_visible_line + 1);
274+
275+
start..end
276+
}
277+
278+
pub fn empty_highlight_iter(
261279
doc: &Document,
262280
anchor: usize,
263281
height: u16,
264-
text_annotations: &TextAnnotations,
265-
) -> Vec<(usize, std::ops::Range<usize>)> {
282+
) -> Box<dyn Iterator<Item = HighlightEvent>> {
266283
let text = doc.text().slice(..);
267284
let row = text.char_to_line(anchor.min(text.len_chars()));
268285

269-
let range = {
270-
// Calculate viewport byte ranges:
271-
// Saturating subs to make it inclusive zero indexing.
272-
let last_line = text.len_lines().saturating_sub(1);
273-
let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
274-
let start = text.line_to_byte(row.min(last_line));
275-
let end = text.line_to_byte(last_visible_line + 1);
276-
277-
start..end
278-
};
279-
280-
text_annotations.collect_overlay_highlights(range)
286+
// Calculate viewport byte ranges:
287+
// Saturating subs to make it inclusive zero indexing.
288+
let range = Self::viewport_byte_range(text, row, height);
289+
Box::new(
290+
[HighlightEvent::Source {
291+
start: text.byte_to_char(range.start),
292+
end: text.byte_to_char(range.end),
293+
}]
294+
.into_iter(),
295+
)
281296
}
282297

283298
/// Get syntax highlights for a document in a view represented by the first line
@@ -292,16 +307,7 @@ impl EditorView {
292307
let text = doc.text().slice(..);
293308
let row = text.char_to_line(anchor.min(text.len_chars()));
294309

295-
let range = {
296-
// Calculate viewport byte ranges:
297-
// Saturating subs to make it inclusive zero indexing.
298-
let last_line = text.len_lines().saturating_sub(1);
299-
let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
300-
let start = text.line_to_byte(row.min(last_line));
301-
let end = text.line_to_byte(last_visible_line + 1);
302-
303-
start..end
304-
};
310+
let range = Self::viewport_byte_range(text, row, height);
305311

306312
match doc.syntax() {
307313
Some(syntax) => {
@@ -334,6 +340,20 @@ impl EditorView {
334340
}
335341
}
336342

343+
pub fn overlay_syntax_highlights(
344+
doc: &Document,
345+
anchor: usize,
346+
height: u16,
347+
text_annotations: &TextAnnotations,
348+
) -> Vec<(usize, std::ops::Range<usize>)> {
349+
let text = doc.text().slice(..);
350+
let row = text.char_to_line(anchor.min(text.len_chars()));
351+
352+
let range = Self::viewport_byte_range(text, row, height);
353+
354+
text_annotations.collect_overlay_highlights(range)
355+
}
356+
337357
/// Get highlight spans for document diagnostics
338358
pub fn doc_diagnostics_highlights(
339359
doc: &Document,

helix-term/src/ui/picker.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -736,17 +736,20 @@ impl<T: Item + 'static> Picker<T> {
736736
}
737737
}
738738

739-
let mut highlights = EditorView::doc_syntax_highlights(
739+
let syntax_highlights = EditorView::doc_syntax_highlights(
740740
doc,
741741
offset.anchor,
742742
area.height,
743743
&cx.editor.theme,
744744
);
745+
746+
let mut overlay_highlights =
747+
EditorView::empty_highlight_iter(doc, offset.anchor, area.height);
745748
for spans in EditorView::doc_diagnostics_highlights(doc, &cx.editor.theme) {
746749
if spans.is_empty() {
747750
continue;
748751
}
749-
highlights = Box::new(helix_core::syntax::merge(highlights, spans));
752+
overlay_highlights = Box::new(helix_core::syntax::merge(overlay_highlights, spans));
750753
}
751754
let mut decorations: Vec<Box<dyn LineDecoration>> = Vec::new();
752755

@@ -777,7 +780,8 @@ impl<T: Item + 'static> Picker<T> {
777780
offset,
778781
// TODO: compute text annotations asynchronously here (like inlay hints)
779782
&TextAnnotations::default(),
780-
highlights,
783+
syntax_highlights,
784+
overlay_highlights,
781785
&cx.editor.theme,
782786
&mut decorations,
783787
&mut [],

0 commit comments

Comments
 (0)