1use anyhow::Result;
2use gpui::{div, prelude::*, App, ClipboardItem, Context, Entity, Task, Window};
3use language::Buffer;
4use markdown_preview::{
5 markdown_elements::ParsedMarkdown, markdown_parser::parse_markdown,
6 markdown_renderer::render_markdown_block,
7};
8use ui::v_flex;
9
10use crate::outputs::OutputContent;
11
12pub struct MarkdownView {
13 raw_text: String,
14 contents: Option<ParsedMarkdown>,
15 parsing_markdown_task: Option<Task<Result<()>>>,
16}
17
18impl MarkdownView {
19 pub fn from(text: String, cx: &mut Context<Self>) -> Self {
20 let task = cx.spawn(|markdown_view, mut cx| {
21 let text = text.clone();
22 let parsed =
23 cx.background_spawn(async move { parse_markdown(&text, None, None).await });
24
25 async move {
26 let content = parsed.await;
27
28 markdown_view.update(&mut cx, |markdown, cx| {
29 markdown.parsing_markdown_task.take();
30 markdown.contents = Some(content);
31 cx.notify();
32 })
33 }
34 });
35
36 Self {
37 raw_text: text.clone(),
38 contents: None,
39 parsing_markdown_task: Some(task),
40 }
41 }
42}
43
44impl OutputContent for MarkdownView {
45 fn clipboard_content(&self, _window: &Window, _cx: &App) -> Option<ClipboardItem> {
46 Some(ClipboardItem::new_string(self.raw_text.clone()))
47 }
48
49 fn has_clipboard_content(&self, _window: &Window, _cx: &App) -> bool {
50 true
51 }
52
53 fn has_buffer_content(&self, _window: &Window, _cx: &App) -> bool {
54 true
55 }
56
57 fn buffer_content(&mut self, _: &mut Window, cx: &mut App) -> Option<Entity<Buffer>> {
58 let buffer = cx.new(|cx| {
59 // TODO: Bring in the language registry so we can set the language to markdown
60 let mut buffer = Buffer::local(self.raw_text.clone(), cx)
61 .with_language(language::PLAIN_TEXT.clone(), cx);
62 buffer.set_capability(language::Capability::ReadOnly, cx);
63 buffer
64 });
65 Some(buffer)
66 }
67}
68
69impl Render for MarkdownView {
70 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
71 let Some(parsed) = self.contents.as_ref() else {
72 return div().into_any_element();
73 };
74
75 let mut markdown_render_context =
76 markdown_preview::markdown_renderer::RenderContext::new(None, window, cx);
77
78 v_flex()
79 .gap_3()
80 .py_4()
81 .children(parsed.children.iter().map(|child| {
82 div().relative().child(
83 div()
84 .relative()
85 .child(render_markdown_block(child, &mut markdown_render_context)),
86 )
87 }))
88 .into_any_element()
89 }
90}