on master help sign in
Extract markdown code block highlighting function
commit970a111aa3689e6cc46214859d71fdb0c87f7e14 authorGokul Soumya <gokulps15@gmail.com> dateSun 27 Feb 2022, 14:06 (5 years ago) parent5a60989
1 file changed+96−69
helix-term/src/ui/markdown.rs+96−69
helix-term/src/ui/markdown.rs+96−69
@@ -17,6 +17,95 @@ use helix_view::{
1717 Theme,
1818};
1919
20+fn styled_multiline_text<'a>(text: String, style: Style) -> Text<'a> {
21+ let spans: Vec<_> = text
22+ .lines()
23+ .map(|line| Span::styled(line.to_string(), style))
24+ .map(Spans::from)
25+ .collect();
26+ Text::from(spans)
27+}
28+
29+pub fn highlighted_code_block<'a>(
30+ text: String,
31+ language: &str,
32+ theme: Option<&Theme>,
33+ config_loader: Arc<syntax::Loader>,
34+) -> Text<'a> {
35+ let mut spans = Vec::new();
36+ let mut lines = Vec::new();
37+
38+ let get_theme = |key: &str| -> Style { theme.map(|t| t.get(key)).unwrap_or_default() };
39+ let text_style = get_theme(Markdown::TEXT_STYLE);
40+ let code_style = get_theme(Markdown::BLOCK_STYLE);
41+
42+ let theme = match theme {
43+ Some(t) => t,
44+ None => return styled_multiline_text(text, code_style),
45+ };
46+
47+ let rope = Rope::from(text.as_ref());
48+ let syntax = config_loader
49+ .language_configuration_for_injection_string(language)
50+ .and_then(|config| config.highlight_config(theme.scopes()))
51+ .map(|config| Syntax::new(&rope, config, Arc::clone(&config_loader)));
52+
53+ let syntax = match syntax {
54+ Some(s) => s,
55+ None => return styled_multiline_text(text, code_style),
56+ };
57+
58+ let mut highlights = Vec::new();
59+
60+ for event in syntax.highlight_iter(rope.slice(..), None, None) {
61+ match event.unwrap() {
62+ HighlightEvent::HighlightStart(span) => {
63+ highlights.push(span);
64+ }
65+ HighlightEvent::HighlightEnd => {
66+ highlights.pop();
67+ }
68+ HighlightEvent::Source { start, end } => {
69+ let style = match highlights.first() {
70+ Some(span) => theme.get(&theme.scopes()[span.0]),
71+ None => text_style,
72+ };
73+
74+ let mut slice = &text[start..end];
75+ // TODO: do we need to handle all unicode line endings
76+ // here, or is just '\n' okay?
77+ while let Some(end) = slice.find('\n') {
78+ // emit span up to newline
79+ let text = &slice[..end];
80+ let text = text.replace('\t', " "); // replace tabs
81+ let span = Span::styled(text, style);
82+ spans.push(span);
83+
84+ // truncate slice to after newline
85+ slice = &slice[end + 1..];
86+
87+ // make a new line
88+ let spans = std::mem::take(&mut spans);
89+ lines.push(Spans::from(spans));
90+ }
91+
92+ // if there's anything left, emit it too
93+ if !slice.is_empty() {
94+ let span = Span::styled(slice.replace('\t', " "), style);
95+ spans.push(span);
96+ }
97+ }
98+ }
99+ }
100+
101+ if !spans.is_empty() {
102+ let spans = std::mem::take(&mut spans);
103+ lines.push(Spans::from(spans));
104+ }
105+
106+ Text::from(lines)
107+}
108+
20109pub struct Markdown {
21110 contents: String,
22111
@@ -101,75 +190,13 @@ impl Markdown {
101190 Event::Text(text) => {
102191 // TODO: temp workaround
103192 if let Some(Tag::CodeBlock(CodeBlockKind::Fenced(language))) = tags.last() {
104− if let Some(theme) = theme {
105− let rope = Rope::from(text.as_ref());
106− let syntax = self
107− .config_loader
108− .language_configuration_for_injection_string(language)
109− .and_then(|config| config.highlight_config(theme.scopes()))
110− .map(|config| {
111− Syntax::new(&rope, config, self.config_loader.clone())
112− });
113−
114− if let Some(syntax) = syntax {
115− // if we have a syntax available, highlight_iter and generate spans
116− let mut highlights = Vec::new();
117−
118− for event in syntax.highlight_iter(rope.slice(..), None, None) {
119− match event.unwrap() {
120− HighlightEvent::HighlightStart(span) => {
121− highlights.push(span);
122− }
123− HighlightEvent::HighlightEnd => {
124− highlights.pop();
125− }
126− HighlightEvent::Source { start, end } => {
127− let style = match highlights.first() {
128− Some(span) => theme.get(&theme.scopes()[span.0]),
129− None => text_style,
130− };
131−
132− let mut slice = &text[start..end];
133− // TODO: do we need to handle all unicode line endings
134− // here, or is just '\n' okay?
135− while let Some(end) = slice.find('\n') {
136− // emit span up to newline
137− let text = &slice[..end];
138− let text = text.replace('\t', " "); // replace tabs
139− let span = Span::styled(text, style);
140− spans.push(span);
141−
142− // truncate slice to after newline
143− slice = &slice[end + 1..];
144−
145− // make a new line
146− let spans = std::mem::take(&mut spans);
147− lines.push(Spans::from(spans));
148− }
149−
150− // if there's anything left, emit it too
151− if !slice.is_empty() {
152− let span = Span::styled(
153− slice.replace('\t', " "),
154− style,
155− );
156− spans.push(span);
157− }
158− }
159− }
160− }
161− } else {
162− for line in text.lines() {
163− let span = Span::styled(line.to_string(), code_style);
164− lines.push(Spans::from(span));
165− }
166− }
167− } else {
168− for line in text.lines() {
169− let span = Span::styled(line.to_string(), code_style);
170− lines.push(Spans::from(span));
171− }
172− }
193+ let tui_text = highlighted_code_block(
194+ text.to_string(),
195+ language,
196+ theme,
197+ Arc::clone(&self.config_loader),
198+ );
199+ lines.extend(tui_text.lines.into_iter());
173200 } else {
174201 let style = if let Some(Tag::Heading(level, ..)) = tags.last() {
175202 match level {