markdown: Use parsed text (#24388)

Stanislav Alekseev created

Fixes #15463

Release Notes:

- Fixed display of symbols such as ` ` in hover popovers

Change summary

crates/editor/src/hover_popover.rs            |  8 ++-
crates/markdown/examples/markdown_as_child.rs |  1 
crates/markdown/src/markdown.rs               | 14 ++-----
crates/markdown/src/parser.rs                 | 41 +++++++++++++++-----
4 files changed, 40 insertions(+), 24 deletions(-)

Detailed changes

crates/editor/src/hover_popover.rs 🔗

@@ -598,7 +598,7 @@ async fn parse_blocks(
                 },
                 syntax: cx.theme().syntax().clone(),
                 selection_background_color: { cx.theme().players().local().selection },
-                break_style: Default::default(),
+
                 heading: StyleRefinement::default()
                     .font_weight(FontWeight::BOLD)
                     .text_base()
@@ -885,8 +885,10 @@ mod tests {
                 let slice = data;
 
                 for (range, event) in slice.iter() {
-                    if [MarkdownEvent::Text, MarkdownEvent::Code].contains(event) {
-                        rendered_text.push_str(&text[range.clone()])
+                    match event {
+                        MarkdownEvent::Text(parsed) => rendered_text.push_str(parsed),
+                        MarkdownEvent::Code => rendered_text.push_str(&text[range.clone()]),
+                        _ => {}
                     }
                 }
             }

crates/markdown/src/markdown.rs 🔗

@@ -28,7 +28,6 @@ pub struct MarkdownStyle {
     pub block_quote_border_color: Hsla,
     pub syntax: Arc<SyntaxTheme>,
     pub selection_background_color: Hsla,
-    pub break_style: StyleRefinement,
     pub heading: StyleRefinement,
 }
 
@@ -44,11 +43,11 @@ impl Default for MarkdownStyle {
             block_quote_border_color: Default::default(),
             syntax: Arc::new(SyntaxTheme::default()),
             selection_background_color: Default::default(),
-            break_style: Default::default(),
             heading: Default::default(),
         }
     }
 }
+
 pub struct Markdown {
     source: String,
     selection: Selection,
@@ -751,8 +750,8 @@ impl Element for MarkdownElement {
                     }
                     _ => log::error!("unsupported markdown tag end: {:?}", tag),
                 },
-                MarkdownEvent::Text => {
-                    builder.push_text(&parsed_markdown.source[range.clone()], range.start);
+                MarkdownEvent::Text(parsed) => {
+                    builder.push_text(parsed, range.start);
                 }
                 MarkdownEvent::Code => {
                     builder.push_text_style(self.style.inline_code.clone());
@@ -777,12 +776,7 @@ impl Element for MarkdownElement {
                     builder.pop_div()
                 }
                 MarkdownEvent::SoftBreak => builder.push_text(" ", range.start),
-                MarkdownEvent::HardBreak => {
-                    let mut d = div().py_3();
-                    d.style().refine(&self.style.break_style);
-                    builder.push_div(d, range, markdown_end);
-                    builder.pop_div()
-                }
+                MarkdownEvent::HardBreak => builder.push_text("\n", range.start),
                 _ => log::error!("unsupported markdown event {:?}", event),
             }
         }

crates/markdown/src/parser.rs 🔗

@@ -37,9 +37,10 @@ pub fn parse_markdown(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
                 }
                 events.push((range, MarkdownEvent::End(tag)));
             }
-            pulldown_cmark::Event::Text(_) => {
+            pulldown_cmark::Event::Text(parsed) => {
                 // Automatically detect links in text if we're not already within a markdown
                 // link.
+                let mut parsed = parsed.as_ref();
                 if !within_link {
                     let mut finder = LinkFinder::new();
                     finder.kinds(&[linkify::LinkKind::Url]);
@@ -49,7 +50,12 @@ pub fn parse_markdown(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
                             text_range.start + link.start()..text_range.start + link.end();
 
                         if link_range.start > range.start {
-                            events.push((range.start..link_range.start, MarkdownEvent::Text));
+                            let (text, tail) = parsed.split_at(link_range.start - range.start);
+                            events.push((
+                                range.start..link_range.start,
+                                MarkdownEvent::Text(SharedString::new(text)),
+                            ));
+                            parsed = tail;
                         }
 
                         events.push((
@@ -61,15 +67,20 @@ pub fn parse_markdown(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
                                 id: SharedString::default(),
                             }),
                         ));
-                        events.push((link_range.clone(), MarkdownEvent::Text));
+
+                        let (link_text, tail) = parsed.split_at(link_range.end - link_range.start);
+                        events.push((
+                            link_range.clone(),
+                            MarkdownEvent::Text(SharedString::new(link_text)),
+                        ));
                         events.push((link_range.clone(), MarkdownEvent::End(MarkdownTagEnd::Link)));
 
                         range.start = link_range.end;
+                        parsed = tail;
                     }
                 }
-
                 if range.start < range.end {
-                    events.push((range, MarkdownEvent::Text));
+                    events.push((range, MarkdownEvent::Text(SharedString::new(parsed))));
                 }
             }
             pulldown_cmark::Event::Code(_) => {
@@ -94,7 +105,7 @@ pub fn parse_markdown(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
     events
 }
 
-pub fn parse_links_only(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
+pub fn parse_links_only(mut text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
     let mut events = Vec::new();
     let mut finder = LinkFinder::new();
     finder.kinds(&[linkify::LinkKind::Url]);
@@ -106,9 +117,15 @@ pub fn parse_links_only(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
         let link_range = link.start()..link.end();
 
         if link_range.start > text_range.start {
-            events.push((text_range.start..link_range.start, MarkdownEvent::Text));
+            let (head, tail) = text.split_at(link_range.start - text_range.start);
+            events.push((
+                text_range.start..link_range.start,
+                MarkdownEvent::Text(SharedString::new(head)),
+            ));
+            text = tail;
         }
 
+        let (link_text, tail) = text.split_at(link_range.end - link_range.start);
         events.push((
             link_range.clone(),
             MarkdownEvent::Start(MarkdownTag::Link {
@@ -118,14 +135,18 @@ pub fn parse_links_only(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
                 id: SharedString::default(),
             }),
         ));
-        events.push((link_range.clone(), MarkdownEvent::Text));
+        events.push((
+            link_range.clone(),
+            MarkdownEvent::Text(SharedString::new(link_text)),
+        ));
         events.push((link_range.clone(), MarkdownEvent::End(MarkdownTagEnd::Link)));
 
         text_range.start = link_range.end;
+        text = tail;
     }
 
     if text_range.end > text_range.start {
-        events.push((text_range, MarkdownEvent::Text));
+        events.push((text_range, MarkdownEvent::Text(SharedString::new(text))));
     }
 
     events
@@ -142,7 +163,7 @@ pub enum MarkdownEvent {
     /// End of a tagged element.
     End(MarkdownTagEnd),
     /// A text node.
-    Text,
+    Text(SharedString),
     /// An inline code node.
     Code,
     /// An HTML node.