markdown/block.rs
1//! Who paints a fenced block.
2//!
3//! A consumer that needs a block of its own — a chart, a diagram, an embed —
4//! reaches for this rather than for a new [`crate::BlockKind`]. The vocabulary
5//! stays closed because markdown is the wire form and a new kind would have to
6//! own a syntax; a fence already has one. It round trips byte for byte, holds a
7//! caret in [`crate::Part::Code`], and on a build that installs nothing it
8//! paints the source it always did.
9//!
10//! The `mermaid` feature paints a ` ```mermaid ` fence the installed renderer
11//! leaves, as a diagram.
12//!
13//! Installed once at boot like the highlighter, and read at paint.
14
15use std::rc::Rc;
16
17use gpui::{AnyElement, App, Global, Window};
18
19/// Replaces the painted fence's code — see [`Fence::rewrite`].
20pub type Rewrite = Rc<dyn Fn(String, &mut Window, &mut App)>;
21
22/// The fence a [`BlockRenderer`] is asked to paint.
23pub struct Fence<'a> {
24 /// The info string.
25 pub language: &'a str,
26 pub code: &'a str,
27 /// The height to stand at, in whole pixels, from the fence's info string
28 /// or a resize in flight. `None` is the block's own height.
29 pub height: Option<u32>,
30 /// Writes new code into this fence through the editor holding it, as an
31 /// undoable edit. `None` in a document nobody is editing.
32 pub rewrite: Option<Rewrite>,
33}
34
35/// Paints the block a fence's info string names, or `None` to leave it to the
36/// ordinary code block.
37///
38/// One answer for a language nothing paints, a renderer that has not been
39/// installed, and a renderer that looked at the code and declined.
40///
41/// The element sits inside [`crate::render::PAINTED_CONTEXT`]. A press it does
42/// not stop reaches the editor, which puts the caret in the fence and shows its
43/// source.
44pub type BlockRenderer = fn(&Fence<'_>, &mut Window, &mut App) -> Option<AnyElement>;
45
46struct Installed(BlockRenderer);
47
48impl Global for Installed {}
49
50/// `cx.set_block_renderer(my_blocks)` — call once at boot.
51pub(crate) fn set_block_renderer(cx: &mut App, renderer: BlockRenderer) {
52 cx.set_global(Installed(renderer));
53}
54
55pub(crate) fn render(fence: &Fence<'_>, window: &mut Window, cx: &mut App) -> Option<AnyElement> {
56 // Copied out before the call: the renderer reads the theme and its own
57 // globals off the same `cx` this borrows.
58 if let Some(renderer) = cx.try_global::<Installed>().map(|installed| installed.0)
59 && let Some(painted) = renderer(fence, window, cx)
60 {
61 return Some(painted);
62 }
63 #[cfg(feature = "mermaid")]
64 if fence.language == crate::mermaid::LANGUAGE {
65 return crate::mermaid::render(fence.code, fence.height, window, cx);
66 }
67 None
68}