Support for raw syntax highlighting in HTML export (#6691)
This commit is contained in:
parent
04b8b3195f
commit
6177c1b22d
|
|
@ -26,7 +26,6 @@ impl Properties {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new property in builder-style.
|
/// Adds a new property in builder-style.
|
||||||
#[expect(unused)]
|
|
||||||
pub fn with(mut self, property: &str, value: impl Display) -> Self {
|
pub fn with(mut self, property: &str, value: impl Display) -> Self {
|
||||||
self.push(property, value);
|
self.push(property, value);
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ mod typed;
|
||||||
pub use self::document::html_document;
|
pub use self::document::html_document;
|
||||||
pub use self::dom::*;
|
pub use self::dom::*;
|
||||||
pub use self::encode::html;
|
pub use self::encode::html;
|
||||||
pub use self::rules::register;
|
pub use self::rules::{html_span_filled, register};
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use typst_library::Category;
|
use typst_library::Category;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use typst_library::text::{
|
||||||
HighlightElem, LinebreakElem, OverlineElem, RawElem, RawLine, SmallcapsElem,
|
HighlightElem, LinebreakElem, OverlineElem, RawElem, RawLine, SmallcapsElem,
|
||||||
SpaceElem, StrikeElem, SubElem, SuperElem, UnderlineElem,
|
SpaceElem, StrikeElem, SubElem, SuperElem, UnderlineElem,
|
||||||
};
|
};
|
||||||
use typst_library::visualize::ImageElem;
|
use typst_library::visualize::{Color, ImageElem};
|
||||||
|
|
||||||
use crate::{FrameElem, HtmlAttrs, HtmlElem, HtmlTag, attr, css, tag};
|
use crate::{FrameElem, HtmlAttrs, HtmlElem, HtmlTag, attr, css, tag};
|
||||||
|
|
||||||
|
|
@ -427,6 +427,20 @@ const RAW_RULE: ShowFn<RawElem> = |elem, _, styles| {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This is used by `RawElem::synthesize` through a routine.
|
||||||
|
///
|
||||||
|
/// It's a temporary workaround until `TextElem::fill` is supported in HTML
|
||||||
|
/// export.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn html_span_filled(content: Content, color: Color) -> Content {
|
||||||
|
let span = content.span();
|
||||||
|
HtmlElem::new(tag::span)
|
||||||
|
.with_styles(css::Properties::new().with("color", css::color(color)))
|
||||||
|
.with_body(Some(content))
|
||||||
|
.pack()
|
||||||
|
.spanned(span)
|
||||||
|
}
|
||||||
|
|
||||||
const RAW_LINE_RULE: ShowFn<RawLine> = |elem, _, _| Ok(elem.body.clone());
|
const RAW_LINE_RULE: ShowFn<RawLine> = |elem, _, _| Ok(elem.body.clone());
|
||||||
|
|
||||||
const IMAGE_RULE: ShowFn<ImageElem> = |elem, engine, styles| {
|
const IMAGE_RULE: ShowFn<ImageElem> = |elem, engine, styles| {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use crate::foundations::{
|
||||||
use crate::introspection::{Introspector, Locator, SplitLocator};
|
use crate::introspection::{Introspector, Locator, SplitLocator};
|
||||||
use crate::layout::{Frame, Region};
|
use crate::layout::{Frame, Region};
|
||||||
use crate::model::DocumentInfo;
|
use crate::model::DocumentInfo;
|
||||||
|
use crate::visualize::Color;
|
||||||
|
|
||||||
/// Defines the `Routines` struct.
|
/// Defines the `Routines` struct.
|
||||||
macro_rules! routines {
|
macro_rules! routines {
|
||||||
|
|
@ -95,6 +96,12 @@ routines! {
|
||||||
|
|
||||||
/// Constructs the `html` module.
|
/// Constructs the `html` module.
|
||||||
fn html_module() -> Module
|
fn html_module() -> Module
|
||||||
|
|
||||||
|
/// Wraps content in a span with a color.
|
||||||
|
///
|
||||||
|
/// This is a temporary workaround until `TextElem::fill` is supported in
|
||||||
|
/// HTML export.
|
||||||
|
fn html_span_filled(content: Content, color: Color) -> Content
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines what kind of realization we are performing.
|
/// Defines what kind of realization we are performing.
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@ use crate::diag::{
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Bytes, Content, Derived, OneOrMultiple, Packed, PlainText, ShowSet, Smart,
|
Bytes, Content, Derived, OneOrMultiple, Packed, PlainText, ShowSet, Smart,
|
||||||
StyleChain, Styles, Synthesize, cast, elem, scope,
|
StyleChain, Styles, Synthesize, Target, TargetElem, cast, elem, scope,
|
||||||
};
|
};
|
||||||
use crate::layout::{Em, HAlignment};
|
use crate::layout::{Em, HAlignment};
|
||||||
use crate::loading::{DataSource, Load};
|
use crate::loading::{DataSource, Load};
|
||||||
use crate::model::{Figurable, ParElem};
|
use crate::model::{Figurable, ParElem};
|
||||||
|
use crate::routines::Routines;
|
||||||
use crate::text::{FontFamily, FontList, LocalName, TextElem, TextSize};
|
use crate::text::{FontFamily, FontList, LocalName, TextElem, TextSize};
|
||||||
use crate::visualize::Color;
|
use crate::visualize::Color;
|
||||||
|
|
||||||
|
|
@ -300,8 +301,12 @@ impl RawElem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for Packed<RawElem> {
|
impl Synthesize for Packed<RawElem> {
|
||||||
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
|
fn synthesize(
|
||||||
let seq = self.highlight(styles);
|
&mut self,
|
||||||
|
engine: &mut Engine,
|
||||||
|
styles: StyleChain,
|
||||||
|
) -> SourceResult<()> {
|
||||||
|
let seq = self.highlight(engine.routines, styles);
|
||||||
self.lines = Some(seq);
|
self.lines = Some(seq);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +314,7 @@ impl Synthesize for Packed<RawElem> {
|
||||||
|
|
||||||
impl Packed<RawElem> {
|
impl Packed<RawElem> {
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
fn highlight(&self, styles: StyleChain) -> Vec<Packed<RawLine>> {
|
fn highlight(&self, routines: &Routines, styles: StyleChain) -> Vec<Packed<RawLine>> {
|
||||||
let elem = self.as_ref();
|
let elem = self.as_ref();
|
||||||
let lines = preprocess(&elem.text, styles, self.span());
|
let lines = preprocess(&elem.text, styles, self.span());
|
||||||
|
|
||||||
|
|
@ -341,6 +346,7 @@ impl Packed<RawElem> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let foreground = theme.settings.foreground.unwrap_or(synt::Color::BLACK);
|
let foreground = theme.settings.foreground.unwrap_or(synt::Color::BLACK);
|
||||||
|
let target = styles.get(TargetElem::target);
|
||||||
|
|
||||||
let mut seq = vec![];
|
let mut seq = vec![];
|
||||||
if matches!(lang.as_deref(), Some("typ" | "typst" | "typc" | "typm")) {
|
if matches!(lang.as_deref(), Some("typ" | "typst" | "typc" | "typm")) {
|
||||||
|
|
@ -363,7 +369,15 @@ impl Packed<RawElem> {
|
||||||
let span_offset = text[..range.start]
|
let span_offset = text[..range.start]
|
||||||
.rfind('\n')
|
.rfind('\n')
|
||||||
.map_or(0, |i| range.start - (i + 1));
|
.map_or(0, |i| range.start - (i + 1));
|
||||||
styled(&text[range], foreground, style, span, span_offset)
|
styled(
|
||||||
|
routines,
|
||||||
|
target,
|
||||||
|
&text[range],
|
||||||
|
foreground,
|
||||||
|
style,
|
||||||
|
span,
|
||||||
|
span_offset,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
&mut |i, range, line| {
|
&mut |i, range, line| {
|
||||||
let span = lines.get(i).map_or_else(Span::detached, |l| l.1);
|
let span = lines.get(i).map_or_else(Span::detached, |l| l.1);
|
||||||
|
|
@ -400,6 +414,8 @@ impl Packed<RawElem> {
|
||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
line_content.push(styled(
|
line_content.push(styled(
|
||||||
|
routines,
|
||||||
|
target,
|
||||||
piece,
|
piece,
|
||||||
foreground,
|
foreground,
|
||||||
style,
|
style,
|
||||||
|
|
@ -769,6 +785,8 @@ fn preprocess(
|
||||||
|
|
||||||
/// Style a piece of text with a syntect style.
|
/// Style a piece of text with a syntect style.
|
||||||
fn styled(
|
fn styled(
|
||||||
|
routines: &Routines,
|
||||||
|
target: Target,
|
||||||
piece: &str,
|
piece: &str,
|
||||||
foreground: synt::Color,
|
foreground: synt::Color,
|
||||||
style: synt::Style,
|
style: synt::Style,
|
||||||
|
|
@ -782,7 +800,11 @@ fn styled(
|
||||||
}
|
}
|
||||||
|
|
||||||
if style.foreground != foreground {
|
if style.foreground != foreground {
|
||||||
body = body.set(TextElem::fill, to_typst(style.foreground).into());
|
let color = to_typst(style.foreground);
|
||||||
|
body = match target {
|
||||||
|
Target::Html => (routines.html_span_filled)(body, color),
|
||||||
|
Target::Paged => body.set(TextElem::fill, color.into()),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if style.font_style.contains(synt::FontStyle::BOLD) {
|
if style.font_style.contains(synt::FontStyle::BOLD) {
|
||||||
|
|
|
||||||
|
|
@ -358,4 +358,5 @@ pub static ROUTINES: LazyLock<Routines> = LazyLock::new(|| Routines {
|
||||||
realize: typst_realize::realize,
|
realize: typst_realize::realize,
|
||||||
layout_frame: typst_layout::layout_frame,
|
layout_frame: typst_layout::layout_frame,
|
||||||
html_module: typst_html::module,
|
html_module: typst_html::module,
|
||||||
|
html_span_filled: typst_html::html_span_filled,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>This is <code><strong>*</strong><strong>inline</strong><strong>*</strong></code>.</p>
|
<p>This is <code><strong>*</strong><strong>inline</strong><strong>*</strong></code>.</p>
|
||||||
<pre><code>#set text(blue)<br><strong>*</strong><strong>Hello</strong><strong>*</strong> <em>_</em><em>world</em><em>_</em>!</code></pre>
|
<pre><code><span style="color: #d73a49">#</span><span style="color: #d73a49">set</span> <span style="color: #4b69c6">text</span>(blue)<br><strong>*</strong><strong>Hello</strong><strong>*</strong> <em>_</em><em>world</em><em>_</em>!</code></pre>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue