Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/ui/document.rs')
-rw-r--r--helix-term/src/ui/document.rs207
1 files changed, 111 insertions, 96 deletions
diff --git a/helix-term/src/ui/document.rs b/helix-term/src/ui/document.rs
index 8423ae8e..d71c47a1 100644
--- a/helix-term/src/ui/document.rs
+++ b/helix-term/src/ui/document.rs
@@ -3,8 +3,7 @@ use std::cmp::min;
use helix_core::doc_formatter::{DocumentFormatter, GraphemeSource, TextFormat};
use helix_core::graphemes::Grapheme;
use helix_core::str_utils::char_to_byte_idx;
-use helix_core::syntax::Highlight;
-use helix_core::syntax::HighlightEvent;
+use helix_core::syntax::{self, HighlightEvent, Highlighter, OverlayHighlights};
use helix_core::text_annotations::TextAnnotations;
use helix_core::{visual_offset_from_block, Position, RopeSlice};
use helix_stdx::rope::RopeSliceExt;
@@ -17,61 +16,6 @@ use tui::buffer::Buffer as Surface;
use crate::ui::text_decorations::DecorationManager;
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum StyleIterKind {
- /// base highlights (usually emitted by TS), byte indices (potentially not codepoint aligned)
- BaseHighlights,
- /// overlay highlights (emitted by custom code from selections), char indices
- Overlay,
-}
-
-/// A wrapper around a HighlightIterator
-/// that merges the layered highlights to create the final text style
-/// and yields the active text style and the char_idx where the active
-/// style will have to be recomputed.
-///
-/// TODO(ropey2): hopefully one day helix and ropey will operate entirely
-/// on byte ranges and we can remove this
-struct StyleIter<'a, H: Iterator<Item = HighlightEvent>> {
- text_style: Style,
- active_highlights: Vec<Highlight>,
- highlight_iter: H,
- kind: StyleIterKind,
- text: RopeSlice<'a>,
- theme: &'a Theme,
-}
-
-impl<H: Iterator<Item = HighlightEvent>> Iterator for StyleIter<'_, H> {
- type Item = (Style, usize);
- fn next(&mut self) -> Option<(Style, usize)> {
- while let Some(event) = self.highlight_iter.next() {
- match event {
- HighlightEvent::HighlightStart(highlights) => {
- self.active_highlights.push(highlights)
- }
- HighlightEvent::HighlightEnd => {
- self.active_highlights.pop();
- }
- HighlightEvent::Source { mut end, .. } => {
- let style = self
- .active_highlights
- .iter()
- .fold(self.text_style, |acc, span| {
- acc.patch(self.theme.highlight(span.0))
- });
- if self.kind == StyleIterKind::BaseHighlights {
- // Move the end byte index to the nearest character boundary (rounding up)
- // and convert it to a character index.
- end = self.text.byte_to_char(self.text.ceil_char_boundary(end));
- }
- return Some((style, end));
- }
- }
- }
- None
- }
-}
-
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct LinePos {
/// Indicates whether the given visual line
@@ -90,8 +34,8 @@ pub fn render_document(
doc: &Document,
offset: ViewPosition,
doc_annotations: &TextAnnotations,
- syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
- overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
+ syntax_highlighter: Option<Highlighter<'_>>,
+ overlay_highlights: Vec<syntax::OverlayHighlights>,
theme: &Theme,
decorations: DecorationManager,
) {
@@ -108,8 +52,8 @@ pub fn render_document(
offset.anchor,
&doc.text_format(viewport.width, Some(theme)),
doc_annotations,
- syntax_highlight_iter,
- overlay_highlight_iter,
+ syntax_highlighter,
+ overlay_highlights,
theme,
decorations,
)
@@ -122,8 +66,8 @@ pub fn render_text(
anchor: usize,
text_fmt: &TextFormat,
text_annotations: &TextAnnotations,
- syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
- overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
+ syntax_highlighter: Option<Highlighter<'_>>,
+ overlay_highlights: Vec<syntax::OverlayHighlights>,
theme: &Theme,
mut decorations: DecorationManager,
) {
@@ -133,22 +77,8 @@ pub fn render_text(
let mut formatter =
DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, text_annotations, anchor);
- let mut syntax_styles = StyleIter {
- text_style: renderer.text_style,
- active_highlights: Vec::with_capacity(64),
- highlight_iter: syntax_highlight_iter,
- kind: StyleIterKind::BaseHighlights,
- theme,
- text,
- };
- let mut overlay_styles = StyleIter {
- text_style: Style::default(),
- active_highlights: Vec::with_capacity(64),
- highlight_iter: overlay_highlight_iter,
- kind: StyleIterKind::Overlay,
- theme,
- text,
- };
+ let mut syntax_highlighter = SyntaxHighlighter::new(syntax_highlighter, text, theme);
+ let mut overlay_highlighter = OverlayHighlighter::new(overlay_highlights, theme);
let mut last_line_pos = LinePos {
first_visual_line: false,
@@ -158,12 +88,6 @@ pub fn render_text(
let mut last_line_end = 0;
let mut is_in_indent_area = true;
let mut last_line_indent_level = 0;
- let mut syntax_style_span = syntax_styles
- .next()
- .unwrap_or_else(|| (Style::default(), usize::MAX));
- let mut overlay_style_span = overlay_styles
- .next()
- .unwrap_or_else(|| (Style::default(), usize::MAX));
let mut reached_view_top = false;
loop {
@@ -207,21 +131,17 @@ pub fn render_text(
}
// acquire the correct grapheme style
- while grapheme.char_idx >= syntax_style_span.1 {
- syntax_style_span = syntax_styles
- .next()
- .unwrap_or((Style::default(), usize::MAX));
+ while grapheme.char_idx >= syntax_highlighter.pos {
+ syntax_highlighter.advance();
}
- while grapheme.char_idx >= overlay_style_span.1 {
- overlay_style_span = overlay_styles
- .next()
- .unwrap_or((Style::default(), usize::MAX));
+ while grapheme.char_idx >= overlay_highlighter.pos {
+ overlay_highlighter.advance();
}
let grapheme_style = if let GraphemeSource::VirtualText { highlight } = grapheme.source {
let mut style = renderer.text_style;
if let Some(highlight) = highlight {
- style = style.patch(theme.highlight(highlight.0));
+ style = style.patch(theme.highlight(highlight));
}
GraphemeStyle {
syntax_style: style,
@@ -229,8 +149,8 @@ pub fn render_text(
}
} else {
GraphemeStyle {
- syntax_style: syntax_style_span.0,
- overlay_style: overlay_style_span.0,
+ syntax_style: syntax_highlighter.style,
+ overlay_style: overlay_highlighter.style,
}
};
decorations.decorate_grapheme(renderer, &grapheme);
@@ -549,3 +469,98 @@ impl<'a> TextRenderer<'a> {
)
}
}
+
+struct SyntaxHighlighter<'h, 'r, 't> {
+ inner: Option<Highlighter<'h>>,
+ text: RopeSlice<'r>,
+ /// The character index of the next highlight event, or `usize::MAX` if the highlighter is
+ /// finished.
+ pos: usize,
+ theme: &'t Theme,
+ style: Style,
+}
+
+impl<'h, 'r, 't> SyntaxHighlighter<'h, 'r, 't> {
+ fn new(inner: Option<Highlighter<'h>>, text: RopeSlice<'r>, theme: &'t Theme) -> Self {
+ let mut highlighter = Self {
+ inner,
+ text,
+ pos: 0,
+ theme,
+ style: Style::default(),
+ };
+ highlighter.update_pos();
+ highlighter
+ }
+
+ fn update_pos(&mut self) {
+ self.pos = self
+ .inner
+ .as_ref()
+ .and_then(|highlighter| {
+ let next_byte_idx = highlighter.next_event_offset();
+ (next_byte_idx != u32::MAX).then(|| {
+ // Move the byte index to the nearest character boundary (rounding up) and
+ // convert it to a character index.
+ self.text
+ .byte_to_char(self.text.ceil_char_boundary(next_byte_idx as usize))
+ })
+ })
+ .unwrap_or(usize::MAX);
+ }
+
+ fn advance(&mut self) {
+ let Some(highlighter) = self.inner.as_mut() else {
+ return;
+ };
+
+ let (event, highlights) = highlighter.advance();
+ let base = match event {
+ HighlightEvent::Refresh => Style::default(),
+ HighlightEvent::Push => self.style,
+ };
+
+ self.style = highlights.fold(base, |acc, highlight| {
+ acc.patch(self.theme.highlight(highlight))
+ });
+ self.update_pos();
+ }
+}
+
+struct OverlayHighlighter<'t> {
+ inner: syntax::OverlayHighlighter,
+ pos: usize,
+ theme: &'t Theme,
+ style: Style,
+}
+
+impl<'t> OverlayHighlighter<'t> {
+ fn new(overlays: Vec<OverlayHighlights>, theme: &'t Theme) -> Self {
+ let inner = syntax::OverlayHighlighter::new(overlays);
+ let mut highlighter = Self {
+ inner,
+ pos: 0,
+ theme,
+ style: Style::default(),
+ };
+ highlighter.update_pos();
+ highlighter
+ }
+
+ fn update_pos(&mut self) {
+ self.pos = self.inner.next_event_offset();
+ }
+
+ fn advance(&mut self) {
+ let (event, highlights) = self.inner.advance();
+ let base = match event {
+ HighlightEvent::Refresh => Style::default(),
+ HighlightEvent::Push => self.style,
+ };
+
+ self.style = highlights.fold(base, |acc, highlight| {
+ acc.patch(self.theme.highlight(highlight))
+ });
+ self.update_pos();
+ }
+}