summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-12-14 10:00:32 +0100
committerLaurenz <laurmaedje@gmail.com>2022-12-14 10:02:19 +0100
commit4c73456fc1f5df8ebb3a89d9db657c3c54624d66 (patch)
tree72a8fa83bb0a797c3e065746a50a74508bb41a3a /src
parent2470df05af993e89405c5c86329e08243960641d (diff)
Move highlighting into new IDE module
Diffstat (limited to 'src')
-rw-r--r--src/geom/paint.rs14
-rw-r--r--src/ide/highlight.rs308
-rw-r--r--src/ide/mod.rs5
-rw-r--r--src/lib.rs1
-rw-r--r--src/syntax/highlight.rs445
-rw-r--r--src/syntax/mod.rs1
-rw-r--r--src/syntax/tokens.rs4
7 files changed, 317 insertions, 461 deletions
diff --git a/src/geom/paint.rs b/src/geom/paint.rs
index 58b034ae..b4064438 100644
--- a/src/geom/paint.rs
+++ b/src/geom/paint.rs
@@ -1,7 +1,5 @@
use std::str::FromStr;
-use syntect::highlighting::Color as SynColor;
-
use super::*;
/// How a fill or stroke should be painted.
@@ -261,18 +259,6 @@ impl FromStr for RgbaColor {
}
}
-impl From<SynColor> for RgbaColor {
- fn from(SynColor { r, g, b, a }: SynColor) -> Self {
- Self { r, g, b, a }
- }
-}
-
-impl From<RgbaColor> for SynColor {
- fn from(RgbaColor { r, g, b, a }: RgbaColor) -> Self {
- Self { r, g, b, a }
- }
-}
-
impl Debug for RgbaColor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if f.alternate() {
diff --git a/src/ide/highlight.rs b/src/ide/highlight.rs
new file mode 100644
index 00000000..75539a3b
--- /dev/null
+++ b/src/ide/highlight.rs
@@ -0,0 +1,308 @@
+//! Syntax highlighting for Typst source code.
+
+use crate::syntax::{LinkedNode, SyntaxKind};
+
+/// Syntax highlighting categories.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum Category {
+ /// A line or block comment.
+ Comment,
+ /// Punctuation in code.
+ Punctuation,
+ /// An escape sequence, shorthand or symbol notation.
+ Escape,
+ /// Strong markup.
+ Strong,
+ /// Emphasized markup.
+ Emph,
+ /// A hyperlink.
+ Link,
+ /// Raw text.
+ Raw,
+ /// A label.
+ Label,
+ /// A reference to a label.
+ Ref,
+ /// A section heading.
+ Heading,
+ /// A marker of a list, enumeration, or description list.
+ ListMarker,
+ /// A term in a description list.
+ ListTerm,
+ /// The delimiters of a math formula.
+ MathDelimiter,
+ /// An operator with special meaning in a math formula.
+ MathOperator,
+ /// A keyword.
+ Keyword,
+ /// An operator in code.
+ Operator,
+ /// A numeric literal.
+ Number,
+ /// A string literal.
+ String,
+ /// A function or method name.
+ Function,
+ /// An interpolated variable in markup or math.
+ Interpolated,
+ /// A syntax error.
+ Error,
+}
+
+impl Category {
+ /// Return the recommended TextMate grammar scope for the given highlighting
+ /// category.
+ pub fn tm_scope(&self) -> &'static str {
+ match self {
+ Self::Comment => "comment.typst",
+ Self::Punctuation => "punctuation.typst",
+ Self::Escape => "constant.character.escape.typst",
+ Self::Strong => "markup.bold.typst",
+ Self::Emph => "markup.italic.typst",
+ Self::Link => "markup.underline.link.typst",
+ Self::Raw => "markup.raw.typst",
+ Self::MathDelimiter => "punctuation.definition.math.typst",
+ Self::MathOperator => "keyword.operator.math.typst",
+ Self::Heading => "markup.heading.typst",
+ Self::ListMarker => "punctuation.definition.list.typst",
+ Self::ListTerm => "markup.list.term.typst",
+ Self::Label => "entity.name.label.typst",
+ Self::Ref => "markup.other.reference.typst",
+ Self::Keyword => "keyword.typst",
+ Self::Operator => "keyword.operator.typst",
+ Self::Number => "constant.numeric.typst",
+ Self::String => "string.quoted.double.typst",
+ Self::Function => "entity.name.function.typst",
+ Self::Interpolated => "meta.interpolation.typst",
+ Self::Error => "invalid.typst",
+ }
+ }
+}
+
+/// Highlight a linked syntax node.
+///
+/// Produces a highlighting category or `None` if the node should not be
+/// highlighted.
+pub fn highlight(node: &LinkedNode) -> Option<Category> {
+ match node.kind() {
+ SyntaxKind::LineComment => Some(Category::Comment),
+ SyntaxKind::BlockComment => Some(Category::Comment),
+ SyntaxKind::Space { .. } => None,
+
+ SyntaxKind::LeftBrace => Some(Category::Punctuation),
+ SyntaxKind::RightBrace => Some(Category::Punctuation),
+ SyntaxKind::LeftBracket => Some(Category::Punctuation),
+ SyntaxKind::RightBracket => Some(Category::Punctuation),
+ SyntaxKind::LeftParen => Some(Category::Punctuation),
+ SyntaxKind::RightParen => Some(Category::Punctuation),
+ SyntaxKind::Comma => Some(Category::Punctuation),
+ SyntaxKind::Semicolon => Some(Category::Punctuation),
+ SyntaxKind::Colon => Some(Category::Punctuation),
+ SyntaxKind::Star => match node.parent_kind() {
+ Some(SyntaxKind::Strong) => None,
+ _ => Some(Category::Operator),
+ },
+ SyntaxKind::Underscore => match node.parent_kind() {
+ Some(SyntaxKind::Script) => Some(Category::MathOperator),
+ _ => None,
+ },
+ SyntaxKind::Dollar => Some(Category::MathDelimiter),
+ SyntaxKind::Plus => Some(match node.parent_kind() {
+ Some(SyntaxKind::EnumItem) => Category::ListMarker,
+ _ => Category::Operator,
+ }),
+ SyntaxKind::Minus => Some(match node.parent_kind() {
+ Some(SyntaxKind::ListItem) => Category::ListMarker,
+ _ => Category::Operator,
+ }),
+ SyntaxKind::Slash => Some(match node.parent_kind() {
+ Some(SyntaxKind::DescItem) => Category::ListMarker,
+ Some(SyntaxKind::Frac) => Category::MathOperator,
+ _ => Category::Operator,
+ }),
+ SyntaxKind::Hat => Some(Category::MathOperator),
+ SyntaxKind::Amp => Some(Category::MathOperator),
+ SyntaxKind::Dot => Some(Category::Punctuation),
+ SyntaxKind::Eq => match node.parent_kind() {
+ Some(SyntaxKind::Heading) => None,
+ _ => Some(Category::Operator),
+ },
+ SyntaxKind::EqEq => Some(Category::Operator),
+ SyntaxKind::ExclEq => Some(Category::Operator),
+ SyntaxKind::Lt => Some(Category::Operator),
+ SyntaxKind::LtEq => Some(Category::Operator),
+ SyntaxKind::Gt => Some(Category::Operator),
+ SyntaxKind::GtEq => Some(Category::Operator),
+ SyntaxKind::PlusEq => Some(Category::Operator),
+ SyntaxKind::HyphEq => Some(Category::Operator),
+ SyntaxKind::StarEq => Some(Category::Operator),
+ SyntaxKind::SlashEq => Some(Category::Operator),
+ SyntaxKind::Dots => Some(Category::Operator),
+ SyntaxKind::Arrow => Some(Category::Operator),
+
+ SyntaxKind::Not => Some(Category::Keyword),
+ SyntaxKind::And => Some(Category::Keyword),
+ SyntaxKind::Or => Some(Category::Keyword),
+ SyntaxKind::None => Some(Category::Keyword),
+ SyntaxKind::Auto => Some(Category::Keyword),
+ SyntaxKind::Let => Some(Category::Keyword),
+ SyntaxKind::Set => Some(Category::Keyword),
+ SyntaxKind::Show => Some(Category::Keyword),
+ SyntaxKind::If => Some(Category::Keyword),
+ SyntaxKind::Else => Some(Category::Keyword),
+ SyntaxKind::For => Some(Category::Keyword),
+ SyntaxKind::In => Some(Category::Keyword),
+ SyntaxKind::While => Some(Category::Keyword),
+ SyntaxKind::Break => Some(Category::Keyword),
+ SyntaxKind::Continue => Some(Category::Keyword),
+ SyntaxKind::Return => Some(Category::Keyword),
+ SyntaxKind::Import => Some(Category::Keyword),
+ SyntaxKind::Include => Some(Category::Keyword),
+ SyntaxKind::From => Some(Category::Keyword),
+
+ SyntaxKind::Markup { .. }
+ if node.parent_kind() == Some(&SyntaxKind::DescItem)
+ && node.next_sibling_kind() == Some(&SyntaxKind::Colon) =>
+ {
+ Some(Category::ListTerm)
+ }
+ SyntaxKind::Markup { .. } => None,
+
+ SyntaxKind::Text(_) => None,
+ SyntaxKind::Linebreak => Some(Category::Escape),
+ SyntaxKind::Escape(_) => Some(Category::Escape),
+ SyntaxKind::Shorthand(_) => Some(Category::Escape),
+ SyntaxKind::Symbol(_) => Some(Category::Escape),
+ SyntaxKind::SmartQuote { .. } => None,
+ SyntaxKind::Strong => Some(Category::Strong),
+ SyntaxKind::Emph => Some(Category::Emph),
+ SyntaxKind::Raw(_) => Some(Category::Raw),
+ SyntaxKind::Link(_) => Some(Category::Link),
+ SyntaxKind::Label(_) => Some(Category::Label),
+ SyntaxKind::Ref(_) => Some(Category::Ref),
+ SyntaxKind::Heading => Some(Category::Heading),
+ SyntaxKind::ListItem => None,
+ SyntaxKind::EnumItem => None,
+ SyntaxKind::EnumNumbering(_) => Some(Category::ListMarker),
+ SyntaxKind::DescItem => None,
+ SyntaxKind::Math => None,
+ SyntaxKind::Atom(_) => None,
+ SyntaxKind::Script => None,
+ SyntaxKind::Frac => None,
+ SyntaxKind::AlignPoint => None,
+
+ SyntaxKind::Ident(_) => match node.parent_kind() {
+ Some(
+ SyntaxKind::Markup { .. }
+ | SyntaxKind::Math
+ | SyntaxKind::Script
+ | SyntaxKind::Frac,
+ ) => Some(Category::Interpolated),
+ Some(SyntaxKind::FuncCall) => Some(Category::Function),
+ Some(SyntaxKind::MethodCall) if node.prev_sibling().is_some() => {
+ Some(Category::Function)
+ }
+ Some(SyntaxKind::Closure) if node.prev_sibling().is_none() => {
+ Some(Category::Function)
+ }
+ Some(SyntaxKind::SetRule) => Some(Category::Function),
+ Some(SyntaxKind::ShowRule)
+ if node.prev_sibling_kind() == Some(&SyntaxKind::Show) =>
+ {
+ Some(Category::Function)
+ }
+ _ => None,
+ },
+ SyntaxKind::Bool(_) => Some(Category::Keyword),
+ SyntaxKind::Int(_) => Some(Category::Number),
+ SyntaxKind::Float(_) => Some(Category::Number),
+ SyntaxKind::Numeric(_, _) => Some(Category::Number),
+ SyntaxKind::Str(_) => Some(Category::String),
+ SyntaxKind::CodeBlock => None,
+ SyntaxKind::ContentBlock => None,
+ SyntaxKind::Parenthesized => None,
+ SyntaxKind::Array => None,
+ SyntaxKind::Dict => None,
+ SyntaxKind::Named => None,
+ SyntaxKind::Keyed => None,
+ SyntaxKind::Unary => None,
+ SyntaxKind::Binary => None,
+ SyntaxKind::FieldAccess => None,
+ SyntaxKind::FuncCall => None,
+ SyntaxKind::MethodCall => None,
+ SyntaxKind::Args => None,
+ SyntaxKind::Spread => None,
+ SyntaxKind::Closure => None,
+ SyntaxKind::Params => None,
+ SyntaxKind::LetBinding => None,
+ SyntaxKind::SetRule => None,
+ SyntaxKind::ShowRule => None,
+ SyntaxKind::Conditional => None,
+ SyntaxKind::WhileLoop => None,
+ SyntaxKind::ForLoop => None,
+ SyntaxKind::ForPattern => None,
+ SyntaxKind::ModuleImport => None,
+ SyntaxKind::ImportItems => None,
+ SyntaxKind::ModuleInclude => None,
+ SyntaxKind::LoopBreak => None,
+ SyntaxKind::LoopContinue => None,
+ SyntaxKind::FuncReturn => None,
+
+ SyntaxKind::Error(_, _) => Some(Category::Error),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::ops::Range;
+
+ use super::*;
+ use crate::syntax::Source;
+
+ #[test]
+ fn test_highlighting() {
+ use Category::*;
+
+ #[track_caller]
+ fn test(text: &str, goal: &[(Range<usize>, Category)]) {
+ let mut vec = vec![];
+ let source = Source::detached(text);
+ highlight_tree(&mut vec, &LinkedNode::new(source.root()));
+ assert_eq!(vec, goal);
+ }
+
+ fn highlight_tree(tags: &mut Vec<(Range<usize>, Category)>, node: &LinkedNode) {
+ if let Some(tag) = highlight(node) {
+ tags.push((node.range(), tag));
+ }
+
+ for child in node.children() {
+ highlight_tree(tags, &child);
+ }
+ }
+
+ test("= *AB*", &[(0..6, Heading), (2..6, Strong)]);
+
+ test(
+ "#f(x + 1)",
+ &[
+ (0..2, Function),
+ (2..3, Punctuation),
+ (5..6, Operator),
+ (7..8, Number),
+ (8..9, Punctuation),
+ ],
+ );
+
+ test(
+ "#let f(x) = x",
+ &[
+ (0..4, Keyword),
+ (5..6, Function),
+ (6..7, Punctuation),
+ (8..9, Punctuation),
+ (10..11, Operator),
+ ],
+ );
+ }
+}
diff --git a/src/ide/mod.rs b/src/ide/mod.rs
new file mode 100644
index 00000000..a4b1cba5
--- /dev/null
+++ b/src/ide/mod.rs
@@ -0,0 +1,5 @@
+//! Capabilities for IDE support.
+
+mod highlight;
+
+pub use highlight::*;
diff --git a/src/lib.rs b/src/lib.rs
index 7b903d4e..631f1042 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -42,6 +42,7 @@ pub mod model;
pub mod doc;
pub mod export;
pub mod font;
+pub mod ide;
pub mod image;
pub mod syntax;
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
deleted file mode 100644
index f9f35944..00000000
--- a/src/syntax/highlight.rs
+++ /dev/null
@@ -1,445 +0,0 @@
-//! Syntax highlighting for Typst source code.
-
-use std::fmt::Write;
-use std::ops::Range;
-
-use syntect::highlighting::{Color, FontStyle, Highlighter, Style, Theme};
-use syntect::parsing::Scope;
-
-use super::{parse, SyntaxKind, SyntaxNode};
-
-/// Highlight source text into a standalone HTML document.
-pub fn highlight_html(text: &str, theme: &Theme) -> String {
- let mut buf = String::new();
- buf.push_str("<!DOCTYPE html>\n");
- buf.push_str("<html>\n");
- buf.push_str("<head>\n");
- buf.push_str(" <meta charset=\"utf-8\">\n");
- buf.push_str("</head>\n");
- buf.push_str("<body>\n");
- buf.push_str(&highlight_pre(text, theme));
- buf.push_str("\n</body>\n");
- buf.push_str("</html>\n");
- buf
-}
-
-/// Highlight source text into an HTML pre element.
-pub fn highlight_pre(text: &str, theme: &Theme) -> String {
- let mut buf = String::new();
- buf.push_str("<pre>\n");
-
- let root = parse(text);
- highlight_themed(&root, theme, |range, style| {
- let styled = style != Style::default();
- if styled {
- buf.push_str("<span style=\"");
-
- if style.foreground != Color::BLACK {
- let Color { r, g, b, a } = style.foreground;
- write!(buf, "color: #{r:02x}{g:02x}{b:02x}{a:02x};").unwrap();
- }
-
- if style.font_style.contains(FontStyle::BOLD) {
- buf.push_str("font-weight:bold;");
- }
-
- if style.font_style.contains(FontStyle::ITALIC) {
- buf.push_str("font-style:italic;");
- }
-
- if style.font_style.contains(FontStyle::UNDERLINE) {
- buf.push_str("text-decoration:underline;")
- }
-
- buf.push_str("\">");
- }
-
- buf.push_str(&text[range]);
-
- if styled {
- buf.push_str("</span>");
- }
- });
-
- buf.push_str("\n</pre>");
- buf
-}
-
-/// Highlight a syntax node in a theme by calling `f` with ranges and their
-/// styles.
-pub fn highlight_themed<F>(root: &SyntaxNode, theme: &Theme, mut f: F)
-where
- F: FnMut(Range<usize>, Style),
-{
- fn process<F>(
- mut offset: usize,
- node: &SyntaxNode,
- scopes: Vec<Scope>,
- highlighter: &Highlighter,
- f: &mut F,
- ) where
- F: FnMut(Range<usize>, Style),
- {
- if node.children().len() == 0 {
- let range = offset..offset + node.len();
- let style = highlighter.style_for_stack(&scopes);
- f(range, style);
- return;
- }
-
- for (i, child) in node.children().enumerate() {
- let mut scopes = scopes.clone();
- if let Some(category) = Category::determine(child, node, i) {
- scopes.push(Scope::new(category.tm_scope()).unwrap())
- }
- process(offset, child, scopes, highlighter, f);
- offset += child.len();
- }
- }
-
- let highlighter = Highlighter::new(theme);
- process(0, root, vec![], &highlighter, &mut f);
-}
-
-/// Highlight a syntax node by calling `f` with ranges overlapping `within` and
-/// their categories.
-pub fn highlight_categories<F>(root: &SyntaxNode, within: Range<usize>, mut f: F)
-where
- F: FnMut(Range<usize>, Category),
-{
- fn process<F>(mut offset: usize, node: &SyntaxNode, range: Range<usize>, f: &mut F)
- where
- F: FnMut(Range<usize>, Category),
- {
- for (i, child) in node.children().enumerate() {
- let span = offset..offset + child.len();
- if range.start <= span.end && range.end >= span.start {
- if let Some(category) = Category::determine(child, node, i) {
- f(span, category);
- }
- process(offset, child, range.clone(), f);
- }
- offset += child.len();
- }
- }
-
- process(0, root, within, &mut f)
-}
-
-/// The syntax highlighting category of a node.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Category {
- /// A line or block comment.
- Comment,
- /// A square bracket, parenthesis or brace.
- Bracket,
- /// Punctuation in code.
- Punctuation,
- /// An escape sequence.
- Escape,
- /// An easily typable shortcut to a unicode codepoint.
- Shorthand,
- /// Symbol notation.
- Symbol,
- /// A smart quote.
- SmartQuote,
- /// Strong markup.
- Strong,
- /// Emphasized markup.
- Emph,
- /// A hyperlink.
- Link,
- /// Raw text.
- Raw,
- /// A label.
- Label,
- /// A reference.
- Ref,
- /// A section heading.
- Heading,
- /// A full item of a list, enumeration or description list.
- ListItem,
- /// A marker of a list, enumeration, or description list.
- ListMarker,
- /// A term in a description list.
- ListTerm,
- /// The delimiters of a math formula.
- MathDelimiter,
- /// An operator with special meaning in a math formula.
- MathOperator,
- /// A keyword.
- Keyword,
- /// A literal defined by a keyword like `none`, `auto` or a boolean.
- KeywordLiteral,
- /// An operator symbol.
- Operator,
- /// A numeric literal.
- Number,
- /// A string literal.
- String,
- /// A function or method name.
- Function,
- /// An interpolated variable in markup or math.
- Interpolated,
- /// A syntax error.
- Error,
-}
-
-impl Category {
- /// Determine the highlighting category of a node given its parent and its
- /// index in its siblings.
- pub fn determine(
- child: &SyntaxNode,
- parent: &SyntaxNode,
- i: usize,
- ) -> Option<Category> {
- match child.kind() {
- SyntaxKind::LineComment => Some(Category::Comment),
- SyntaxKind::BlockComment => Some(Category::Comment),
- SyntaxKind::Space { .. } => None,
-
- SyntaxKind::LeftBrace => Some(Category::Bracket),
- SyntaxKind::RightBrace => Some(Category::Bracket),
- SyntaxKind::LeftBracket => Some(Category::Bracket),
- SyntaxKind::RightBracket => Some(Category::Bracket),
- SyntaxKind::LeftParen => Some(Category::Bracket),
- SyntaxKind::RightParen => Some(Category::Bracket),
- SyntaxKind::Comma => Some(Category::Punctuation),
- SyntaxKind::Semicolon => Some(Category::Punctuation),
- SyntaxKind::Colon => Some(Category::Punctuation),
- SyntaxKind::Star => match parent.kind() {
- SyntaxKind::Strong => None,
- _ => Some(Category::Operator),
- },
- SyntaxKind::Underscore => match parent.kind() {
- SyntaxKind::Script => Some(Category::MathOperator),
- _ => None,
- },
- SyntaxKind::Dollar => Some(Category::MathDelimiter),
- SyntaxKind::Plus => Some(match parent.kind() {
- SyntaxKind::EnumItem => Category::ListMarker,
- _ => Category::Operator,
- }),
- SyntaxKind::Minus => Some(match parent.kind() {
- SyntaxKind::ListItem => Category::ListMarker,
- _ => Category::Operator,
- }),
- SyntaxKind::Slash => Some(match parent.kind() {
- SyntaxKind::DescItem => Category::ListMarker,
- SyntaxKind::Frac => Category::MathOperator,
- _ => Category::Operator,
- }),
- SyntaxKind::Hat => Some(Category::MathOperator),
- SyntaxKind::Amp => Some(Category::MathOperator),
- SyntaxKind::Dot => Some(Category::Punctuation),
- SyntaxKind::Eq => match parent.kind() {
- SyntaxKind::Heading => None,
- _ => Some(Category::Operator),
- },
- SyntaxKind::EqEq => Some(Category::Operator),
- SyntaxKind::ExclEq => Some(Category::Operator),
- SyntaxKind::Lt => Some(Category::Operator),
- SyntaxKind::LtEq => Some(Category::Operator),
- SyntaxKind::Gt => Some(Category::Operator),
- SyntaxKind::GtEq => Some(Category::Operator),
- SyntaxKind::PlusEq => Some(Category::Operator),
- SyntaxKind::HyphEq => Some(Category::Operator),
- SyntaxKind::StarEq => Some(Category::Operator),
- SyntaxKind::SlashEq => Some(Category::Operator),
- SyntaxKind::Dots => Some(Category::Operator),
- SyntaxKind::Arrow => Some(Category::Operator),
-
- SyntaxKind::Not => Some(Category::Keyword),
- SyntaxKind::And => Some(Category::Keyword),
- SyntaxKind::Or => Some(Category::Keyword),
- SyntaxKind::None => Some(Category::KeywordLiteral),
- SyntaxKind::Auto => Some(Category::KeywordLiteral),
- SyntaxKind::Let => Some(Category::Keyword),
- SyntaxKind::Set => Some(Category::Keyword),
- SyntaxKind::Show => Some(Category::Keyword),
- SyntaxKind::If => Some(Category::Keyword),
- SyntaxKind::Else => Some(Category::Keyword),
- SyntaxKind::For => Some(Category::Keyword),
- SyntaxKind::In => Some(Category::Keyword),
- SyntaxKind::While => Some(Category::Keyword),
- SyntaxKind::Break => Some(Category::Keyword),
- SyntaxKind::Continue => Some(Category::Keyword),
- SyntaxKind::Return => Some(Category::Keyword),
- SyntaxKind::Import => Some(Category::Keyword),
- SyntaxKind::Include => Some(Category::Keyword),
- SyntaxKind::From => Some(Category::Keyword),
-
- SyntaxKind::Markup { .. } => match parent.kind() {
- SyntaxKind::DescItem
- if parent
- .children()
- .take_while(|child| child.kind() != &SyntaxKind::Colon)
- .find(|c| matches!(c.kind(), SyntaxKind::Markup { .. }))
- .map_or(false, |ident| std::ptr::eq(ident, child)) =>
- {
- Some(Category::ListTerm)
- }
- _ => None,
- },
- SyntaxKind::Text(_) => None,
- SyntaxKind::Linebreak => Some(Category::Escape),
- SyntaxKind::Escape(_) => Some(Category::Escape),
- SyntaxKind::Shorthand(_) => Some(Category::Shorthand),
- SyntaxKind::Symbol(_) => Some(Category::Symbol),
- SyntaxKind::SmartQuote { .. } => Some(Category::SmartQuote),
- SyntaxKind::Strong => Some(Category::Strong),
- SyntaxKind::Emph => Some(Category::Emph),
- SyntaxKind::Raw(_) => Some(Category::Raw),
- SyntaxKind::Link(_) => Some(Category::Link),
- SyntaxKind::Label(_) => Some(Category::Label),
- SyntaxKind::Ref(_) => Some(Category::Ref),
- SyntaxKind::Heading => Some(Category::Heading),
- SyntaxKind::ListItem => Some(Category::ListItem),
- SyntaxKind::EnumItem => Some(Category::ListItem),
- SyntaxKind::EnumNumbering(_) => Some(Category::ListMarker),
- SyntaxKind::DescItem => Some(Category::ListItem),
- SyntaxKind::Math => None,
- SyntaxKind::Atom(_) => None,
- SyntaxKind::Script => None,
- SyntaxKind::Frac => None,
- SyntaxKind::AlignPoint => None,
-
- SyntaxKind::Ident(_) => match parent.kind() {
- SyntaxKind::Markup { .. }
- | SyntaxKind::Math
- | SyntaxKind::Script
- | SyntaxKind::Frac => Some(Category::Interpolated),
- SyntaxKind::FuncCall => Some(Category::Function),
- SyntaxKind::MethodCall if i > 0 => Some(Category::Function),
- SyntaxKind::Closure if i == 0 => Some(Category::Function),
- SyntaxKind::SetRule => Some(Category::Function),
- SyntaxKind::ShowRule
- if parent
- .children()
- .rev()
- .skip_while(|child| child.kind() != &SyntaxKind::Colon)
- .find(|c| matches!(c.kind(), SyntaxKind::Ident(_)))
- .map_or(false, |ident| std::ptr::eq(ident, child)) =>
- {
- Some(Category::Function)
- }
- _ => None,
- },
- SyntaxKind::Bool(_) => Some(Category::KeywordLiteral),
- SyntaxKind::Int(_) => Some(Category::Number),
- SyntaxKind::Float(_) => Some(Category::Number),
- SyntaxKind::Numeric(_, _) => Some(Category::Number),
- SyntaxKind::Str(_) => Some(Category::String),
- SyntaxKind::CodeBlock => None,
- SyntaxKind::ContentBlock => None,
- SyntaxKind::Parenthesized => None,
- SyntaxKind::Array => None,
- SyntaxKind::Dict => None,
- SyntaxKind::Named => None,
- SyntaxKind::Keyed => None,
- SyntaxKind::Unary => None,
- SyntaxKind::Binary => None,
- SyntaxKind::FieldAccess => None,
- SyntaxKind::FuncCall => None,
- SyntaxKind::MethodCall => None,
- SyntaxKind::Args => None,
- SyntaxKind::Spread => None,
- SyntaxKind::Closure => None,
- SyntaxKind::Params => None,
- SyntaxKind::LetBinding => None,
- SyntaxKind::SetRule => None,
- SyntaxKind::ShowRule => None,
- SyntaxKind::Conditional => None,
- SyntaxKind::WhileLoop => None,
- SyntaxKind::ForLoop => None,
- SyntaxKind::ForPattern => None,
- SyntaxKind::ModuleImport => None,
- SyntaxKind::ImportItems => None,
- SyntaxKind::ModuleInclude => None,
- SyntaxKind::LoopBreak => None,
- SyntaxKind::LoopContinue => None,
- SyntaxKind::FuncReturn => None,
-
- SyntaxKind::Error(_, _) => Some(Category::Error),
- }
- }
-
- /// Return the TextMate grammar scope for the given highlighting category.
- pub fn tm_scope(&self) -> &'static str {
- match self {
- Self::Comment => "comment.typst",
- Self::Bracket => "punctuation.definition.bracket.typst",
- Self::Punctuation => "punctuation.typst",
- Self::Escape => "constant.character.escape.typst",
- Self::Shorthand => "constant.character.shorthand.typst",
- Self::Symbol => "constant.symbol.typst",
- Self::SmartQuote => "constant.character.quote.typst",
- Self::Strong => "markup.bold.typst",
- Self::Emph => "markup.italic.typst",
- Self::Link => "markup.underline.link.typst",
- Self::Raw => "markup.raw.typst",
- Self::MathDelimiter => "punctuation.definition.math.typst",
- Self::MathOperator => "keyword.operator.math.typst",
- Self::Heading => "markup.heading.typst",
- Self::ListItem => "markup.list.typst",
- Self::ListMarker => "punctuation.definition.list.typst",
- Self::ListTerm => "markup.list.term.typst",
- Self::Label => "entity.name.label.typst",
- Self::Ref => "markup.other.reference.typst",
- Self::Keyword => "keyword.typst",
- Self::Operator => "keyword.operator.typst",
- Self::KeywordLiteral => "constant.language.typst",
- Self::Number => "constant.numeric.typst",
- Self::String => "string.quoted.double.typst",
- Self::Function => "entity.name.function.typst",
- Self::Interpolated => "meta.interpolation.typst",
- Self::Error => "invalid.typst",
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::super::Source;
- use super::*;
-
- #[test]
- fn test_highlighting() {
- use Category::*;
-
- #[track_caller]
- fn test(text: &str, goal: &[(Range<usize>, Category)]) {
- let mut vec = vec![];
- let source = Source::detached(text);
- let full = 0..text.len();
- highlight_categories(source.root(), full, &mut |range, category| {
- vec.push((range, category));
- });
- assert_eq!(vec, goal);
- }
-
- test("= *AB*", &[(0..6, Heading), (2..6, Strong)]);
-
- test(
- "#f(x + 1)",
- &[
- (0..2, Function),
- (2..3, Bracket),
- (5..6, Operator),
- (7..8, Number),
- (8..9, Bracket),
- ],
- );
-
- test(
- "#let f(x) = x",
- &[
- (0..4, Keyword),
- (5..6, Function),
- (6..7, Bracket),
- (8..9, Bracket),
- (10..11, Operator),
- ],
- );
- }
-}
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 77411cf6..4f159b83 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -1,7 +1,6 @@
//! Syntax definition, parsing, and highlighting.
pub mod ast;
-pub mod highlight;
mod incremental;
mod kind;
diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs
index ff6a1a96..98f244e1 100644
--- a/src/syntax/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -304,8 +304,10 @@ impl Tokens<'_> {
Some(keyword) => keyword,
None => SyntaxKind::Ident(read.into()),
}
- } else {
+ } else if self.mode == TokenMode::Markup {
self.text(start)
+ } else {
+ SyntaxKind::Atom("#".into())
}
}