on master help sign in
Fix precedence of ui.virtual.whitespace (#8750)
41b307bchtenb
helix-term/src/ui/document.rs+60−24
@@ -97,7 +97,8 @@ pub fn render_document(
97 doc: &Document,
97 doc: &Document,
98 offset: ViewPosition,
98 offset: ViewPosition,
99 doc_annotations: &TextAnnotations,
99 doc_annotations: &TextAnnotations,
100+ syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
100− highlight_iter: impl Iterator<Item = HighlightEvent>,
101+ overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
101 theme: &Theme,
102 theme: &Theme,
102 line_decoration: &mut [Box<dyn LineDecoration + '_>],
103 line_decoration: &mut [Box<dyn LineDecoration + '_>],
103 translated_positions: &mut [TranslatedPosition],
104 translated_positions: &mut [TranslatedPosition],
@@ -109,7 +110,8 @@ pub fn render_document(
109 offset,
110 offset,
110 &doc.text_format(viewport.width, Some(theme)),
111 &doc.text_format(viewport.width, Some(theme)),
111 doc_annotations,
112 doc_annotations,
112− highlight_iter,
113+ syntax_highlight_iter,
114+ overlay_highlight_iter,
113 theme,
115 theme,
114 line_decoration,
116 line_decoration,
115 translated_positions,
117 translated_positions,
@@ -157,7 +159,8 @@ pub fn render_text<'t>(
157 offset: ViewPosition,
159 offset: ViewPosition,
158 text_fmt: &TextFormat,
160 text_fmt: &TextFormat,
159 text_annotations: &TextAnnotations,
161 text_annotations: &TextAnnotations,
162+ syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
160− highlight_iter: impl Iterator<Item = HighlightEvent>,
163+ overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
161 theme: &Theme,
164 theme: &Theme,
162 line_decorations: &mut [Box<dyn LineDecoration + '_>],
165 line_decorations: &mut [Box<dyn LineDecoration + '_>],
163 translated_positions: &mut [TranslatedPosition],
166 translated_positions: &mut [TranslatedPosition],
@@ -178,10 +181,16 @@ pub fn render_text<'t>(
178
181
179 let (mut formatter, mut first_visible_char_idx) =
182 let (mut formatter, mut first_visible_char_idx) =
180 DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, text_annotations, offset.anchor);
183 DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, text_annotations, offset.anchor);
181− let mut styles = StyleIter {
184+ let mut syntax_styles = StyleIter {
182 text_style: renderer.text_style,
185 text_style: renderer.text_style,
183 active_highlights: Vec::with_capacity(64),
186 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,
185 theme,
194 theme,
186 };
195 };
187
196
@@ -193,7 +202,10 @@ pub fn render_text<'t>(
193 };
202 };
194 let mut is_in_indent_area = true;
203 let mut is_in_indent_area = true;
195 let mut last_line_indent_level = 0;
204 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
197 .next()
209 .next()
198 .unwrap_or_else(|| (Style::default(), usize::MAX));
210 .unwrap_or_else(|| (Style::default(), usize::MAX));
199
211
@@ -221,9 +233,16 @@ pub fn render_text<'t>(
221
233
222 // skip any graphemes on visual lines before the block start
234 // skip any graphemes on visual lines before the block start
223 if pos.row < row_off {
235 if pos.row < row_off {
224− if char_pos >= style_span.1 {
236+ if char_pos >= syntax_style_span.1 {
225− style_span = if let Some(style_span) = styles.next() {
237+ syntax_style_span = if let Some(syntax_style_span) = syntax_styles.next() {
226− style_span
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
227 } else {
246 } else {
228 break;
247 break;
229 }
248 }
@@ -260,8 +279,15 @@ pub fn render_text<'t>(
260 }
279 }
261
280
262 // acquire the correct grapheme style
281 // acquire the correct grapheme style
263− if char_pos >= style_span.1 {
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()
264− style_span = styles.next().unwrap_or((Style::default(), usize::MAX));
290+ .unwrap_or((Style::default(), usize::MAX));
265 }
291 }
266 char_pos += grapheme.doc_chars();
292 char_pos += grapheme.doc_chars();
267
293
@@ -275,22 +301,25 @@ pub fn render_text<'t>(
275 pos,
301 pos,
276 );
302 );
277
303
304+ let (syntax_style, overlay_style) =
278− let grapheme_style = if let GraphemeSource::VirtualText { highlight } = grapheme.source {
305+ if let GraphemeSource::VirtualText { highlight } = grapheme.source {
279− let style = renderer.text_style;
306+ let mut style = renderer.text_style;
280− if let Some(highlight) = highlight {
307+ if let Some(highlight) = highlight {
281− style.patch(theme.highlight(highlight.0))
308+ style = style.patch(theme.highlight(highlight.0))
309+ }
310+ (style, Style::default())
282 } else {
311 } else {
283− style
312+ (syntax_style_span.0, overlay_style_span.0)
284− }
285− } else {
286− style_span.0
287− };
313+ };
288
314
289− let virt = grapheme.is_virtual();
315+ let is_virtual = grapheme.is_virtual();
290 renderer.draw_grapheme(
316 renderer.draw_grapheme(
291 grapheme.grapheme,
317 grapheme.grapheme,
292− grapheme_style,
318+ GraphemeStyle {
293− virt,
319+ syntax_style,
320+ overlay_style,
321+ },
322+ is_virtual,
294 &mut last_line_indent_level,
323 &mut last_line_indent_level,
295 &mut is_in_indent_area,
324 &mut is_in_indent_area,
296 pos,
325 pos,
@@ -322,6 +351,11 @@ pub struct TextRenderer<'a> {
322 pub viewport: Rect,
351 pub viewport: Rect,
323}
352}
324
353
354+pub struct GraphemeStyle {
355+ syntax_style: Style,
356+ overlay_style: Style,
357+}
358+
325impl<'a> TextRenderer<'a> {
359impl<'a> TextRenderer<'a> {
326 pub fn new(
360 pub fn new(
327 surface: &'a mut Surface,
361 surface: &'a mut Surface,
@@ -395,7 +429,7 @@ impl<'a> TextRenderer<'a> {
395 pub fn draw_grapheme(
429 pub fn draw_grapheme(
396 &mut self,
430 &mut self,
397 grapheme: Grapheme,
431 grapheme: Grapheme,
398− mut style: Style,
432+ grapheme_style: GraphemeStyle,
399 is_virtual: bool,
433 is_virtual: bool,
400 last_indent_level: &mut usize,
434 last_indent_level: &mut usize,
401 is_in_indent_area: &mut bool,
435 is_in_indent_area: &mut bool,
@@ -405,9 +439,11 @@ impl<'a> TextRenderer<'a> {
405 let is_whitespace = grapheme.is_whitespace();
439 let is_whitespace = grapheme.is_whitespace();
406
440
407 // TODO is it correct to apply the whitespace style to all unicode white spaces?
441 // TODO is it correct to apply the whitespace style to all unicode white spaces?
442+ let mut style = grapheme_style.syntax_style;
408 if is_whitespace {
443 if is_whitespace {
409 style = style.patch(self.whitespace_style);
444 style = style.patch(self.whitespace_style);
410 }
445 }
446+ style = style.patch(grapheme_style.overlay_style);
411
447
412 let width = grapheme.width();
448 let width = grapheme.width();
413 let space = if is_virtual { " " } else { &self.space };
449 let space = if is_virtual { " " } else { &self.space };
helix-term/src/ui/editor.rs+58−38
@@ -124,16 +124,20 @@ impl EditorView {
124 line_decorations.push(Box::new(line_decoration));
124 line_decorations.push(Box::new(line_decoration));
125 }
125 }
126
126
127− let mut highlights =
127+ let syntax_highlights =
128 Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
128 Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
129+
129− let overlay_highlights = Self::overlay_syntax_highlights(
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(
130 doc,
133 doc,
131 view.offset.anchor,
134 view.offset.anchor,
132 inner.height,
135 inner.height,
133 &text_annotations,
136 &text_annotations,
134 );
137 );
135− if !overlay_highlights.is_empty() {
138+ if !overlay_syntax_highlights.is_empty() {
136− highlights = Box::new(syntax::merge(highlights, overlay_highlights));
139+ overlay_highlights =
140+ Box::new(syntax::merge(overlay_highlights, overlay_syntax_highlights));
137 }
141 }
138
142
139 for diagnostic in Self::doc_diagnostics_highlights(doc, theme) {
143 for diagnostic in Self::doc_diagnostics_highlights(doc, theme) {
@@ -142,12 +146,12 @@ impl EditorView {
142 if diagnostic.is_empty() {
146 if diagnostic.is_empty() {
143 continue;
147 continue;
144 }
148 }
145− highlights = Box::new(syntax::merge(highlights, diagnostic));
149+ overlay_highlights = Box::new(syntax::merge(overlay_highlights, diagnostic));
146 }
150 }
147
151
148− let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
152+ if is_focused {
149 let highlights = syntax::merge(
153 let highlights = syntax::merge(
150− highlights,
154+ overlay_highlights,
151 Self::doc_selection_highlights(
155 Self::doc_selection_highlights(
152 editor.mode(),
156 editor.mode(),
153 doc,
157 doc,
@@ -158,13 +162,11 @@ impl EditorView {
158 );
162 );
159 let focused_view_elements = Self::highlight_focused_view_elements(view, doc, theme);
163 let focused_view_elements = Self::highlight_focused_view_elements(view, doc, theme);
160 if focused_view_elements.is_empty() {
164 if focused_view_elements.is_empty() {
161− Box::new(highlights)
165+ overlay_highlights = Box::new(highlights)
162 } else {
166 } else {
163− Box::new(syntax::merge(highlights, focused_view_elements))
167+ overlay_highlights = Box::new(syntax::merge(highlights, focused_view_elements))
164 }
168 }
165− } else {
169+ }
166− Box::new(highlights)
167− };
168
170
169 let gutter_overflow = view.gutter_offset(doc) == 0;
171 let gutter_overflow = view.gutter_offset(doc) == 0;
170 if !gutter_overflow {
172 if !gutter_overflow {
@@ -197,7 +199,8 @@ impl EditorView {
197 doc,
199 doc,
198 view.offset,
200 view.offset,
199 &text_annotations,
201 &text_annotations,
200− highlights,
202+ syntax_highlights,
203+ overlay_highlights,
201 theme,
204 theme,
202 &mut line_decorations,
205 &mut line_decorations,
203 &mut translated_positions,
206 &mut translated_positions,
@@ -257,27 +260,39 @@ impl EditorView {
257 .for_each(|area| surface.set_style(area, ruler_theme))
260 .for_each(|area| surface.set_style(area, ruler_theme))
258 }
261 }
259
262
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(
261 doc: &Document,
279 doc: &Document,
262 anchor: usize,
280 anchor: usize,
263 height: u16,
281 height: u16,
264− text_annotations: &TextAnnotations,
282+ ) -> Box<dyn Iterator<Item = HighlightEvent>> {
265− ) -> Vec<(usize, std::ops::Range<usize>)> {
266 let text = doc.text().slice(..);
283 let text = doc.text().slice(..);
267 let row = text.char_to_line(anchor.min(text.len_chars()));
284 let row = text.char_to_line(anchor.min(text.len_chars()));
268
285
269− let range = {
270− // Calculate viewport byte ranges:
286+ // Calculate viewport byte ranges:
271− // Saturating subs to make it inclusive zero indexing.
287+ // Saturating subs to make it inclusive zero indexing.
272− let last_line = text.len_lines().saturating_sub(1);
288+ let range = Self::viewport_byte_range(text, row, height);
273− let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
289+ Box::new(
274− let start = text.line_to_byte(row.min(last_line));
290+ [HighlightEvent::Source {
275− let end = text.line_to_byte(last_visible_line + 1);
291+ start: text.byte_to_char(range.start),
276−
292+ end: text.byte_to_char(range.end),
277− start..end
278− };
293+ }]
279−
294+ .into_iter(),
280− text_annotations.collect_overlay_highlights(range)
295+ )
281 }
296 }
282
297
283 /// Get syntax highlights for a document in a view represented by the first line
298 /// Get syntax highlights for a document in a view represented by the first line
@@ -292,16 +307,7 @@ impl EditorView {
292 let text = doc.text().slice(..);
307 let text = doc.text().slice(..);
293 let row = text.char_to_line(anchor.min(text.len_chars()));
308 let row = text.char_to_line(anchor.min(text.len_chars()));
294
309
295− let range = {
310+ let range = Self::viewport_byte_range(text, row, height);
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− };
305
311
306 match doc.syntax() {
312 match doc.syntax() {
307 Some(syntax) => {
313 Some(syntax) => {
@@ -334,6 +340,20 @@ impl EditorView {
334 }
340 }
335 }
341 }
336
342
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+
337 /// Get highlight spans for document diagnostics
357 /// Get highlight spans for document diagnostics
338 pub fn doc_diagnostics_highlights(
358 pub fn doc_diagnostics_highlights(
339 doc: &Document,
359 doc: &Document,
helix-term/src/ui/picker.rs+7−3
@@ -736,17 +736,20 @@ impl<T: Item + 'static> Picker<T> {
736 }
736 }
737 }
737 }
738
738
739− let mut highlights = EditorView::doc_syntax_highlights(
739+ let syntax_highlights = EditorView::doc_syntax_highlights(
740 doc,
740 doc,
741 offset.anchor,
741 offset.anchor,
742 area.height,
742 area.height,
743 &cx.editor.theme,
743 &cx.editor.theme,
744 );
744 );
745+
746+ let mut overlay_highlights =
747+ EditorView::empty_highlight_iter(doc, offset.anchor, area.height);
745 for spans in EditorView::doc_diagnostics_highlights(doc, &cx.editor.theme) {
748 for spans in EditorView::doc_diagnostics_highlights(doc, &cx.editor.theme) {
746 if spans.is_empty() {
749 if spans.is_empty() {
747 continue;
750 continue;
748 }
751 }
749− highlights = Box::new(helix_core::syntax::merge(highlights, spans));
752+ overlay_highlights = Box::new(helix_core::syntax::merge(overlay_highlights, spans));
750 }
753 }
751 let mut decorations: Vec<Box<dyn LineDecoration>> = Vec::new();
754 let mut decorations: Vec<Box<dyn LineDecoration>> = Vec::new();
752
755
@@ -777,7 +780,8 @@ impl<T: Item + 'static> Picker<T> {
777 offset,
780 offset,
778 // TODO: compute text annotations asynchronously here (like inlay hints)
781 // TODO: compute text annotations asynchronously here (like inlay hints)
779 &TextAnnotations::default(),
782 &TextAnnotations::default(),
780− highlights,
783+ syntax_highlights,
784+ overlay_highlights,
781 &cx.editor.theme,
785 &cx.editor.theme,
782 &mut decorations,
786 &mut decorations,
783 &mut [],
787 &mut [],