Skip to main content

damascene_core/widgets/
code_block.rs

1//! Code block — surface-tinted monospace block for fenced code.
2//!
3//! Mirrors HTML's `<pre><code>` shape: a bordered, rounded surface that
4//! wraps a multi-line monospace body. The body uses the `mono` font path
5//! at `TEXT_SM` size and does not wrap — long lines extend horizontally
6//! rather than reflowing, the same convention shadcn's typography uses
7//! for fenced code (`overflow-x-auto`). Author wraps the result in
8//! `scroll([...])` for horizontal scroll if desired; that integration is
9//! out of scope for this primitive.
10//!
11//! Two entry points: [`code_block`] takes a verbatim string and renders
12//! it as one mono text leaf, while [`code_block_chrome`] takes any
13//! pre-built body `El` (typically a styled [`text_runs`] paragraph
14//! produced by a syntax highlighter) and applies the same surface
15//! chrome — used by `damascene-markdown` to render highlighted fenced code
16//! without duplicating the chrome tokens.
17//!
18//! ```ignore
19//! use damascene_core::prelude::*;
20//!
21//! code_block("fn main() {\n    println!(\"hi\");\n}")
22//! ```
23
24// Lock in full per-item documentation for this module (issue #73).
25#![warn(missing_docs)]
26
27use std::panic::Location;
28
29use crate::style::StyleProfile;
30use crate::tokens;
31use crate::tree::*;
32use crate::widgets::text::text;
33
34/// Fenced-code surface (HTML `<pre><code>`) — the verbatim string as a
35/// non-wrapping mono leaf inside the standard code-block chrome. Long
36/// lines extend horizontally; wrap in `scroll([...])` if needed.
37#[track_caller]
38pub fn code_block(s: impl Into<String>) -> El {
39    let loc = Location::caller();
40    let body = text(s)
41        .at_loc(loc)
42        .mono()
43        .font_size(tokens::TEXT_SM.size)
44        .nowrap_text()
45        .width(Size::Hug)
46        .height(Size::Hug);
47    code_block_chrome_at(body, loc)
48}
49
50/// Wrap an author-supplied body `El` in the standard code-block surface
51/// chrome (sunken muted fill, themed border, rounded corners,
52/// `SPACE_3` padding). Used by syntax-highlighter integrations that
53/// need the same chrome around a styled `text_runs([...])` paragraph
54/// rather than a plain mono text leaf.
55#[track_caller]
56pub fn code_block_chrome(body: El) -> El {
57    code_block_chrome_at(body, Location::caller())
58}
59
60fn code_block_chrome_at(body: El, loc: &'static Location<'static>) -> El {
61    column([body])
62        .at_loc(loc)
63        .style_profile(StyleProfile::Surface)
64        .surface_role(SurfaceRole::Sunken)
65        .fill(tokens::MUTED)
66        .stroke(tokens::BORDER)
67        .default_radius(tokens::RADIUS_MD)
68        .default_padding(Sides::all(tokens::SPACE_3))
69        .width(Size::Fill(1.0))
70        .height(Size::Hug)
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn code_block_wraps_mono_body_in_sunken_surface() {
79        let block = code_block("fn main() {\n    println!(\"hi\");\n}");
80
81        assert_eq!(block.kind, Kind::Group);
82        assert_eq!(block.axis, Axis::Column);
83        assert_eq!(block.style_profile, StyleProfile::Surface);
84        assert_eq!(block.surface_role, SurfaceRole::Sunken);
85        assert_eq!(block.fill, Some(tokens::MUTED));
86        assert_eq!(block.stroke, Some(tokens::BORDER));
87        assert_eq!(block.padding, Sides::all(tokens::SPACE_3));
88        assert_eq!(block.width, Size::Fill(1.0));
89        assert_eq!(block.children.len(), 1);
90
91        let body = &block.children[0];
92        assert_eq!(body.kind, Kind::Text);
93        assert!(body.font_mono);
94        assert_eq!(body.font_size, tokens::TEXT_SM.size);
95        assert_eq!(body.text_wrap, TextWrap::NoWrap);
96        assert_eq!(
97            body.text.as_deref(),
98            Some("fn main() {\n    println!(\"hi\");\n}")
99        );
100    }
101}