typst/src/syntax/node.rs
Laurenz 89eb8bae49 New syntax 💎
- Everything everywhere!
  - Blocks with curly braces: {}
  - Templates with brackets: []
  - Function templates with hashtag: `#[f]`
- Headings with equals sign: `= Introduction`
2021-01-30 12:09:26 +01:00

160 lines
5.7 KiB
Rust

use super::*;
/// A syntax node, encompassing a single logical entity of parsed source code.
#[derive(Debug, Clone, PartialEq)]
pub enum Node {
/// Strong text was enabled / disabled.
Strong,
/// Emphasized text was enabled / disabled.
Emph,
/// Whitespace containing less than two newlines.
Space,
/// A forced line break.
Linebreak,
/// A paragraph break.
Parbreak,
/// Plain text.
Text(String),
/// A section heading.
Heading(NodeHeading),
/// An optionally syntax-highlighted raw block.
Raw(NodeRaw),
/// An expression.
Expr(Expr),
}
impl Pretty for Node {
fn pretty(&self, p: &mut Printer) {
match self {
Self::Strong => p.push_str("*"),
Self::Emph => p.push_str("_"),
Self::Space => p.push_str(" "),
Self::Linebreak => p.push_str(r"\"),
Self::Parbreak => p.push_str("\n\n"),
Self::Text(text) => p.push_str(&text),
Self::Heading(heading) => heading.pretty(p),
Self::Raw(raw) => raw.pretty(p),
Self::Expr(expr) => {
if let Expr::Call(call) = expr {
// Format function templates appropriately.
pretty_func_template(call, p, false)
} else {
expr.pretty(p);
}
}
}
}
}
/// A section heading: `# Introduction`.
#[derive(Debug, Clone, PartialEq)]
pub struct NodeHeading {
/// The section depth (numer of hashtags minus 1, capped at 5).
pub level: Spanned<u8>,
/// The contents of the heading.
pub contents: Tree,
}
impl Pretty for NodeHeading {
fn pretty(&self, p: &mut Printer) {
for _ in 0 ..= self.level.v {
p.push_str("=");
}
self.contents.pretty(p);
}
}
/// A raw block with optional syntax highlighting: `` `raw` ``.
///
/// Raw blocks start with an arbitrary number of backticks and end with the same
/// number of backticks. If you want to include a sequence of backticks in a raw
/// block, simply surround the block with more backticks.
///
/// When using at least two backticks, an optional language tag may follow
/// directly after the backticks. This tag defines which language to
/// syntax-highlight the text in. Apart from the language tag and some
/// whitespace trimming discussed below, everything inside a raw block is
/// rendered verbatim, in particular, there are no escape sequences.
///
/// # Examples
/// - Raw text is surrounded by backticks.
/// ```typst
/// `raw`
/// ```
/// - An optional language tag may follow directly at the start when the block
/// is surrounded by at least two backticks.
/// ```typst
/// ``rust println!("hello!")``;
/// ```
/// - Blocks can span multiple lines. Two backticks suffice to be able to
/// specify the language tag, but three are fine, too.
/// ```typst
/// ``rust
/// loop {
/// find_yak().shave();
/// }
/// ``
/// ```
/// - Start with a space to omit the language tag (the space will be trimmed
/// from the output) and use more backticks to allow backticks in the raw
/// text.
/// `````typst
/// ```` This contains ```backticks``` and has no leading & trailing spaces. ````
/// `````
///
/// # Trimming
/// If we would always render the raw text between the backticks exactly as
/// given, a few things would become problematic or even impossible:
/// - Typical multiline code blocks (like in the example above) would have an
/// additional newline before and after the code.
/// - Raw text wrapped in more than one backtick could not exist without
/// leading whitespace since the first word would be interpreted as a
/// language tag.
/// - A single backtick without surrounding spaces could not exist as raw text
/// since it would be interpreted as belonging to the opening or closing
/// backticks.
///
/// To fix these problems, we trim text in multi-backtick blocks as follows:
/// - We trim a single space or a sequence of whitespace followed by a newline
/// at the start.
/// - We trim a single space or a newline followed by a sequence of whitespace
/// at the end.
///
/// With these rules, a single raw backtick can be produced by the sequence
/// ``` `` ` `` ```, ``` `` unhighlighted text `` ``` has no surrounding
/// spaces and multiline code blocks don't have extra empty lines. Note that
/// you can always force leading or trailing whitespace simply by adding more
/// spaces.
#[derive(Debug, Clone, PartialEq)]
pub struct NodeRaw {
/// An optional identifier specifying the language to syntax-highlight in.
pub lang: Option<Ident>,
/// The lines of raw text, determined as the raw string between the
/// backticks trimmed according to the above rules and split at newlines.
pub lines: Vec<String>,
/// Whether the element can be layouted inline.
///
/// - When true, it will be layouted integrated within the surrounding
/// paragraph.
/// - When false, it will be separated into its own paragraph.
///
/// Single-backtick blocks are always inline-level. Multi-backtick blocks
/// are inline-level when they contain no newlines.
pub inline: bool,
}
impl Pretty for NodeRaw {
fn pretty(&self, p: &mut Printer) {
p.push_str("`");
if let Some(lang) = &self.lang {
p.push_str(&lang);
p.push_str(" ");
}
// TODO: Technically, we should handle backticks in the lines by
// wrapping with more backticks, and we should add space before the
// first and/or after the last line if necessary.
p.join(&self.lines, "\n", |line, p| p.push_str(line));
p.push_str("`");
}
}