diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-01-29 23:23:03 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-01-29 23:23:03 +0100 |
| commit | c987f07b76b18d8762a0ef48740ecc71722540f0 (patch) | |
| tree | e3c46b60db9d967c87dabddbb74ac3ca03623ba0 /src/ide | |
| parent | 196d9594fbb88985dbf61c146a82b8299bb5fd2e (diff) | |
HTML highlighting
Diffstat (limited to 'src/ide')
| -rw-r--r-- | src/ide/highlight.rs | 80 | ||||
| -rw-r--r-- | src/ide/mod.rs | 10 |
2 files changed, 81 insertions, 9 deletions
diff --git a/src/ide/highlight.rs b/src/ide/highlight.rs index ede13d7f..2e418e22 100644 --- a/src/ide/highlight.rs +++ b/src/ide/highlight.rs @@ -1,4 +1,4 @@ -use crate::syntax::{ast, LinkedNode, SyntaxKind}; +use crate::syntax::{ast, LinkedNode, SyntaxKind, SyntaxNode}; /// Syntax highlighting categories. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -75,12 +75,38 @@ impl Category { Self::Error => "invalid.typst", } } + + /// The recommended CSS class for the highlighting category. + 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", + } + } } -/// Highlight a linked syntax node. +/// Determine the highlight category of a linked syntax node. /// -/// Produces a highlighting category or `None` if the node should not be -/// highlighted. +/// Returns `None` if the node should not be highlighted. pub fn highlight(node: &LinkedNode) -> Option<Category> { match node.kind() { SyntaxKind::Markup @@ -285,6 +311,52 @@ 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](Category::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(category) = highlight(node) { + if category != Category::Error { + span = true; + html.push_str("<span class=\""); + html.push_str(category.css_class()); + html.push_str("\">"); + } + } + + let text = node.text(); + if !text.is_empty() { + for c in text.chars() { + match c { + '<' => html.push_str("<"), + '>' => html.push_str(">"), + '&' => html.push_str("&"), + '\'' => html.push_str("'"), + '"' => html.push_str("""), + _ => 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; diff --git a/src/ide/mod.rs b/src/ide/mod.rs index ac69b38a..4999da52 100644 --- a/src/ide/mod.rs +++ b/src/ide/mod.rs @@ -5,13 +5,13 @@ mod complete; mod highlight; mod tooltip; -pub use analyze::*; -pub use complete::*; -pub use highlight::*; -pub use tooltip::*; +pub use self::complete::*; +pub use self::highlight::*; +pub use self::tooltip::*; use std::fmt::Write; +use self::analyze::*; use crate::font::{FontInfo, FontStyle}; /// Extract the first sentence of plain text of a piece of documentation. @@ -60,7 +60,7 @@ fn plain_docs_sentence(docs: &str) -> String { } /// Create a short description of a font family. -pub fn summarize_font_family<'a>(variants: impl Iterator<Item = &'a FontInfo>) -> String { +fn summarize_font_family<'a>(variants: impl Iterator<Item = &'a FontInfo>) -> String { let mut infos: Vec<_> = variants.collect(); infos.sort_by_key(|info| info.variant); |
