Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/ui/markdown.rs')
| -rw-r--r-- | helix-term/src/ui/markdown.rs | 133 |
1 files changed, 72 insertions, 61 deletions
diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index fe581b5a..ae58d75e 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -10,8 +10,8 @@ use std::sync::Arc; use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag, TagEnd}; use helix_core::{ - syntax::{self, HighlightEvent, InjectionLanguageMarker, Syntax}, - RopeSlice, + syntax::{self, HighlightEvent, OverlayHighlights}, + RopeSlice, Syntax, }; use helix_view::{ graphics::{Margin, Rect, Style}, @@ -32,8 +32,12 @@ pub fn highlighted_code_block<'a>( text: &str, language: &str, theme: Option<&Theme>, - config_loader: Arc<ArcSwap<syntax::Loader>>, - additional_highlight_spans: Option<Vec<(usize, std::ops::Range<usize>)>>, + loader: &syntax::Loader, + // Optional overlay highlights to mix in with the syntax highlights. + // + // Note that `OverlayHighlights` is typically used with char indexing but the only caller + // which passes this parameter currently passes **byte indices** instead. + additional_highlight_spans: Option<OverlayHighlights>, ) -> Text<'a> { let mut spans = Vec::new(); let mut lines = Vec::new(); @@ -48,67 +52,74 @@ pub fn highlighted_code_block<'a>( }; let ropeslice = RopeSlice::from(text); - let syntax = config_loader - .load() - .language_configuration_for_injection_string(&InjectionLanguageMarker::Name( - language.into(), - )) - .and_then(|config| config.highlight_config(theme.scopes())) - .and_then(|config| Syntax::new(ropeslice, config, Arc::clone(&config_loader))); - - let syntax = match syntax { - Some(s) => s, - None => return styled_multiline_text(text, code_style), + let Some(syntax) = loader + .language_for_match(RopeSlice::from(language)) + .and_then(|lang| Syntax::new(ropeslice, lang, loader).ok()) + else { + return styled_multiline_text(text, code_style); }; - let highlight_iter = syntax - .highlight_iter(ropeslice, None, None) - .map(|e| e.unwrap()); - let highlight_iter: Box<dyn Iterator<Item = HighlightEvent>> = - if let Some(spans) = additional_highlight_spans { - Box::new(helix_core::syntax::merge(highlight_iter, spans)) - } else { - Box::new(highlight_iter) - }; - - let mut highlights = Vec::new(); - for event in highlight_iter { - match event { - HighlightEvent::HighlightStart(span) => { - highlights.push(span); + let mut syntax_highlighter = syntax.highlighter(ropeslice, loader, ..); + let mut syntax_highlight_stack = Vec::new(); + let mut overlay_highlight_stack = Vec::new(); + let mut overlay_highlighter = syntax::OverlayHighlighter::new(additional_highlight_spans); + let mut pos = 0; + + while pos < ropeslice.len_bytes() as u32 { + if pos == syntax_highlighter.next_event_offset() { + let (event, new_highlights) = syntax_highlighter.advance(); + if event == HighlightEvent::Refresh { + syntax_highlight_stack.clear(); } - HighlightEvent::HighlightEnd => { - highlights.pop(); + syntax_highlight_stack.extend(new_highlights); + } else if pos == overlay_highlighter.next_event_offset() as u32 { + let (event, new_highlights) = overlay_highlighter.advance(); + if event == HighlightEvent::Refresh { + overlay_highlight_stack.clear(); } - HighlightEvent::Source { start, end } => { - let style = highlights - .iter() - .fold(text_style, |acc, span| acc.patch(theme.highlight(span.0))); - - let mut slice = &text[start..end]; - // TODO: do we need to handle all unicode line endings - // here, or is just '\n' okay? - while let Some(end) = slice.find('\n') { - // emit span up to newline - let text = &slice[..end]; - let text = text.replace('\t', " "); // replace tabs - let span = Span::styled(text, style); - spans.push(span); - - // truncate slice to after newline - slice = &slice[end + 1..]; - - // make a new line - let spans = std::mem::take(&mut spans); - lines.push(Spans::from(spans)); - } + overlay_highlight_stack.extend(new_highlights) + } - // if there's anything left, emit it too - if !slice.is_empty() { - let span = Span::styled(slice.replace('\t', " "), style); - spans.push(span); - } - } + let start = pos; + pos = syntax_highlighter + .next_event_offset() + .min(overlay_highlighter.next_event_offset() as u32); + if pos == u32::MAX { + pos = ropeslice.len_bytes() as u32; + } + if pos == start { + continue; + } + assert!(pos > start); + + let style = syntax_highlight_stack + .iter() + .chain(overlay_highlight_stack.iter()) + .fold(text_style, |acc, highlight| { + acc.patch(theme.highlight(*highlight)) + }); + + let mut slice = &text[start as usize..pos as usize]; + // TODO: do we need to handle all unicode line endings + // here, or is just '\n' okay? + while let Some(end) = slice.find('\n') { + // emit span up to newline + let text = &slice[..end]; + let text = text.replace('\t', " "); // replace tabs + let span = Span::styled(text, style); + spans.push(span); + + // truncate slice to after newline + slice = &slice[end + 1..]; + + // make a new line + let spans = std::mem::take(&mut spans); + lines.push(Spans::from(spans)); + } + + if !slice.is_empty() { + let span = Span::styled(slice.replace('\t', " "), style); + spans.push(span); } } @@ -286,7 +297,7 @@ impl Markdown { &text, language, theme, - Arc::clone(&self.config_loader), + &self.config_loader.load(), None, ); lines.extend(tui_text.lines.into_iter()); |