summaryrefslogtreecommitdiff
path: root/src/ide/highlight.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/ide/highlight.rs
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'src/ide/highlight.rs')
-rw-r--r--src/ide/highlight.rs430
1 files changed, 0 insertions, 430 deletions
diff --git a/src/ide/highlight.rs b/src/ide/highlight.rs
deleted file mode 100644
index 2db636e3..00000000
--- a/src/ide/highlight.rs
+++ /dev/null
@@ -1,430 +0,0 @@
-use crate::syntax::{ast, LinkedNode, SyntaxKind, SyntaxNode};
-
-/// A syntax highlighting tag.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Tag {
- /// A line or block comment.
- Comment,
- /// Punctuation in code.
- Punctuation,
- /// An escape sequence or shorthand.
- 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 term list.
- ListMarker,
- /// A term in a term list.
- ListTerm,
- /// The delimiters of an equation.
- MathDelimiter,
- /// An operator with special meaning in an equation.
- 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 Tag {
- /// Return the recommended TextMate grammar scope for the given highlighting
- /// tag.
- 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",
- }
- }
-
- /// The recommended CSS class for the highlighting tag.
- pub fn css_class(self) -> &'static str {
- match self {
- Self::Comment => "typ-comment",
- Self::Punctuation => "typ-punct",
- Self::Escape => "typ-escape",
- Self::Strong => "typ-strong",
- Self::Emph => "typ-emph",
- Self::Link => "typ-link",
- Self::Raw => "typ-raw",
- Self::Label => "typ-label",
- Self::Ref => "typ-ref",
- Self::Heading => "typ-heading",
- Self::ListMarker => "typ-marker",
- Self::ListTerm => "typ-term",
- Self::MathDelimiter => "typ-math-delim",
- Self::MathOperator => "typ-math-op",
- Self::Keyword => "typ-key",
- Self::Operator => "typ-op",
- Self::Number => "typ-num",
- Self::String => "typ-str",
- Self::Function => "typ-func",
- Self::Interpolated => "typ-pol",
- Self::Error => "typ-error",
- }
- }
-}
-
-/// Determine the highlight tag of a linked syntax node.
-///
-/// Returns `None` if the node should not be highlighted.
-pub fn highlight(node: &LinkedNode) -> Option<Tag> {
- match node.kind() {
- SyntaxKind::Markup
- if node.parent_kind() == Some(SyntaxKind::TermItem)
- && node.next_sibling_kind() == Some(SyntaxKind::Colon) =>
- {
- Some(Tag::ListTerm)
- }
- SyntaxKind::Markup => None,
- SyntaxKind::Text => None,
- SyntaxKind::Space => None,
- SyntaxKind::Linebreak => Some(Tag::Escape),
- SyntaxKind::Parbreak => None,
- SyntaxKind::Escape => Some(Tag::Escape),
- SyntaxKind::Shorthand => Some(Tag::Escape),
- SyntaxKind::SmartQuote => None,
- SyntaxKind::Strong => Some(Tag::Strong),
- SyntaxKind::Emph => Some(Tag::Emph),
- SyntaxKind::Raw => Some(Tag::Raw),
- SyntaxKind::Link => Some(Tag::Link),
- SyntaxKind::Label => Some(Tag::Label),
- SyntaxKind::Ref => Some(Tag::Ref),
- SyntaxKind::RefMarker => None,
- SyntaxKind::Heading => Some(Tag::Heading),
- SyntaxKind::HeadingMarker => None,
- SyntaxKind::ListItem => None,
- SyntaxKind::ListMarker => Some(Tag::ListMarker),
- SyntaxKind::EnumItem => None,
- SyntaxKind::EnumMarker => Some(Tag::ListMarker),
- SyntaxKind::TermItem => None,
- SyntaxKind::TermMarker => Some(Tag::ListMarker),
- SyntaxKind::Equation => None,
-
- SyntaxKind::Math => None,
- SyntaxKind::MathIdent => highlight_ident(node),
- SyntaxKind::MathAlignPoint => Some(Tag::MathOperator),
- SyntaxKind::MathDelimited => None,
- SyntaxKind::MathAttach => None,
- SyntaxKind::MathFrac => None,
- SyntaxKind::MathRoot => None,
-
- SyntaxKind::Hashtag => highlight_hashtag(node),
- SyntaxKind::LeftBrace => Some(Tag::Punctuation),
- SyntaxKind::RightBrace => Some(Tag::Punctuation),
- SyntaxKind::LeftBracket => Some(Tag::Punctuation),
- SyntaxKind::RightBracket => Some(Tag::Punctuation),
- SyntaxKind::LeftParen => Some(Tag::Punctuation),
- SyntaxKind::RightParen => Some(Tag::Punctuation),
- SyntaxKind::Comma => Some(Tag::Punctuation),
- SyntaxKind::Semicolon => Some(Tag::Punctuation),
- SyntaxKind::Colon => Some(Tag::Punctuation),
- SyntaxKind::Star => match node.parent_kind() {
- Some(SyntaxKind::Strong) => None,
- _ => Some(Tag::Operator),
- },
- SyntaxKind::Underscore => match node.parent_kind() {
- Some(SyntaxKind::MathAttach) => Some(Tag::MathOperator),
- _ => None,
- },
- SyntaxKind::Dollar => Some(Tag::MathDelimiter),
- SyntaxKind::Plus => Some(Tag::Operator),
- SyntaxKind::Minus => Some(Tag::Operator),
- SyntaxKind::Slash => Some(match node.parent_kind() {
- Some(SyntaxKind::MathFrac) => Tag::MathOperator,
- _ => Tag::Operator,
- }),
- SyntaxKind::Hat => Some(Tag::MathOperator),
- SyntaxKind::Dot => Some(Tag::Punctuation),
- SyntaxKind::Eq => match node.parent_kind() {
- Some(SyntaxKind::Heading) => None,
- _ => Some(Tag::Operator),
- },
- SyntaxKind::EqEq => Some(Tag::Operator),
- SyntaxKind::ExclEq => Some(Tag::Operator),
- SyntaxKind::Lt => Some(Tag::Operator),
- SyntaxKind::LtEq => Some(Tag::Operator),
- SyntaxKind::Gt => Some(Tag::Operator),
- SyntaxKind::GtEq => Some(Tag::Operator),
- SyntaxKind::PlusEq => Some(Tag::Operator),
- SyntaxKind::HyphEq => Some(Tag::Operator),
- SyntaxKind::StarEq => Some(Tag::Operator),
- SyntaxKind::SlashEq => Some(Tag::Operator),
- SyntaxKind::Dots => Some(Tag::Operator),
- SyntaxKind::Arrow => Some(Tag::Operator),
- SyntaxKind::Root => Some(Tag::MathOperator),
-
- SyntaxKind::Not => Some(Tag::Keyword),
- SyntaxKind::And => Some(Tag::Keyword),
- SyntaxKind::Or => Some(Tag::Keyword),
- SyntaxKind::None => Some(Tag::Keyword),
- SyntaxKind::Auto => Some(Tag::Keyword),
- SyntaxKind::Let => Some(Tag::Keyword),
- SyntaxKind::Set => Some(Tag::Keyword),
- SyntaxKind::Show => Some(Tag::Keyword),
- SyntaxKind::If => Some(Tag::Keyword),
- SyntaxKind::Else => Some(Tag::Keyword),
- SyntaxKind::For => Some(Tag::Keyword),
- SyntaxKind::In => Some(Tag::Keyword),
- SyntaxKind::While => Some(Tag::Keyword),
- SyntaxKind::Break => Some(Tag::Keyword),
- SyntaxKind::Continue => Some(Tag::Keyword),
- SyntaxKind::Return => Some(Tag::Keyword),
- SyntaxKind::Import => Some(Tag::Keyword),
- SyntaxKind::Include => Some(Tag::Keyword),
- SyntaxKind::As => Some(Tag::Keyword),
-
- SyntaxKind::Code => None,
- SyntaxKind::Ident => highlight_ident(node),
- SyntaxKind::Bool => Some(Tag::Keyword),
- SyntaxKind::Int => Some(Tag::Number),
- SyntaxKind::Float => Some(Tag::Number),
- SyntaxKind::Numeric => Some(Tag::Number),
- SyntaxKind::Str => Some(Tag::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::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::ModuleImport => None,
- SyntaxKind::ImportItems => None,
- SyntaxKind::ModuleInclude => None,
- SyntaxKind::LoopBreak => None,
- SyntaxKind::LoopContinue => None,
- SyntaxKind::FuncReturn => None,
- SyntaxKind::Destructuring => None,
- SyntaxKind::DestructAssignment => None,
-
- SyntaxKind::LineComment => Some(Tag::Comment),
- SyntaxKind::BlockComment => Some(Tag::Comment),
- SyntaxKind::Error => Some(Tag::Error),
- SyntaxKind::Eof => None,
- }
-}
-
-/// Highlight an identifier based on context.
-fn highlight_ident(node: &LinkedNode) -> Option<Tag> {
- // Are we directly before an argument list?
- let next_leaf = node.next_leaf();
- if let Some(next) = &next_leaf {
- if node.range().end == next.offset()
- && ((next.kind() == SyntaxKind::LeftParen
- && matches!(
- next.parent_kind(),
- Some(SyntaxKind::Args | SyntaxKind::Params)
- ))
- || (next.kind() == SyntaxKind::LeftBracket
- && next.parent_kind() == Some(SyntaxKind::ContentBlock)))
- {
- return Some(Tag::Function);
- }
- }
-
- // Are we in math?
- if node.kind() == SyntaxKind::MathIdent {
- return Some(Tag::Interpolated);
- }
-
- // Find the first non-field access ancestor.
- let mut ancestor = node;
- while ancestor.parent_kind() == Some(SyntaxKind::FieldAccess) {
- ancestor = ancestor.parent()?;
- }
-
- // Are we directly before or behind a show rule colon?
- if ancestor.parent_kind() == Some(SyntaxKind::ShowRule)
- && (next_leaf.map(|leaf| leaf.kind()) == Some(SyntaxKind::Colon)
- || node.prev_leaf().map(|leaf| leaf.kind()) == Some(SyntaxKind::Colon))
- {
- return Some(Tag::Function);
- }
-
- // Are we (or an ancestor field access) directly after a hashtag.
- if ancestor.prev_leaf().map(|leaf| leaf.kind()) == Some(SyntaxKind::Hashtag) {
- return Some(Tag::Interpolated);
- }
-
- // Are we behind a dot, that is behind another identifier?
- let prev = node.prev_leaf()?;
- if prev.kind() == SyntaxKind::Dot {
- let prev_prev = prev.prev_leaf()?;
- if is_ident(&prev_prev) {
- return highlight_ident(&prev_prev);
- }
- }
-
- None
-}
-
-/// Highlight a hashtag based on context.
-fn highlight_hashtag(node: &LinkedNode) -> Option<Tag> {
- let next = node.next_sibling()?;
- let expr = next.cast::<ast::Expr>()?;
- if !expr.hashtag() {
- return None;
- }
- highlight(&next.leftmost_leaf()?)
-}
-
-/// Whether the node is one of the two identifier nodes.
-fn is_ident(node: &LinkedNode) -> bool {
- matches!(node.kind(), SyntaxKind::Ident | SyntaxKind::MathIdent)
-}
-
-/// Highlight a node to an HTML `code` element.
-///
-/// This uses these [CSS classes for categories](Tag::css_class).
-pub fn highlight_html(root: &SyntaxNode) -> String {
- let mut buf = String::from("<code>");
- let node = LinkedNode::new(root);
- highlight_html_impl(&mut buf, &node);
- buf.push_str("</code>");
- buf
-}
-
-/// Highlight one source node, emitting HTML.
-fn highlight_html_impl(html: &mut String, node: &LinkedNode) {
- let mut span = false;
- if let Some(tag) = highlight(node) {
- if tag != Tag::Error {
- span = true;
- html.push_str("<span class=\"");
- html.push_str(tag.css_class());
- html.push_str("\">");
- }
- }
-
- let text = node.text();
- if !text.is_empty() {
- for c in text.chars() {
- match c {
- '<' => html.push_str("&lt;"),
- '>' => html.push_str("&gt;"),
- '&' => html.push_str("&amp;"),
- '\'' => html.push_str("&#39;"),
- '"' => html.push_str("&quot;"),
- _ => html.push(c),
- }
- }
- } else {
- for child in node.children() {
- highlight_html_impl(html, &child);
- }
- }
-
- if span {
- html.push_str("</span>");
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::ops::Range;
-
- use super::*;
- use crate::syntax::Source;
-
- #[test]
- fn test_highlighting() {
- use Tag::*;
-
- #[track_caller]
- fn test(text: &str, goal: &[(Range<usize>, Tag)]) {
- 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>, Tag)>, 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..1, Function),
- (1..2, Function),
- (2..3, Punctuation),
- (5..6, Operator),
- (7..8, Number),
- (8..9, Punctuation),
- ],
- );
-
- test(
- "#let f(x) = x",
- &[
- (0..1, Keyword),
- (1..4, Keyword),
- (5..6, Function),
- (6..7, Punctuation),
- (8..9, Punctuation),
- (10..11, Operator),
- ],
- );
- }
-}