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.rs586
1 files changed, 281 insertions, 305 deletions
diff --git a/helix-term/src/ui/document.rs b/helix-term/src/ui/document.rs
index de85268a..bcbaa351 100644
--- a/helix-term/src/ui/document.rs
+++ b/helix-term/src/ui/document.rs
@@ -3,7 +3,8 @@ 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::{self, HighlightEvent, Highlighter, OverlayHighlights};
+use helix_core::syntax::Highlight;
+use helix_core::syntax::HighlightEvent;
use helix_core::text_annotations::TextAnnotations;
use helix_core::{visual_offset_from_block, Position, RopeSlice};
use helix_stdx::rope::RopeSliceExt;
@@ -11,10 +12,82 @@ use helix_view::editor::{WhitespaceConfig, WhitespaceRenderValue};
use helix_view::graphics::Rect;
use helix_view::theme::Style;
use helix_view::view::ViewPosition;
-use helix_view::{Document, Theme};
+use helix_view::Document;
+use helix_view::Theme;
use tui::buffer::Buffer as Surface;
-use crate::ui::text_decorations::DecorationManager;
+pub trait LineDecoration {
+ fn render_background(&mut self, _renderer: &mut TextRenderer, _pos: LinePos) {}
+ fn render_foreground(
+ &mut self,
+ _renderer: &mut TextRenderer,
+ _pos: LinePos,
+ _end_char_idx: usize,
+ ) {
+ }
+}
+
+impl<F: FnMut(&mut TextRenderer, LinePos)> LineDecoration for F {
+ fn render_background(&mut self, renderer: &mut TextRenderer, pos: LinePos) {
+ self(renderer, pos)
+ }
+}
+
+#[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 { start, mut end } => {
+ if start == end {
+ continue;
+ }
+ let style = self
+ .active_highlights
+ .iter()
+ .fold(self.text_style, |acc, span| {
+ acc.patch(self.theme.highlight(span.0))
+ });
+ if self.kind == StyleIterKind::BaseHighlights {
+ end = self.text.byte_to_next_char(end);
+ }
+ return Some((style, end));
+ }
+ }
+ }
+ None
+ }
+}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct LinePos {
@@ -25,8 +98,15 @@ pub struct LinePos {
pub doc_line: usize,
/// Vertical offset from the top of the inner view area
pub visual_line: u16,
+ /// The first char index of this visual line.
+ /// Note that if the visual line is entirely filled by
+ /// a very long inline virtual text then this index will point
+ /// at the next (non-virtual) char after this visual line
+ pub start_char_idx: usize,
}
+pub type TranslatedPosition<'a> = (usize, Box<dyn FnMut(&mut TextRenderer, Position) + 'a>);
+
#[allow(clippy::too_many_arguments)]
pub fn render_document(
surface: &mut Surface,
@@ -34,147 +114,248 @@ pub fn render_document(
doc: &Document,
offset: ViewPosition,
doc_annotations: &TextAnnotations,
- syntax_highlighter: Option<Highlighter<'_>>,
- overlay_highlights: Vec<syntax::OverlayHighlights>,
+ syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
+ overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
theme: &Theme,
- decorations: DecorationManager,
+ line_decoration: &mut [Box<dyn LineDecoration + '_>],
+ translated_positions: &mut [TranslatedPosition],
) {
- let mut renderer = TextRenderer::new(
- surface,
- doc,
- theme,
- Position::new(offset.vertical_offset, offset.horizontal_offset),
- viewport,
- );
+ let mut renderer = TextRenderer::new(surface, doc, theme, offset.horizontal_offset, viewport);
render_text(
&mut renderer,
doc.text().slice(..),
- offset.anchor,
+ offset,
&doc.text_format(viewport.width, Some(theme)),
doc_annotations,
- syntax_highlighter,
- overlay_highlights,
+ syntax_highlight_iter,
+ overlay_highlight_iter,
theme,
- decorations,
+ line_decoration,
+ translated_positions,
)
}
+fn translate_positions(
+ char_pos: usize,
+ first_visible_char_idx: usize,
+ translated_positions: &mut [TranslatedPosition],
+ text_fmt: &TextFormat,
+ renderer: &mut TextRenderer,
+ pos: Position,
+) {
+ // check if any positions translated on the fly (like cursor) has been reached
+ for (char_idx, callback) in &mut *translated_positions {
+ if *char_idx < char_pos && *char_idx >= first_visible_char_idx {
+ // by replacing the char_index with usize::MAX large number we ensure
+ // that the same position is only translated once
+ // text will never reach usize::MAX as rust memory allocations are limited
+ // to isize::MAX
+ *char_idx = usize::MAX;
+
+ if text_fmt.soft_wrap {
+ callback(renderer, pos)
+ } else if pos.col >= renderer.col_offset
+ && pos.col - renderer.col_offset < renderer.viewport.width as usize
+ {
+ callback(
+ renderer,
+ Position {
+ row: pos.row,
+ col: pos.col - renderer.col_offset,
+ },
+ )
+ }
+ }
+ }
+}
+
#[allow(clippy::too_many_arguments)]
-pub fn render_text(
+pub fn render_text<'t>(
renderer: &mut TextRenderer,
- text: RopeSlice<'_>,
- anchor: usize,
+ text: RopeSlice<'t>,
+ offset: ViewPosition,
text_fmt: &TextFormat,
text_annotations: &TextAnnotations,
- syntax_highlighter: Option<Highlighter<'_>>,
- overlay_highlights: Vec<syntax::OverlayHighlights>,
+ syntax_highlight_iter: impl Iterator<Item = HighlightEvent>,
+ overlay_highlight_iter: impl Iterator<Item = HighlightEvent>,
theme: &Theme,
- mut decorations: DecorationManager,
+ line_decorations: &mut [Box<dyn LineDecoration + '_>],
+ translated_positions: &mut [TranslatedPosition],
) {
- let row_off = visual_offset_from_block(text, anchor, anchor, text_fmt, text_annotations)
- .0
- .row;
-
- let mut formatter =
- DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, text_annotations, anchor);
- let mut syntax_highlighter =
- SyntaxHighlighter::new(syntax_highlighter, text, theme, renderer.text_style);
- let mut overlay_highlighter = OverlayHighlighter::new(overlay_highlights, theme);
+ let (
+ Position {
+ row: mut row_off, ..
+ },
+ mut char_pos,
+ ) = visual_offset_from_block(
+ text,
+ offset.anchor,
+ offset.anchor,
+ text_fmt,
+ text_annotations,
+ );
+ row_off += offset.vertical_offset;
+
+ let (mut formatter, mut first_visible_char_idx) =
+ DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, text_annotations, offset.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 last_line_pos = LinePos {
first_visual_line: false,
doc_line: usize::MAX,
visual_line: u16::MAX,
+ start_char_idx: usize::MAX,
};
- let mut last_line_end = 0;
let mut is_in_indent_area = true;
let mut last_line_indent_level = 0;
- let mut reached_view_top = false;
+ 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));
loop {
- let Some(mut grapheme) = formatter.next() else {
+ // formattter.line_pos returns to line index of the next grapheme
+ // so it must be called before formatter.next
+ let doc_line = formatter.line_pos();
+ let Some((grapheme, mut pos)) = formatter.next() else {
+ let mut last_pos = formatter.visual_pos();
+ if last_pos.row >= row_off {
+ last_pos.col -= 1;
+ last_pos.row -= row_off;
+ // check if any positions translated on the fly (like cursor) are at the EOF
+ translate_positions(
+ char_pos + 1,
+ first_visible_char_idx,
+ translated_positions,
+ text_fmt,
+ renderer,
+ last_pos,
+ );
+ }
break;
};
// skip any graphemes on visual lines before the block start
- if grapheme.visual_pos.row < row_off {
+ if pos.row < row_off {
+ if char_pos >= syntax_style_span.1 {
+ syntax_style_span = if let Some(syntax_style_span) = syntax_styles.next() {
+ syntax_style_span
+ } else {
+ break;
+ }
+ }
+ if char_pos >= overlay_style_span.1 {
+ overlay_style_span = if let Some(overlay_style_span) = overlay_styles.next() {
+ overlay_style_span
+ } else {
+ break;
+ }
+ }
+ char_pos += grapheme.doc_chars();
+ first_visible_char_idx = char_pos + 1;
continue;
}
- grapheme.visual_pos.row -= row_off;
- if !reached_view_top {
- decorations.prepare_for_rendering(grapheme.char_idx);
- reached_view_top = true;
- }
+ pos.row -= row_off;
// if the end of the viewport is reached stop rendering
- if grapheme.visual_pos.row as u16 >= renderer.viewport.height + renderer.offset.row as u16 {
+ if pos.row as u16 >= renderer.viewport.height {
break;
}
// apply decorations before rendering a new line
- if grapheme.visual_pos.row as u16 != last_line_pos.visual_line {
- // we initiate doc_line with usize::MAX because no file
- // can reach that size (memory allocations are limited to isize::MAX)
- // initially there is no "previous" line (so doc_line is set to usize::MAX)
- // in that case we don't need to draw indent guides/virtual text
- if last_line_pos.doc_line != usize::MAX {
- // draw indent guides for the last line
+ if pos.row as u16 != last_line_pos.visual_line {
+ if pos.row > 0 {
renderer.draw_indent_guides(last_line_indent_level, last_line_pos.visual_line);
is_in_indent_area = true;
- decorations.render_virtual_lines(renderer, last_line_pos, last_line_end)
+ for line_decoration in &mut *line_decorations {
+ line_decoration.render_foreground(renderer, last_line_pos, char_pos);
+ }
}
last_line_pos = LinePos {
- first_visual_line: grapheme.line_idx != last_line_pos.doc_line,
- doc_line: grapheme.line_idx,
- visual_line: grapheme.visual_pos.row as u16,
+ first_visual_line: doc_line != last_line_pos.doc_line,
+ doc_line,
+ visual_line: pos.row as u16,
+ start_char_idx: char_pos,
};
- decorations.decorate_line(renderer, last_line_pos);
+ for line_decoration in &mut *line_decorations {
+ line_decoration.render_background(renderer, last_line_pos);
+ }
}
// acquire the correct grapheme style
- while grapheme.char_idx >= syntax_highlighter.pos {
- syntax_highlighter.advance();
+ if char_pos >= syntax_style_span.1 {
+ syntax_style_span = syntax_styles
+ .next()
+ .unwrap_or((Style::default(), usize::MAX));
}
- while grapheme.char_idx >= overlay_highlighter.pos {
- overlay_highlighter.advance();
+ if char_pos >= overlay_style_span.1 {
+ overlay_style_span = overlay_styles
+ .next()
+ .unwrap_or((Style::default(), usize::MAX));
}
+ char_pos += grapheme.doc_chars();
+
+ // check if any positions translated on the fly (like cursor) has been reached
+ translate_positions(
+ char_pos,
+ first_visible_char_idx,
+ translated_positions,
+ text_fmt,
+ renderer,
+ pos,
+ );
- 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));
- }
- GraphemeStyle {
- syntax_style: style,
- overlay_style: Style::default(),
- }
- } else {
- GraphemeStyle {
- syntax_style: syntax_highlighter.style,
- overlay_style: overlay_highlighter.style,
- }
- };
- decorations.decorate_grapheme(renderer, &grapheme);
+ let (syntax_style, overlay_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::default())
+ } else {
+ (syntax_style_span.0, overlay_style_span.0)
+ };
- let virt = grapheme.is_virtual();
- let grapheme_width = renderer.draw_grapheme(
- grapheme.raw,
- grapheme_style,
- virt,
+ let is_virtual = grapheme.is_virtual();
+ renderer.draw_grapheme(
+ grapheme.grapheme,
+ GraphemeStyle {
+ syntax_style,
+ overlay_style,
+ },
+ is_virtual,
&mut last_line_indent_level,
&mut is_in_indent_area,
- grapheme.visual_pos,
+ pos,
);
- last_line_end = grapheme.visual_pos.col + grapheme_width;
}
renderer.draw_indent_guides(last_line_indent_level, last_line_pos.visual_line);
- decorations.render_virtual_lines(renderer, last_line_pos, last_line_end)
+ for line_decoration in &mut *line_decorations {
+ line_decoration.render_foreground(renderer, last_line_pos, char_pos);
+ }
}
#[derive(Debug)]
pub struct TextRenderer<'a> {
- surface: &'a mut Surface,
+ pub surface: &'a mut Surface,
pub text_style: Style,
pub whitespace_style: Style,
pub indent_guide_char: String,
@@ -188,8 +369,8 @@ pub struct TextRenderer<'a> {
pub indent_width: u16,
pub starting_indent: usize,
pub draw_indent_guides: bool,
+ pub col_offset: usize,
pub viewport: Rect,
- pub offset: Position,
}
pub struct GraphemeStyle {
@@ -202,7 +383,7 @@ impl<'a> TextRenderer<'a> {
surface: &'a mut Surface,
doc: &Document,
theme: &Theme,
- offset: Position,
+ col_offset: usize,
viewport: Rect,
) -> TextRenderer<'a> {
let editor_config = doc.config.load();
@@ -214,7 +395,7 @@ impl<'a> TextRenderer<'a> {
let tab_width = doc.tab_width();
let tab = if ws_render.tab() == WhitespaceRenderValue::All {
std::iter::once(ws_chars.tab)
- .chain(std::iter::repeat_n(ws_chars.tabpad, tab_width - 1))
+ .chain(std::iter::repeat(ws_chars.tabpad).take(tab_width - 1))
.collect()
} else {
" ".repeat(tab_width)
@@ -257,8 +438,8 @@ impl<'a> TextRenderer<'a> {
virtual_tab,
whitespace_style: theme.get("ui.virtual.whitespace"),
indent_width,
- starting_indent: offset.col / indent_width as usize
- + (offset.col % indent_width as usize != 0) as usize
+ starting_indent: col_offset / indent_width as usize
+ + (col_offset % indent_width as usize != 0) as usize
+ editor_config.indent_guides.skip_levels as usize,
indent_guide_style: text_style.patch(
theme
@@ -268,46 +449,8 @@ impl<'a> TextRenderer<'a> {
text_style,
draw_indent_guides: editor_config.indent_guides.render,
viewport,
- offset,
- }
- }
- /// Draws a single `grapheme` at the current render position with a specified `style`.
- pub fn draw_decoration_grapheme(
- &mut self,
- grapheme: Grapheme,
- mut style: Style,
- mut row: u16,
- col: u16,
- ) -> bool {
- if (row as usize) < self.offset.row
- || row >= self.viewport.height
- || col >= self.viewport.width
- {
- return false;
- }
- row -= self.offset.row as u16;
- // TODO is it correct to apply the whitspace style to all unicode white spaces?
- if grapheme.is_whitespace() {
- style = style.patch(self.whitespace_style);
+ col_offset,
}
-
- let grapheme = match grapheme {
- Grapheme::Tab { width } => {
- let grapheme_tab_width = char_to_byte_idx(&self.virtual_tab, width);
- &self.virtual_tab[..grapheme_tab_width]
- }
- Grapheme::Other { ref g } if g == "\u{00A0}" => " ",
- Grapheme::Other { ref g } => g,
- Grapheme::Newline => " ",
- };
-
- self.surface.set_string(
- self.viewport.x + col,
- self.viewport.y + row,
- grapheme,
- style,
- );
- true
}
/// Draws a single `grapheme` at the current render position with a specified `style`.
@@ -318,13 +461,9 @@ impl<'a> TextRenderer<'a> {
is_virtual: bool,
last_indent_level: &mut usize,
is_in_indent_area: &mut bool,
- mut position: Position,
- ) -> usize {
- if position.row < self.offset.row {
- return 0;
- }
- position.row -= self.offset.row;
- let cut_off_start = self.offset.col.saturating_sub(position.col);
+ position: Position,
+ ) {
+ let cut_off_start = self.col_offset.saturating_sub(position.col);
let is_whitespace = grapheme.is_whitespace();
// TODO is it correct to apply the whitespace style to all unicode white spaces?
@@ -356,11 +495,12 @@ impl<'a> TextRenderer<'a> {
Grapheme::Newline => &self.newline,
};
- let in_bounds = self.column_in_bounds(position.col, width);
+ let in_bounds = self.col_offset <= position.col
+ && position.col < self.viewport.width as usize + self.col_offset;
if in_bounds {
self.surface.set_string(
- self.viewport.x + (position.col - self.offset.col) as u16,
+ self.viewport.x + (position.col - self.col_offset) as u16,
self.viewport.y + position.row as u16,
grapheme,
style,
@@ -375,37 +515,31 @@ impl<'a> TextRenderer<'a> {
);
self.surface.set_style(rect, style);
}
+
if *is_in_indent_area && !is_whitespace {
*last_indent_level = position.col;
*is_in_indent_area = false;
}
-
- width
- }
-
- pub fn column_in_bounds(&self, colum: usize, width: usize) -> bool {
- self.offset.col <= colum && colum + width <= self.offset.col + self.viewport.width as usize
}
/// Overlay indentation guides ontop of a rendered line
/// The indentation level is computed in `draw_lines`.
/// Therefore this function must always be called afterwards.
- pub fn draw_indent_guides(&mut self, indent_level: usize, mut row: u16) {
- if !self.draw_indent_guides || self.offset.row > row as usize {
+ pub fn draw_indent_guides(&mut self, indent_level: usize, row: u16) {
+ if !self.draw_indent_guides {
return;
}
- row -= self.offset.row as u16;
// Don't draw indent guides outside of view
let end_indent = min(
indent_level,
// Add indent_width - 1 to round up, since the first visible
// indent might be a bit after offset.col
- self.offset.col + self.viewport.width as usize + (self.indent_width as usize - 1),
+ self.col_offset + self.viewport.width as usize + (self.indent_width as usize - 1),
) / self.indent_width as usize;
for i in self.starting_indent..end_indent {
- let x = (self.viewport.x as usize + (i * self.indent_width as usize) - self.offset.col)
+ let x = (self.viewport.x as usize + (i * self.indent_width as usize) - self.col_offset)
as u16;
let y = self.viewport.y + row;
debug_assert!(self.surface.in_bounds(x, y));
@@ -413,162 +547,4 @@ impl<'a> TextRenderer<'a> {
.set_string(x, y, &self.indent_guide_char, self.indent_guide_style);
}
}
-
- pub fn set_string(&mut self, x: u16, y: u16, string: impl AsRef<str>, style: Style) {
- if (y as usize) < self.offset.row {
- return;
- }
- self.surface
- .set_string(x, y + self.viewport.y, string, style)
- }
-
- pub fn set_stringn(
- &mut self,
- x: u16,
- y: u16,
- string: impl AsRef<str>,
- width: usize,
- style: Style,
- ) {
- if (y as usize) < self.offset.row {
- return;
- }
- self.surface
- .set_stringn(x, y + self.viewport.y, string, width, style);
- }
-
- /// Sets the style of an area **within the text viewport* this accounts
- /// both for the renderers vertical offset and its viewport
- pub fn set_style(&mut self, mut area: Rect, style: Style) {
- area = area.clip_top(self.offset.row as u16);
- area.y += self.viewport.y;
- self.surface.set_style(area, style);
- }
-
- #[allow(clippy::too_many_arguments)]
- pub fn set_string_truncated(
- &mut self,
- x: u16,
- y: u16,
- string: &str,
- width: usize,
- style: impl Fn(usize) -> Style, // Map a grapheme's string offset to a style
- ellipsis: bool,
- truncate_start: bool,
- ) -> (u16, u16) {
- if (y as usize) < self.offset.row {
- return (x, y);
- }
- self.surface.set_string_truncated(
- x,
- y + self.viewport.y,
- string,
- width,
- style,
- ellipsis,
- truncate_start,
- )
- }
-}
-
-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,
- text_style: Style,
- style: Style,
-}
-
-impl<'h, 'r, 't> SyntaxHighlighter<'h, 'r, 't> {
- fn new(
- inner: Option<Highlighter<'h>>,
- text: RopeSlice<'r>,
- theme: &'t Theme,
- text_style: Style,
- ) -> Self {
- let mut highlighter = Self {
- inner,
- text,
- pos: 0,
- theme,
- style: text_style,
- text_style,
- };
- 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 => self.text_style,
- 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();
- }
}