summaryrefslogtreecommitdiff
path: root/src/syntax/node.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax/node.rs')
-rw-r--r--src/syntax/node.rs138
1 files changed, 90 insertions, 48 deletions
diff --git a/src/syntax/node.rs b/src/syntax/node.rs
index f7625036..7b6aa728 100644
--- a/src/syntax/node.rs
+++ b/src/syntax/node.rs
@@ -26,9 +26,9 @@ pub enum Node {
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::Strong => p.push('*'),
+ Self::Emph => p.push('_'),
+ Self::Space => p.push(' '),
Self::Linebreak => p.push_str(r"\"),
Self::Parbreak => p.push_str("\n\n"),
Self::Text(text) => p.push_str(&text),
@@ -46,10 +46,10 @@ impl Pretty for Node {
}
}
-/// A section heading: `# Introduction`.
+/// A section heading: `= Introduction`.
#[derive(Debug, Clone, PartialEq)]
pub struct NodeHeading {
- /// The section depth (numer of hashtags minus 1, capped at 5).
+ /// The section depth (numer of equals signs minus 1, capped at 5).
pub level: Spanned<u8>,
/// The contents of the heading.
pub contents: Tree,
@@ -58,7 +58,7 @@ pub struct NodeHeading {
impl Pretty for NodeHeading {
fn pretty(&self, p: &mut Printer) {
for _ in 0 ..= self.level.v {
- p.push_str("=");
+ p.push('=');
}
self.contents.pretty(p);
}
@@ -67,8 +67,7 @@ impl Pretty for NodeHeading {
/// A raw block with optional syntax highlighting: `` `raw` ``.
///
/// Raw blocks start with 1 or 3+ 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.
+/// backticks.
///
/// When using at least three backticks, an optional language tag may follow
/// directly after the backticks. This tag defines which language to
@@ -86,7 +85,7 @@ impl Pretty for NodeHeading {
/// ````typst
/// ```rust println!("hello!")```;
/// ````
-/// - Blocks can span multiple lines.
+/// - Blocks can span multiple lines.
/// ````typst
/// ```rust
/// loop {
@@ -94,34 +93,40 @@ impl Pretty for NodeHeading {
/// }
/// ```
/// ````
-/// - 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.
+/// - Start with a space to omit the language tag (the space will be trimmed
+/// from the output).
/// `````typst
-/// ```` This contains ```backticks``` and has no leading & trailing spaces. ````
+/// ```` This has no leading space.````
/// `````
+/// - Use more backticks to allow backticks in the raw text.
+/// `````typst
+/// ```` This contains ```backticks```.````
+/// `````
+///
+/// # Trimming
+/// If we would always render the raw text between the backticks exactly as
+/// given, some things would become cumbersome/impossible to write:
+/// - Typical multiline code blocks (like in the example above) would have an
+/// additional newline before and after the code.
+/// - Multi-line blocks would need to start with a space since a word would be
+/// interpreted as a language tag.
+/// - Text ending with a backtick would be impossible since the backtick would
+/// be interpreted as belonging to the closing backticks.
///
-/// # 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.
-/// - The first word of text wrapped in more than three backticks would always
-/// be interpreted as a language tag which means that text without leading
-/// space would be impossible.
-/// - 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 sometimes trim a bit of space from blocks with 3+
+/// backticks:
+/// - At the start, we trim a single space or a sequence of whitespace followed
+/// by a newline.
+/// - At the end, we trim
+/// - a single space if the raw text ends with a backtick followed only by
+/// whitespace,
+/// - a newline followed by a sequence of whitespace.
///
-/// To fix these problems, we trim blocks with 3+ backticks as follows:
-/// - A single space or a sequence of whitespace followed by a newline at the start.
-/// - A single space or a newline followed by a sequence of whitespace at the end.
+/// You can thus produce a single backtick without surrounding spaces with the
+/// sequence ```` ``` ` ``` ````.
///
-/// 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.
+/// Note that with these rules 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.
@@ -129,28 +134,65 @@ pub struct NodeRaw {
/// 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,
+ /// Whether the element is block-level, that is, it has 3+ backticks
+ /// and contains at least one newline.
+ pub block: bool,
}
impl Pretty for NodeRaw {
fn pretty(&self, p: &mut Printer) {
- p.push_str("`");
+ // Find out how many backticks we need.
+ let mut backticks = 1;
+
+ // Language tag and block-level are only possible with 3+ backticks.
+ if self.lang.is_some() || self.block {
+ backticks = 3;
+ }
+
+ // More backticks may be required if there are lots of consecutive
+ // backticks in the lines.
+ let mut count = 0;
+ for line in &self.lines {
+ for c in line.chars() {
+ if c == '`' {
+ count += 1;
+ backticks = backticks.max(3).max(count + 1);
+ } else {
+ count = 0;
+ }
+ }
+ }
+
+ // Starting backticks.
+ for _ in 0 .. backticks {
+ p.push('`');
+ }
+
+ // Language tag.
if let Some(lang) = &self.lang {
- p.push_str(&lang);
- p.push_str(" ");
+ lang.pretty(p);
+ }
+
+ // Start untrimming.
+ if self.block {
+ p.push('\n');
+ } else if backticks >= 3 {
+ p.push(' ');
}
- // 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.
+
+ // The lines.
p.join(&self.lines, "\n", |line, p| p.push_str(line));
- p.push_str("`");
+
+ // End untrimming.
+ if self.block {
+ p.push('\n');
+ } else if self.lines.last().map_or(false, |line| line.trim_end().ends_with('`')) {
+ p.push(' ');
+ }
+
+ // Ending backticks.
+ for _ in 0 .. backticks {
+ p.push('`');
+ }
}
}