summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval/mod.rs2
-rw-r--r--src/library/mod.rs1
-rw-r--r--src/library/structure/mod.rs2
-rw-r--r--src/library/structure/reference.rs28
-rw-r--r--src/library/text/raw.rs5
-rw-r--r--src/parse/mod.rs30
-rw-r--r--src/parse/tokens.rs31
-rw-r--r--src/syntax/ast.rs6
-rw-r--r--src/syntax/highlight.rs34
-rw-r--r--src/syntax/mod.rs8
10 files changed, 119 insertions, 28 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 94d9ef40..7f182f48 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -203,6 +203,8 @@ impl Eval for MarkupNode {
Self::Heading(heading) => heading.eval(vm)?,
Self::List(list) => list.eval(vm)?,
Self::Enum(enum_) => enum_.eval(vm)?,
+ Self::Label(_) => Content::Empty,
+ Self::Ref(label) => Content::show(library::structure::RefNode(label.clone())),
Self::Expr(expr) => expr.eval(vm)?.display(),
})
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index bd34590a..c1a645fb 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -37,6 +37,7 @@ pub fn new() -> Scope {
std.def_fn("smallcaps", text::smallcaps);
// Structure.
+ std.def_node::<structure::RefNode>("ref");
std.def_node::<structure::HeadingNode>("heading");
std.def_node::<structure::ListNode>("list");
std.def_node::<structure::EnumNode>("enum");
diff --git a/src/library/structure/mod.rs b/src/library/structure/mod.rs
index a597211e..088d1e6c 100644
--- a/src/library/structure/mod.rs
+++ b/src/library/structure/mod.rs
@@ -3,9 +3,11 @@
mod doc;
mod heading;
mod list;
+mod reference;
mod table;
pub use doc::*;
pub use heading::*;
pub use list::*;
+pub use reference::*;
pub use table::*;
diff --git a/src/library/structure/reference.rs b/src/library/structure/reference.rs
new file mode 100644
index 00000000..0eeb4bf5
--- /dev/null
+++ b/src/library/structure/reference.rs
@@ -0,0 +1,28 @@
+use crate::library::prelude::*;
+
+/// A reference to a label.
+#[derive(Debug, Hash)]
+pub struct RefNode(pub EcoString);
+
+#[node(showable)]
+impl RefNode {
+ fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
+ Ok(Content::show(Self(args.expect("label")?)))
+ }
+}
+
+impl Show for RefNode {
+ fn unguard(&self, _: Selector) -> ShowNode {
+ Self(self.0.clone()).pack()
+ }
+
+ fn encode(&self, _: StyleChain) -> Dict {
+ dict! {
+ "label" => Value::Str(self.0.clone().into()),
+ }
+ }
+
+ fn realize(&self, _: &mut Context, _: StyleChain) -> TypResult<Content> {
+ Ok(Content::Text(format_eco!("@{}", self.0)))
+ }
+}
diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs
index e64636f8..a09d791d 100644
--- a/src/library/text/raw.rs
+++ b/src/library/text/raw.rs
@@ -169,6 +169,9 @@ pub static THEME: Lazy<Theme> = Lazy::new(|| Theme {
item("markup.raw", Some("#818181"), None),
item("markup.list", Some("#8b41b1"), None),
item("comment", Some("#8a8a8a"), None),
+ item("punctuation.shortcut", Some("#1d6c76"), None),
+ item("constant.character.escape", Some("#1d6c76"), None),
+ item("entity.name.label, markup.other.reference", Some("#1d6c76"), None),
item("keyword, constant.language, variable.language", Some("#d73a49"), None),
item("storage.type, storage.modifier", Some("#d73a49"), None),
item("entity.other", Some("#8b41b1"), None),
@@ -177,8 +180,6 @@ pub static THEME: Lazy<Theme> = Lazy::new(|| Theme {
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),
item("invalid", Some("#ff0000"), None),
],
});
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index a997421e..0737d4ca 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -175,18 +175,24 @@ fn markup_indented(p: &mut Parser, min_indent: usize) {
_ => false,
});
+ let marker = p.marker();
let mut at_start = false;
- p.perform(NodeKind::Markup { min_indent }, |p| {
- while !p.eof() {
- if let Some(NodeKind::Space { newlines: (1 ..) }) = p.peek() {
- if p.column(p.current_end()) < min_indent {
- break;
- }
- }
- markup_node(p, &mut at_start);
+ while !p.eof() {
+ match p.peek() {
+ Some(NodeKind::Space { newlines: (1 ..) })
+ if p.column(p.current_end()) < min_indent =>
+ {
+ break;
+ }
+ Some(NodeKind::Label(_)) => break,
+ _ => {}
}
- });
+
+ markup_node(p, &mut at_start);
+ }
+
+ marker.end(p, NodeKind::Markup { min_indent });
}
/// Parse a markup node.
@@ -212,16 +218,18 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
// Text and markup.
NodeKind::Text(_)
+ | NodeKind::Linebreak { .. }
| NodeKind::NonBreakingSpace
| NodeKind::Shy
| NodeKind::EnDash
| NodeKind::EmDash
| NodeKind::Ellipsis
| NodeKind::Quote { .. }
- | NodeKind::Linebreak { .. }
+ | NodeKind::Escape(_)
| NodeKind::Raw(_)
| NodeKind::Math(_)
- | NodeKind::Escape(_) => {
+ | NodeKind::Label(_)
+ | NodeKind::Ref(_) => {
p.eat();
}
diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs
index be107f3c..e004dd37 100644
--- a/src/parse/tokens.rs
+++ b/src/parse/tokens.rs
@@ -148,8 +148,10 @@ impl<'s> Tokens<'s> {
'*' if !self.in_word() => NodeKind::Star,
'_' if !self.in_word() => NodeKind::Underscore,
'`' => self.raw(),
- '$' => self.math(),
'=' => NodeKind::Eq,
+ '$' => self.math(),
+ '<' => self.label(),
+ '@' => self.reference(),
c if c == '.' || c.is_ascii_digit() => self.numbering(start, c),
// Plain text.
@@ -277,7 +279,9 @@ impl<'s> Tokens<'s> {
// Parenthesis and hashtag.
'[' | ']' | '{' | '}' | '#' |
// Markup.
- '~' | '\'' | '"' | '*' | '_' | '`' | '$' | '=' | '-' | '.' => {
+ '~' | '-' | '.' | ':' |
+ '\'' | '"' | '*' | '_' | '`' | '$' | '=' |
+ '<' | '>' | '@' => {
self.s.expect(c);
NodeKind::Escape(c)
}
@@ -453,6 +457,29 @@ impl<'s> Tokens<'s> {
}
}
+ fn label(&mut self) -> NodeKind {
+ let label = self.s.eat_while(is_id_continue);
+ if self.s.eat_if('>') {
+ if !label.is_empty() {
+ NodeKind::Label(label.into())
+ } else {
+ NodeKind::Error(SpanPos::Full, "label cannot be empty".into())
+ }
+ } else {
+ self.terminated = false;
+ NodeKind::Error(SpanPos::End, "expected closing angle bracket".into())
+ }
+ }
+
+ fn reference(&mut self) -> NodeKind {
+ let label = self.s.eat_while(is_id_continue);
+ if !label.is_empty() {
+ NodeKind::Ref(label.into())
+ } else {
+ NodeKind::Error(SpanPos::Full, "label cannot be empty".into())
+ }
+ }
+
fn ident(&mut self, start: usize) -> NodeKind {
self.s.eat_while(is_id_continue);
match self.s.from(start) {
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 67f9e038..10bee4e8 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -84,6 +84,8 @@ impl Markup {
NodeKind::Heading => node.cast().map(MarkupNode::Heading),
NodeKind::List => node.cast().map(MarkupNode::List),
NodeKind::Enum => node.cast().map(MarkupNode::Enum),
+ NodeKind::Label(v) => Some(MarkupNode::Label(v.clone())),
+ NodeKind::Ref(v) => Some(MarkupNode::Ref(v.clone())),
_ => node.cast().map(MarkupNode::Expr),
})
}
@@ -116,6 +118,10 @@ pub enum MarkupNode {
List(ListNode),
/// An item in an enumeration (ordered list): `1. ...`.
Enum(EnumNode),
+ /// A label.
+ Label(EcoString),
+ /// A reference.
+ Ref(EcoString),
/// An expression.
Expr(Expr),
}
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
index b52234d4..ff02190a 100644
--- a/src/syntax/highlight.rs
+++ b/src/syntax/highlight.rs
@@ -153,6 +153,10 @@ pub enum Category {
Punctuation,
/// A line or block comment.
Comment,
+ /// An easily typable shortcut to a unicode codepoint.
+ Shortcut,
+ /// An escape sequence.
+ Escape,
/// Strong text.
Strong,
/// Emphasized text.
@@ -165,10 +169,10 @@ pub enum Category {
Heading,
/// A list or enumeration.
List,
- /// An easily typable shortcut to a unicode codepoint.
- Shortcut,
- /// An escape sequence.
- Escape,
+ /// A label.
+ Label,
+ /// A reference.
+ Ref,
/// A keyword.
Keyword,
/// An operator symbol.
@@ -212,6 +216,13 @@ impl Category {
NodeKind::Dot => Some(Category::Punctuation),
NodeKind::LineComment => Some(Category::Comment),
NodeKind::BlockComment => Some(Category::Comment),
+ NodeKind::Linebreak { .. } => Some(Category::Shortcut),
+ NodeKind::NonBreakingSpace => Some(Category::Shortcut),
+ NodeKind::Shy => Some(Category::Shortcut),
+ NodeKind::EnDash => Some(Category::Shortcut),
+ NodeKind::EmDash => Some(Category::Shortcut),
+ NodeKind::Ellipsis => Some(Category::Shortcut),
+ NodeKind::Escape(_) => Some(Category::Escape),
NodeKind::Strong => Some(Category::Strong),
NodeKind::Emph => Some(Category::Emph),
NodeKind::Raw(_) => Some(Category::Raw),
@@ -222,13 +233,8 @@ impl Category {
_ => Some(Category::Operator),
},
NodeKind::EnumNumbering(_) => Some(Category::List),
- NodeKind::Linebreak { .. } => Some(Category::Shortcut),
- NodeKind::NonBreakingSpace => Some(Category::Shortcut),
- NodeKind::Shy => Some(Category::Shortcut),
- NodeKind::EnDash => Some(Category::Shortcut),
- NodeKind::EmDash => Some(Category::Shortcut),
- NodeKind::Ellipsis => Some(Category::Shortcut),
- NodeKind::Escape(_) => Some(Category::Escape),
+ NodeKind::Label(_) => Some(Category::Label),
+ NodeKind::Ref(_) => Some(Category::Ref),
NodeKind::Not => Some(Category::Keyword),
NodeKind::And => Some(Category::Keyword),
NodeKind::Or => Some(Category::Keyword),
@@ -344,14 +350,16 @@ impl Category {
Self::Bracket => "punctuation.definition.typst",
Self::Punctuation => "punctuation.typst",
Self::Comment => "comment.typst",
+ Self::Shortcut => "punctuation.shortcut.typst",
+ Self::Escape => "constant.character.escape.content.typst",
Self::Strong => "markup.bold.typst",
Self::Emph => "markup.italic.typst",
Self::Raw => "markup.raw.typst",
Self::Math => "string.other.math.typst",
Self::Heading => "markup.heading.typst",
Self::List => "markup.list.typst",
- Self::Shortcut => "punctuation.shortcut.typst",
- Self::Escape => "constant.character.escape.content.typst",
+ Self::Label => "entity.name.label.typst",
+ Self::Ref => "markup.other.reference.typst",
Self::Keyword => "keyword.typst",
Self::Operator => "keyword.operator.typst",
Self::None => "constant.language.none.typst",
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 4bae7a4b..eb070a04 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -723,6 +723,10 @@ pub enum NodeKind {
///
/// Can also exist without the number: `.`.
EnumNumbering(Option<usize>),
+ /// A label: `<label>`.
+ Label(EcoString),
+ /// A reference: `@label`.
+ Ref(EcoString),
/// An identifier: `center`.
Ident(EcoString),
/// A boolean: `true`, `false`.
@@ -935,6 +939,8 @@ impl NodeKind {
Self::Heading => "heading",
Self::Enum => "enumeration item",
Self::EnumNumbering(_) => "enumeration item numbering",
+ Self::Label(_) => "label",
+ Self::Ref(_) => "reference",
Self::Ident(_) => "identifier",
Self::Bool(_) => "boolean",
Self::Int(_) => "integer",
@@ -1060,6 +1066,8 @@ impl Hash for NodeKind {
Self::Heading => {}
Self::Enum => {}
Self::EnumNumbering(num) => num.hash(state),
+ Self::Label(c) => c.hash(state),
+ Self::Ref(c) => c.hash(state),
Self::Ident(v) => v.hash(state),
Self::Bool(v) => v.hash(state),
Self::Int(v) => v.hash(state),