summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fonts/IBMPlexMono-Bold.ttfbin0 -> 111944 bytes
-rw-r--r--src/eval/func.rs10
-rw-r--r--src/eval/value.rs22
-rw-r--r--src/geom/paint.rs5
-rw-r--r--src/library/text/raw.rs76
-rw-r--r--src/parse/mod.rs30
-rw-r--r--src/syntax/highlight.rs6
-rw-r--r--tests/ref/code/array.pngbin2534 -> 6191 bytes
-rw-r--r--tests/ref/code/call.pngbin3388 -> 2624 bytes
-rw-r--r--tests/ref/code/dict.pngbin1126 -> 1973 bytes
-rw-r--r--tests/ref/code/repr.pngbin32238 -> 30668 bytes
-rw-r--r--tests/ref/graphics/shape-rect.pngbin16331 -> 16279 bytes
-rw-r--r--tests/ref/text/code.pngbin0 -> 38850 bytes
-rw-r--r--tests/ref/text/edge.pngbin15857 -> 16107 bytes
-rw-r--r--tests/ref/text/par.pngbin30068 -> 30060 bytes
-rw-r--r--tests/ref/text/raw.pngbin22841 -> 18090 bytes
-rw-r--r--tests/typ/code/array.typ2
-rw-r--r--tests/typ/code/call.typ1
-rw-r--r--tests/typ/code/repr.typ15
-rw-r--r--tests/typ/text/code.typ57
-rw-r--r--tests/typ/text/microtype.typ2
-rw-r--r--tests/typ/text/raw.typ10
-rw-r--r--tools/support/typst.tmLanguage.json2
23 files changed, 170 insertions, 68 deletions
diff --git a/fonts/IBMPlexMono-Bold.ttf b/fonts/IBMPlexMono-Bold.ttf
new file mode 100644
index 00000000..2ad2fa1d
--- /dev/null
+++ b/fonts/IBMPlexMono-Bold.ttf
Binary files differ
diff --git a/src/eval/func.rs b/src/eval/func.rs
index 7e4040b6..b72e9f18 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -1,4 +1,4 @@
-use std::fmt::{self, Debug, Formatter, Write};
+use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
@@ -119,12 +119,10 @@ impl Func {
impl Debug for Func {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("<function")?;
- if let Some(name) = self.name() {
- f.write_char(' ')?;
- f.write_str(name)?;
+ match self.name() {
+ Some(name) => f.write_str(name),
+ None => f.write_str("(..) => {..}"),
}
- f.write_char('>')
}
}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index fc54cbce..b5607cfd 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -122,8 +122,9 @@ impl Value {
Value::Content(v) => v,
// For values which can't be shown "naturally", we return the raw
- // representation.
- v => Content::show(RawNode { text: v.repr(), block: false }),
+ // representation with typst code syntax highlighting.
+ v => Content::show(RawNode { text: v.repr(), block: false })
+ .styled(RawNode::LANG, Some("typc".into())),
}
}
}
@@ -149,7 +150,7 @@ impl Debug for Value {
Self::Fraction(v) => Debug::fmt(v, f),
Self::Color(v) => Debug::fmt(v, f),
Self::Str(v) => Debug::fmt(v, f),
- Self::Content(_) => f.pad("<content>"),
+ Self::Content(_) => f.pad("[...]"),
Self::Array(v) => Debug::fmt(v, f),
Self::Dict(v) => Debug::fmt(v, f),
Self::Func(v) => Debug::fmt(v, f),
@@ -720,7 +721,10 @@ mod tests {
"30% + 56.69pt",
);
test(Fraction::one() * 7.55, "7.55fr");
- test(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101");
+ test(
+ Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)),
+ "rgb(\"#010101\")",
+ );
// Collections.
test("hello", r#""hello""#);
@@ -734,13 +738,9 @@ mod tests {
test(dict!["one" => 1], "(one: 1)");
test(dict!["two" => false, "one" => 1], "(one: 1, two: false)");
- // Functions.
- test(
- Func::from_fn("nil", |_, _| Ok(Value::None)),
- "<function nil>",
- );
-
- // Dynamics.
+ // Functions, content and dynamics.
+ test(Content::Text("a".into()), "[...]");
+ test(Func::from_fn("nil", |_, _| Ok(Value::None)), "nil");
test(Dynamic::new(1), "1");
}
}
diff --git a/src/geom/paint.rs b/src/geom/paint.rs
index 351ef443..9b310249 100644
--- a/src/geom/paint.rs
+++ b/src/geom/paint.rs
@@ -154,7 +154,7 @@ impl FromStr for RgbaColor {
impl From<SynColor> for RgbaColor {
fn from(color: SynColor) -> Self {
- Self::new(color.r, color.b, color.g, color.a)
+ Self::new(color.r, color.g, color.b, color.a)
}
}
@@ -167,10 +167,11 @@ impl Debug for RgbaColor {
self.r, self.g, self.b, self.a,
)?;
} else {
- write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?;
+ write!(f, "rgb(\"#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?;
if self.a != 255 {
write!(f, "{:02x}", self.a)?;
}
+ write!(f, "\")")?;
}
Ok(())
}
diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs
index c711aa16..d5a3aa95 100644
--- a/src/library/text/raw.rs
+++ b/src/library/text/raw.rs
@@ -1,20 +1,17 @@
+use std::sync::Arc;
+
use once_cell::sync::Lazy;
use syntect::easy::HighlightLines;
-use syntect::highlighting::{FontStyle, Highlighter, Style, Theme, ThemeSet};
+use syntect::highlighting::{
+ Color, FontStyle, Highlighter, Style, StyleModifier, Theme, ThemeItem, ThemeSettings,
+};
use syntect::parsing::SyntaxSet;
use super::{FontFamily, Hyphenate, TextNode, Toggle};
use crate::library::layout::BlockSpacing;
use crate::library::prelude::*;
use crate::source::SourceId;
-use crate::syntax::{self, RedNode};
-
-/// The lazily-loaded theme used for syntax highlighting.
-static THEME: Lazy<Theme> =
- Lazy::new(|| ThemeSet::load_defaults().themes.remove("InspiredGitHub").unwrap());
-
-/// The lazily-loaded syntect syntax definitions.
-static SYNTAXES: Lazy<SyntaxSet> = Lazy::new(|| SyntaxSet::load_defaults_newlines());
+use crate::syntax::{self, GreenNode, NodeKind, RedNode};
/// Monospaced text with optional syntax highlighting.
#[derive(Debug, Hash)]
@@ -63,7 +60,7 @@ impl Show for RawNode {
}
fn realize(&self, _: &mut Context, styles: StyleChain) -> TypResult<Content> {
- let lang = styles.get(Self::LANG).as_ref();
+ let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
let foreground = THEME
.settings
.foreground
@@ -71,15 +68,19 @@ impl Show for RawNode {
.unwrap_or(Color::BLACK)
.into();
- let mut realized = if matches!(
- lang.map(|s| s.to_lowercase()).as_deref(),
- Some("typ" | "typst")
- ) {
- let mut seq = vec![];
- let green = crate::parse::parse(&self.text);
- let red = RedNode::from_root(green, SourceId::from_raw(0));
+ let mut realized = if matches!(lang.as_deref(), Some("typ" | "typst" | "typc")) {
+ let root = match lang.as_deref() {
+ Some("typc") => {
+ let children = crate::parse::parse_code(&self.text);
+ Arc::new(GreenNode::with_children(NodeKind::CodeBlock, children))
+ }
+ _ => crate::parse::parse(&self.text),
+ };
+
+ let red = RedNode::from_root(root, SourceId::from_raw(0));
let highlighter = Highlighter::new(&THEME);
+ let mut seq = vec![];
syntax::highlight_syntect(red.as_ref(), &highlighter, &mut |range, style| {
seq.push(styled(&self.text[range], foreground, style));
});
@@ -159,3 +160,44 @@ fn styled(piece: &str, foreground: Paint, style: Style) -> Content {
body.styled_with_map(styles)
}
+
+/// The lazily-loaded syntect syntax definitions.
+static SYNTAXES: Lazy<SyntaxSet> = Lazy::new(|| SyntaxSet::load_defaults_newlines());
+
+/// The lazily-loaded theme used for syntax highlighting.
+#[rustfmt::skip]
+static THEME: Lazy<Theme> = Lazy::new(|| Theme {
+ name: Some("Typst Light".into()),
+ author: Some("The Typst Project Developers".into()),
+ settings: ThemeSettings::default(),
+ scopes: vec![
+ item("markup.bold", None, Some(FontStyle::BOLD)),
+ item("markup.italic", None, Some(FontStyle::ITALIC)),
+ item("markup.heading, entity.name.section", None, Some(FontStyle::BOLD)),
+ item("markup.raw", Some("#818181"), None),
+ item("markup.list", Some("#8b41b1"), None),
+ item("comment", Some("#8a8a8a"), None),
+ item("keyword, constant.language, variable.language", Some("#d73a49"), None),
+ item("storage.type, storage.modifier", Some("#d73a49"), None),
+ item("entity.other", Some("#8b41b1"), None),
+ item("entity.name, variable.function, support", Some("#4b69c6"), None),
+ item("support.macro", Some("#16718d"), None),
+ item("meta.annotation", Some("#301414"), None),
+ item("constant", Some("#b60157"), None),
+ item("string", Some("#298e0d"), None),
+ item("punctuation.shortcut", Some("#1d6c76"), None),
+ item("constant.character.escape", Some("#1d6c76"), None),
+ ],
+});
+
+/// Create a syntect theme item.
+fn item(scope: &str, color: Option<&str>, font_style: Option<FontStyle>) -> ThemeItem {
+ ThemeItem {
+ scope: scope.parse().unwrap(),
+ style: StyleModifier {
+ foreground: color.map(|s| s.parse().unwrap()),
+ background: None,
+ font_style,
+ },
+ }
+}
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 9f4860e7..7811482b 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -26,6 +26,13 @@ pub fn parse(src: &str) -> Arc<GreenNode> {
}
}
+/// Parse code directly, only used for syntax highlighting.
+pub fn parse_code(src: &str) -> Vec<Green> {
+ let mut p = Parser::new(src, TokenMode::Code);
+ code(&mut p);
+ p.finish()
+}
+
/// Reparse a code block.
///
/// Returns `Some` if all of the input was consumed.
@@ -696,18 +703,23 @@ fn params(p: &mut Parser, marker: Marker) {
fn code_block(p: &mut Parser) {
p.perform(NodeKind::CodeBlock, |p| {
p.start_group(Group::Brace);
- while !p.eof() {
- p.start_group(Group::Expr);
- if expr(p).is_ok() && !p.eof() {
- p.expected("semicolon or line break");
- }
- p.end_group();
+ code(p);
+ p.end_group();
+ });
+}
- // Forcefully skip over newlines since the group's contents can't.
- p.eat_while(|t| matches!(t, NodeKind::Space(_)));
+/// Parse expressions.
+fn code(p: &mut Parser) {
+ while !p.eof() {
+ p.start_group(Group::Expr);
+ if expr(p).is_ok() && !p.eof() {
+ p.expected("semicolon or line break");
}
p.end_group();
- });
+
+ // Forcefully skip over newlines since the group's contents can't.
+ p.eat_while(|t| matches!(t, NodeKind::Space(_)));
+ }
}
// Parse a content block: `[...]`.
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
index 8e62424f..dae379ac 100644
--- a/src/syntax/highlight.rs
+++ b/src/syntax/highlight.rs
@@ -95,7 +95,7 @@ pub enum Category {
/// A function.
Function,
/// An interpolated variable in markup.
- Variable,
+ Interpolated,
/// An invalid node.
Invalid,
}
@@ -178,7 +178,7 @@ impl Category {
NodeKind::None => Some(Category::None),
NodeKind::Auto => Some(Category::Auto),
NodeKind::Ident(_) => match parent.kind() {
- NodeKind::Markup(_) => Some(Category::Variable),
+ NodeKind::Markup(_) => Some(Category::Interpolated),
NodeKind::FuncCall => Some(Category::Function),
NodeKind::MethodCall if i > 0 => Some(Category::Function),
NodeKind::ClosureExpr if i == 0 => Some(Category::Function),
@@ -263,7 +263,7 @@ impl Category {
Self::Number => "constant.numeric.typst",
Self::String => "string.quoted.double.typst",
Self::Function => "entity.name.function.typst",
- Self::Variable => "variable.parameter.typst",
+ Self::Interpolated => "entity.other.interpolated.typst",
Self::Invalid => "invalid.typst",
}
}
diff --git a/tests/ref/code/array.png b/tests/ref/code/array.png
index 613aab97..cbda8aee 100644
--- a/tests/ref/code/array.png
+++ b/tests/ref/code/array.png
Binary files differ
diff --git a/tests/ref/code/call.png b/tests/ref/code/call.png
index eefc8cae..eaf77ad6 100644
--- a/tests/ref/code/call.png
+++ b/tests/ref/code/call.png
Binary files differ
diff --git a/tests/ref/code/dict.png b/tests/ref/code/dict.png
index 43cf1370..28e44999 100644
--- a/tests/ref/code/dict.png
+++ b/tests/ref/code/dict.png
Binary files differ
diff --git a/tests/ref/code/repr.png b/tests/ref/code/repr.png
index b6427518..b8beb09a 100644
--- a/tests/ref/code/repr.png
+++ b/tests/ref/code/repr.png
Binary files differ
diff --git a/tests/ref/graphics/shape-rect.png b/tests/ref/graphics/shape-rect.png
index 5ba78f97..2c2c7d7c 100644
--- a/tests/ref/graphics/shape-rect.png
+++ b/tests/ref/graphics/shape-rect.png
Binary files differ
diff --git a/tests/ref/text/code.png b/tests/ref/text/code.png
new file mode 100644
index 00000000..b012c2cf
--- /dev/null
+++ b/tests/ref/text/code.png
Binary files differ
diff --git a/tests/ref/text/edge.png b/tests/ref/text/edge.png
index 0514d628..817e3300 100644
--- a/tests/ref/text/edge.png
+++ b/tests/ref/text/edge.png
Binary files differ
diff --git a/tests/ref/text/par.png b/tests/ref/text/par.png
index ae00ab45..6ee4aa9c 100644
--- a/tests/ref/text/par.png
+++ b/tests/ref/text/par.png
Binary files differ
diff --git a/tests/ref/text/raw.png b/tests/ref/text/raw.png
index e13293a6..2c101dcb 100644
--- a/tests/ref/text/raw.png
+++ b/tests/ref/text/raw.png
Binary files differ
diff --git a/tests/typ/code/array.typ b/tests/typ/code/array.typ
index cd163175..58b43ebf 100644
--- a/tests/typ/code/array.typ
+++ b/tests/typ/code/array.typ
@@ -4,6 +4,8 @@
---
// Ref: true
+#set page(width: 150pt)
+
// Empty.
{()}
diff --git a/tests/typ/code/call.typ b/tests/typ/code/call.typ
index 55471774..5b8b5b05 100644
--- a/tests/typ/code/call.typ
+++ b/tests/typ/code/call.typ
@@ -13,7 +13,6 @@
#f(1)[2](3)
// Don't parse this as a function.
-// Should output `<function test> (it)`.
#test (it)
#let f(body) = body
diff --git a/tests/typ/code/repr.typ b/tests/typ/code/repr.typ
index 8742f413..4d7a6cd5 100644
--- a/tests/typ/code/repr.typ
+++ b/tests/typ/code/repr.typ
@@ -25,23 +25,24 @@
{2.3fr}
---
-// Colors.
-#rgb("f7a20500") \
-{2pt + rgb("f7a20500")}
+// Colors and strokes.
+#set text(0.8em)
+#rgb("f7a205") \
+{2pt + rgb("f7a205")}
---
// Strings and escaping.
-#repr("hi") \
+#raw(repr("hi"), lang: "typc") \
#repr("a\n[]\"\u{1F680}string")
---
// Content.
-#repr[*{"H" + "i"} there*]
+#raw(repr[*{"H" + "i"} there*])
---
// Functions
#let f(x) = x
-{() => none} \
{f} \
-{rect}
+{rect} \
+{() => none} \
diff --git a/tests/typ/text/code.typ b/tests/typ/text/code.typ
new file mode 100644
index 00000000..4230dd87
--- /dev/null
+++ b/tests/typ/text/code.typ
@@ -0,0 +1,57 @@
+// Test code highlighting.
+
+---
+#set text(6pt)
+```typ
+= Chapter 1
+#lorem(100)
+
+#let hi = "Hello World"
+```
+
+---
+#set page(width: 180pt)
+#set text(6pt)
+
+```rust
+/// A carefully designed state machine.
+#[derive(Debug)]
+enum State<'a> { A(u8), B(&'a str) }
+
+fn advance(state: State<'_>) -> State<'_> {
+ unimplemented!("state machine")
+}
+```
+
+---
+#set text(6pt)
+```py
+import this
+
+def hi():
+ print("Hi!")
+```
+
+---
+#set page(width: 180pt)
+#set text(6pt)
+
+#rect(inset: (x: 4pt, y: 5pt), radius: 4pt, fill: rgb(239, 241, 243))[
+ ```html
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <h1>Topic</h1>
+ <p>The Hypertext Markup Language.</p>
+ <script>
+ function foo(a, b) {
+ return a + b + "string";
+ }
+ </script>
+ </body>
+ </html>
+ ```
+]
diff --git a/tests/typ/text/microtype.typ b/tests/typ/text/microtype.typ
index 991e0d0d..28d29445 100644
--- a/tests/typ/text/microtype.typ
+++ b/tests/typ/text/microtype.typ
@@ -5,7 +5,7 @@
#set page(width: 130pt, margins: 15pt)
#set par(justify: true, linebreaks: "simple")
#set text(size: 9pt)
-#rect(fill: rgb(repr(teal) + "00"), width: 100%)[
+#rect(fill: rgb(0, 0, 0, 0), width: 100%)[
This is a little bit of text that builds up to
hang-ing hyphens and dash---es and then, you know,
some punctuation in the margin.
diff --git a/tests/typ/text/raw.typ b/tests/typ/text/raw.typ
index 33b08568..cf497d75 100644
--- a/tests/typ/text/raw.typ
+++ b/tests/typ/text/raw.typ
@@ -37,16 +37,6 @@ The keyword ```rust let```.
<``` trimmed ```> \
<``` trimmed```>
-// Multiline trimming and dedenting.
-#block[
- ```py
- import this
-
- def hi():
- print("Hi!")
- ```
-]
-
---
// First line is not dedented and leading space is still possible.
``` A
diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json
index e1dd0ba1..0475af43 100644
--- a/tools/support/typst.tmLanguage.json
+++ b/tools/support/typst.tmLanguage.json
@@ -179,7 +179,7 @@
"patterns": [{ "include": "#arguments" }]
},
{
- "name": "variable.interpolated.typst",
+ "name": "entity.other.interpolated.typst",
"match": "(#)[[:alpha:]_][[:alnum:]_-]*",
"captures": { "1": { "name": "punctuation.definition.variable.typst" } }
}