Skip to main content

markdown/fence/
mod.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
15mod chrome;
16pub mod highlight;
17#[cfg(feature = "mermaid")]
18pub mod mermaid;
19
20use std::rc::Rc;
21
22use gpui::{AnyElement, App, Global, SharedString, Window};
23
24pub(crate) use chrome::{CODE_PADDING_X, CODE_PADDING_Y, copy_button};
25pub use chrome::{NOTE_SELECTOR, fence_band, fence_note, fence_panel};
26
27/// Replaces the painted fence's code — see [`Fence::rewrite`].
28pub type Rewrite = Rc<dyn Fn(String, &mut Window, &mut App)>;
29
30/// The fence a [`BlockRenderer`] is asked to paint.
31pub struct Fence<'a> {
32    /// The info string.
33    pub language: &'a str,
34    pub code: &'a str,
35    /// The height to stand at, in whole pixels, from the fence's info string
36    /// or a resize in flight. `None` is the block's own height.
37    pub height: Option<u32>,
38    /// Writes new code into this fence through the editor holding it, as an
39    /// undoable edit. `None` in a document nobody is editing.
40    pub rewrite: Option<Rewrite>,
41}
42
43/// Paints the block a fence's info string names, or `None` to leave it to the
44/// ordinary code block.
45///
46/// One answer for a language nothing paints, a renderer that has not been
47/// installed, and a renderer that looked at the code and declined.
48///
49/// The element sits inside [`crate::render::PAINTED_CONTEXT`]. A press it does
50/// not stop reaches the editor, which puts the caret in the fence and shows its
51/// source.
52pub type BlockRenderer = fn(&Fence<'_>, &mut Window, &mut App) -> Option<AnyElement>;
53
54struct Installed(BlockRenderer);
55
56impl Global for Installed {}
57
58/// `cx.set_block_renderer(my_blocks)` — call once at boot.
59pub(crate) fn set_block_renderer(cx: &mut App, renderer: BlockRenderer) {
60    cx.set_global(Installed(renderer));
61}
62
63/// What a fence is painted as: a block in its place, or its code with a note
64/// under it.
65pub(crate) enum Painted {
66    Block(AnyElement),
67    Code(Option<SharedString>),
68}
69
70/// `block` is the fence's place in its document.
71pub(crate) fn render(
72    fence: &Fence<'_>,
73    #[cfg_attr(not(feature = "mermaid"), allow(unused_variables))] block: usize,
74    window: &mut Window,
75    cx: &mut App,
76) -> Painted {
77    // Copied out before the call: the renderer reads the theme and its own
78    // globals off the same `cx` this borrows.
79    if let Some(renderer) = cx.try_global::<Installed>().map(|installed| installed.0)
80        && let Some(painted) = renderer(fence, window, cx)
81    {
82        return Painted::Block(painted);
83    }
84    #[cfg(feature = "mermaid")]
85    if fence.language == self::mermaid::LANGUAGE {
86        return match self::mermaid::render(fence.code, fence.height, block, window, cx) {
87            Ok(painted) => Painted::Block(painted),
88            Err(note) => Painted::Code(Some(note)),
89        };
90    }
91    Painted::Code(None)
92}