New raw theme & nicer debug representation
BIN
fonts/IBMPlexMono-Bold.ttf
Normal file
@ -1,4 +1,4 @@
|
|||||||
use std::fmt::{self, Debug, Formatter, Write};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -119,12 +119,10 @@ impl Func {
|
|||||||
|
|
||||||
impl Debug for Func {
|
impl Debug for Func {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.write_str("<function")?;
|
match self.name() {
|
||||||
if let Some(name) = self.name() {
|
Some(name) => f.write_str(name),
|
||||||
f.write_char(' ')?;
|
None => f.write_str("(..) => {..}"),
|
||||||
f.write_str(name)?;
|
|
||||||
}
|
}
|
||||||
f.write_char('>')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,8 +122,9 @@ impl Value {
|
|||||||
Value::Content(v) => v,
|
Value::Content(v) => v,
|
||||||
|
|
||||||
// For values which can't be shown "naturally", we return the raw
|
// For values which can't be shown "naturally", we return the raw
|
||||||
// representation.
|
// representation with typst code syntax highlighting.
|
||||||
v => Content::show(RawNode { text: v.repr(), block: false }),
|
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::Fraction(v) => Debug::fmt(v, f),
|
||||||
Self::Color(v) => Debug::fmt(v, f),
|
Self::Color(v) => Debug::fmt(v, f),
|
||||||
Self::Str(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::Array(v) => Debug::fmt(v, f),
|
||||||
Self::Dict(v) => Debug::fmt(v, f),
|
Self::Dict(v) => Debug::fmt(v, f),
|
||||||
Self::Func(v) => Debug::fmt(v, f),
|
Self::Func(v) => Debug::fmt(v, f),
|
||||||
@ -720,7 +721,10 @@ mod tests {
|
|||||||
"30% + 56.69pt",
|
"30% + 56.69pt",
|
||||||
);
|
);
|
||||||
test(Fraction::one() * 7.55, "7.55fr");
|
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.
|
// Collections.
|
||||||
test("hello", r#""hello""#);
|
test("hello", r#""hello""#);
|
||||||
@ -734,13 +738,9 @@ mod tests {
|
|||||||
test(dict!["one" => 1], "(one: 1)");
|
test(dict!["one" => 1], "(one: 1)");
|
||||||
test(dict!["two" => false, "one" => 1], "(one: 1, two: false)");
|
test(dict!["two" => false, "one" => 1], "(one: 1, two: false)");
|
||||||
|
|
||||||
// Functions.
|
// Functions, content and dynamics.
|
||||||
test(
|
test(Content::Text("a".into()), "[...]");
|
||||||
Func::from_fn("nil", |_, _| Ok(Value::None)),
|
test(Func::from_fn("nil", |_, _| Ok(Value::None)), "nil");
|
||||||
"<function nil>",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dynamics.
|
|
||||||
test(Dynamic::new(1), "1");
|
test(Dynamic::new(1), "1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ impl FromStr for RgbaColor {
|
|||||||
|
|
||||||
impl From<SynColor> for RgbaColor {
|
impl From<SynColor> for RgbaColor {
|
||||||
fn from(color: SynColor) -> Self {
|
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,
|
self.r, self.g, self.b, self.a,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} 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 {
|
if self.a != 255 {
|
||||||
write!(f, "{:02x}", self.a)?;
|
write!(f, "{:02x}", self.a)?;
|
||||||
}
|
}
|
||||||
|
write!(f, "\")")?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use syntect::easy::HighlightLines;
|
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 syntect::parsing::SyntaxSet;
|
||||||
|
|
||||||
use super::{FontFamily, Hyphenate, TextNode, Toggle};
|
use super::{FontFamily, Hyphenate, TextNode, Toggle};
|
||||||
use crate::library::layout::BlockSpacing;
|
use crate::library::layout::BlockSpacing;
|
||||||
use crate::library::prelude::*;
|
use crate::library::prelude::*;
|
||||||
use crate::source::SourceId;
|
use crate::source::SourceId;
|
||||||
use crate::syntax::{self, RedNode};
|
use crate::syntax::{self, GreenNode, NodeKind, 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());
|
|
||||||
|
|
||||||
/// Monospaced text with optional syntax highlighting.
|
/// Monospaced text with optional syntax highlighting.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
@ -63,7 +60,7 @@ impl Show for RawNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &mut Context, styles: StyleChain) -> TypResult<Content> {
|
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
|
let foreground = THEME
|
||||||
.settings
|
.settings
|
||||||
.foreground
|
.foreground
|
||||||
@ -71,15 +68,19 @@ impl Show for RawNode {
|
|||||||
.unwrap_or(Color::BLACK)
|
.unwrap_or(Color::BLACK)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let mut realized = if matches!(
|
let mut realized = if matches!(lang.as_deref(), Some("typ" | "typst" | "typc")) {
|
||||||
lang.map(|s| s.to_lowercase()).as_deref(),
|
let root = match lang.as_deref() {
|
||||||
Some("typ" | "typst")
|
Some("typc") => {
|
||||||
) {
|
let children = crate::parse::parse_code(&self.text);
|
||||||
let mut seq = vec![];
|
Arc::new(GreenNode::with_children(NodeKind::CodeBlock, children))
|
||||||
let green = crate::parse::parse(&self.text);
|
}
|
||||||
let red = RedNode::from_root(green, SourceId::from_raw(0));
|
_ => crate::parse::parse(&self.text),
|
||||||
|
};
|
||||||
|
|
||||||
|
let red = RedNode::from_root(root, SourceId::from_raw(0));
|
||||||
let highlighter = Highlighter::new(&THEME);
|
let highlighter = Highlighter::new(&THEME);
|
||||||
|
|
||||||
|
let mut seq = vec![];
|
||||||
syntax::highlight_syntect(red.as_ref(), &highlighter, &mut |range, style| {
|
syntax::highlight_syntect(red.as_ref(), &highlighter, &mut |range, style| {
|
||||||
seq.push(styled(&self.text[range], foreground, 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)
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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.
|
/// Reparse a code block.
|
||||||
///
|
///
|
||||||
/// Returns `Some` if all of the input was consumed.
|
/// Returns `Some` if all of the input was consumed.
|
||||||
@ -696,20 +703,25 @@ fn params(p: &mut Parser, marker: Marker) {
|
|||||||
fn code_block(p: &mut Parser) {
|
fn code_block(p: &mut Parser) {
|
||||||
p.perform(NodeKind::CodeBlock, |p| {
|
p.perform(NodeKind::CodeBlock, |p| {
|
||||||
p.start_group(Group::Brace);
|
p.start_group(Group::Brace);
|
||||||
while !p.eof() {
|
code(p);
|
||||||
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(_)));
|
|
||||||
}
|
|
||||||
p.end_group();
|
p.end_group();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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: `[...]`.
|
// Parse a content block: `[...]`.
|
||||||
fn content_block(p: &mut Parser) {
|
fn content_block(p: &mut Parser) {
|
||||||
p.perform(NodeKind::ContentBlock, |p| {
|
p.perform(NodeKind::ContentBlock, |p| {
|
||||||
|
@ -95,7 +95,7 @@ pub enum Category {
|
|||||||
/// A function.
|
/// A function.
|
||||||
Function,
|
Function,
|
||||||
/// An interpolated variable in markup.
|
/// An interpolated variable in markup.
|
||||||
Variable,
|
Interpolated,
|
||||||
/// An invalid node.
|
/// An invalid node.
|
||||||
Invalid,
|
Invalid,
|
||||||
}
|
}
|
||||||
@ -178,7 +178,7 @@ impl Category {
|
|||||||
NodeKind::None => Some(Category::None),
|
NodeKind::None => Some(Category::None),
|
||||||
NodeKind::Auto => Some(Category::Auto),
|
NodeKind::Auto => Some(Category::Auto),
|
||||||
NodeKind::Ident(_) => match parent.kind() {
|
NodeKind::Ident(_) => match parent.kind() {
|
||||||
NodeKind::Markup(_) => Some(Category::Variable),
|
NodeKind::Markup(_) => Some(Category::Interpolated),
|
||||||
NodeKind::FuncCall => Some(Category::Function),
|
NodeKind::FuncCall => Some(Category::Function),
|
||||||
NodeKind::MethodCall if i > 0 => Some(Category::Function),
|
NodeKind::MethodCall if i > 0 => Some(Category::Function),
|
||||||
NodeKind::ClosureExpr 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::Number => "constant.numeric.typst",
|
||||||
Self::String => "string.quoted.double.typst",
|
Self::String => "string.quoted.double.typst",
|
||||||
Self::Function => "entity.name.function.typst",
|
Self::Function => "entity.name.function.typst",
|
||||||
Self::Variable => "variable.parameter.typst",
|
Self::Interpolated => "entity.other.interpolated.typst",
|
||||||
Self::Invalid => "invalid.typst",
|
Self::Invalid => "invalid.typst",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
BIN
tests/ref/text/code.png
Normal file
After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 18 KiB |
@ -4,6 +4,8 @@
|
|||||||
---
|
---
|
||||||
// Ref: true
|
// Ref: true
|
||||||
|
|
||||||
|
#set page(width: 150pt)
|
||||||
|
|
||||||
// Empty.
|
// Empty.
|
||||||
{()}
|
{()}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
#f(1)[2](3)
|
#f(1)[2](3)
|
||||||
|
|
||||||
// Don't parse this as a function.
|
// Don't parse this as a function.
|
||||||
// Should output `<function test> (it)`.
|
|
||||||
#test (it)
|
#test (it)
|
||||||
|
|
||||||
#let f(body) = body
|
#let f(body) = body
|
||||||
|
@ -25,23 +25,24 @@
|
|||||||
{2.3fr}
|
{2.3fr}
|
||||||
|
|
||||||
---
|
---
|
||||||
// Colors.
|
// Colors and strokes.
|
||||||
#rgb("f7a20500") \
|
#set text(0.8em)
|
||||||
{2pt + rgb("f7a20500")}
|
#rgb("f7a205") \
|
||||||
|
{2pt + rgb("f7a205")}
|
||||||
|
|
||||||
---
|
---
|
||||||
// Strings and escaping.
|
// Strings and escaping.
|
||||||
#repr("hi") \
|
#raw(repr("hi"), lang: "typc") \
|
||||||
#repr("a\n[]\"\u{1F680}string")
|
#repr("a\n[]\"\u{1F680}string")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Content.
|
// Content.
|
||||||
#repr[*{"H" + "i"} there*]
|
#raw(repr[*{"H" + "i"} there*])
|
||||||
|
|
||||||
---
|
---
|
||||||
// Functions
|
// Functions
|
||||||
#let f(x) = x
|
#let f(x) = x
|
||||||
|
|
||||||
{() => none} \
|
|
||||||
{f} \
|
{f} \
|
||||||
{rect}
|
{rect} \
|
||||||
|
{() => none} \
|
||||||
|
57
tests/typ/text/code.typ
Normal file
@ -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>
|
||||||
|
```
|
||||||
|
]
|
@ -5,7 +5,7 @@
|
|||||||
#set page(width: 130pt, margins: 15pt)
|
#set page(width: 130pt, margins: 15pt)
|
||||||
#set par(justify: true, linebreaks: "simple")
|
#set par(justify: true, linebreaks: "simple")
|
||||||
#set text(size: 9pt)
|
#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
|
This is a little bit of text that builds up to
|
||||||
hang-ing hyphens and dash---es and then, you know,
|
hang-ing hyphens and dash---es and then, you know,
|
||||||
some punctuation in the margin.
|
some punctuation in the margin.
|
||||||
|
@ -37,16 +37,6 @@ The keyword ```rust let```.
|
|||||||
<``` trimmed ```> \
|
<``` trimmed ```> \
|
||||||
<``` 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.
|
// First line is not dedented and leading space is still possible.
|
||||||
``` A
|
``` A
|
||||||
|
@ -179,7 +179,7 @@
|
|||||||
"patterns": [{ "include": "#arguments" }]
|
"patterns": [{ "include": "#arguments" }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "variable.interpolated.typst",
|
"name": "entity.other.interpolated.typst",
|
||||||
"match": "(#)[[:alpha:]_][[:alnum:]_-]*",
|
"match": "(#)[[:alpha:]_][[:alnum:]_-]*",
|
||||||
"captures": { "1": { "name": "punctuation.definition.variable.typst" } }
|
"captures": { "1": { "name": "punctuation.definition.variable.typst" } }
|
||||||
}
|
}
|
||||||
|