Introduce NodeKind::Quote

This commit is contained in:
Martin Haug 2022-04-12 13:00:34 +02:00 committed by Laurenz
parent c3a387b8f7
commit 072543fc59
8 changed files with 36 additions and 7 deletions

View File

@ -45,6 +45,8 @@ pub enum Content {
Horizontal(Spacing),
/// Plain text.
Text(EcoString),
/// A smart quote, may be single (`false`) or double (`true`).
Quote(bool),
/// An inline-level node.
Inline(LayoutNode),
/// A paragraph break.
@ -214,6 +216,7 @@ impl Debug for Content {
Self::Linebreak => f.pad("Linebreak"),
Self::Horizontal(kind) => write!(f, "Horizontal({kind:?})"),
Self::Text(text) => write!(f, "Text({text:?})"),
Self::Quote(double) => write!(f, "Quote({double:?})"),
Self::Inline(node) => {
f.write_str("Inline(")?;
node.fmt(f)?;
@ -384,6 +387,9 @@ impl<'a> Builder<'a> {
self.par.ignorant(child, styles);
}
}
Content::Quote(double) => {
self.par.supportive(ParChild::Quote(*double), styles);
}
Content::Text(text) => {
self.par.supportive(ParChild::Text(text.clone()), styles);
}
@ -496,7 +502,7 @@ impl<'a> Builder<'a> {
.items()
.find_map(|child| match child {
ParChild::Spacing(_) => None,
ParChild::Text(_) => Some(true),
ParChild::Text(_) | ParChild::Quote(_) => Some(true),
ParChild::Node(_) => Some(false),
})
.unwrap_or_default()

View File

@ -111,6 +111,7 @@ impl Eval for MarkupNode {
Self::Linebreak => Content::Linebreak,
Self::Parbreak => Content::Parbreak,
Self::Text(text) => Content::Text(text.clone()),
Self::Quote(double) => Content::Quote(*double),
Self::Strong(strong) => strong.eval(ctx, scp)?,
Self::Emph(emph) => emph.eval(ctx, scp)?,
Self::Raw(raw) => raw.eval(ctx, scp)?,

View File

@ -19,6 +19,8 @@ pub struct ParNode(pub StyleVec<ParChild>);
pub enum ParChild {
/// A chunk of text.
Text(EcoString),
/// A smart quote, may be single (`false`) or double (`true`).
Quote(bool),
/// Horizontal spacing between other children.
Spacing(Spacing),
/// An arbitrary inline-level node.
@ -89,6 +91,7 @@ impl Debug for ParChild {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Text(text) => write!(f, "Text({:?})", text),
Self::Quote(double) => write!(f, "Quote({})", double),
Self::Spacing(kind) => write!(f, "{:?}", kind),
Self::Node(node) => node.fmt(f),
}
@ -397,6 +400,11 @@ fn collect<'a>(
}
Segment::Text(full.len() - prev)
}
ParChild::Quote(double) => {
let prev = full.len();
full.push(if *double { '"' } else { '\'' });
Segment::Text(full.len() - prev)
}
ParChild::Spacing(spacing) => {
full.push(SPACING_REPLACE);
Segment::Spacing(*spacing)

View File

@ -215,6 +215,7 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
| NodeKind::NonBreakingSpace
| NodeKind::EnDash
| NodeKind::EmDash
| NodeKind::Quote(_)
| NodeKind::Linebreak
| NodeKind::Raw(_)
| NodeKind::Math(_)

View File

@ -143,11 +143,13 @@ impl<'s> Tokens<'s> {
// Markup.
'~' => NodeKind::NonBreakingSpace,
'-' => self.hyph(),
'\'' => NodeKind::Quote(false),
'"' => NodeKind::Quote(true),
'*' if !self.in_word() => NodeKind::Star,
'_' if !self.in_word() => NodeKind::Underscore,
'`' => self.raw(),
'$' => self.math(),
'-' => self.hyph(),
'=' => NodeKind::Eq,
c if c == '.' || c.is_ascii_digit() => self.numbering(start, c),
@ -220,7 +222,7 @@ impl<'s> Tokens<'s> {
// Comments, parentheses, code.
'/' | '[' | ']' | '{' | '}' | '#' |
// Markup
'~' | '*' | '_' | '`' | '$' | '-' | '\\'
'~' | '\'' | '"' | '*' | '_' | '`' | '$' | '-' | '\\'
};
loop {
@ -269,8 +271,8 @@ impl<'s> Tokens<'s> {
// Parenthesis and hashtag.
'[' | ']' | '{' | '}' | '#' |
// Markup.
'~' | '*' | '_' | '`' | '$' | '=' | '-' | '.' => {
self.s.eat_assert(c);
'~' | '\'' | '"' | '*' | '_' | '`' | '$' | '=' | '-' | '.' => {
self.s.eat_assert(c) ;
NodeKind::Escape(c)
}
'u' if self.s.rest().starts_with("u{") => {
@ -789,7 +791,7 @@ mod tests {
t!(Markup[" /"]: "hello-world" => Text("hello"), Minus, Text("world"));
// Test code symbols in text.
t!(Markup[" /"]: "a():\"b" => Text("a():\"b"));
t!(Markup[" /"]: "a():\"b" => Text("a():"), Quote(true), Text("b"));
t!(Markup[" /"]: ";:,|/+" => Text(";:,|"), Text("/+"));
t!(Markup[" /"]: "=-a" => Eq, Minus, Text("a"));
t!(Markup[" "]: "#123" => Text("#"), Text("123"));
@ -812,6 +814,8 @@ mod tests {
t!(Markup: r"\_" => Escape('_'));
t!(Markup: r"\=" => Escape('='));
t!(Markup: r"\~" => Escape('~'));
t!(Markup: r"\'" => Escape('\''));
t!(Markup: r#"\""# => Escape('"'));
t!(Markup: r"\`" => Escape('`'));
t!(Markup: r"\$" => Escape('$'));
t!(Markup: r"\#" => Escape('#'));
@ -820,7 +824,6 @@ mod tests {
t!(Markup[" /"]: r"\a" => Text(r"\"), Text("a"));
t!(Markup[" /"]: r"\u" => Text(r"\"), Text("u"));
t!(Markup[" /"]: r"\1" => Text(r"\"), Text("1"));
t!(Markup[" /"]: r#"\""# => Text(r"\"), Text("\""));
// Test basic unicode escapes.
t!(Markup: r"\u{}" => Error(Full, "invalid unicode escape sequence"));

View File

@ -68,6 +68,7 @@ impl Markup {
NodeKind::NonBreakingSpace => Some(MarkupNode::Text('\u{00A0}'.into())),
NodeKind::EnDash => Some(MarkupNode::Text('\u{2013}'.into())),
NodeKind::EmDash => Some(MarkupNode::Text('\u{2014}'.into())),
NodeKind::Quote(d) => Some(MarkupNode::Quote(*d)),
NodeKind::Strong => node.cast().map(MarkupNode::Strong),
NodeKind::Emph => node.cast().map(MarkupNode::Emph),
NodeKind::Raw(raw) => Some(MarkupNode::Raw(raw.as_ref().clone())),
@ -91,6 +92,8 @@ pub enum MarkupNode {
Parbreak,
/// Plain text.
Text(EcoString),
/// A smart quote: `'` (`false`) or `"` (true).
Quote(bool),
/// Strong content: `*Strong*`.
Strong(StrongNode),
/// Emphasized content: `_Emphasized_`.

View File

@ -130,6 +130,7 @@ impl Category {
NodeKind::NonBreakingSpace => Some(Category::Shortcut),
NodeKind::EnDash => Some(Category::Shortcut),
NodeKind::EmDash => Some(Category::Shortcut),
NodeKind::Quote(_) => Some(Category::Shortcut),
NodeKind::Escape(_) => Some(Category::Escape),
NodeKind::Not => Some(Category::Keyword),
NodeKind::And => Some(Category::Keyword),

View File

@ -596,6 +596,8 @@ pub enum NodeKind {
EnDash,
/// An em-dash: `---`.
EmDash,
/// A smart quote: `'` (`false`) or `"` (true).
Quote(bool),
/// A slash and the letter "u" followed by a hexadecimal unicode entity
/// enclosed in curly braces: `\u{1F5FA}`.
Escape(char),
@ -769,6 +771,7 @@ impl NodeKind {
| Self::NonBreakingSpace
| Self::EnDash
| Self::EmDash
| Self::Quote(_)
| Self::Escape(_)
| Self::Strong
| Self::Emph
@ -861,6 +864,8 @@ impl NodeKind {
Self::NonBreakingSpace => "non-breaking space",
Self::EnDash => "en dash",
Self::EmDash => "em dash",
Self::Quote(false) => "single quote",
Self::Quote(true) => "double quote",
Self::Escape(_) => "escape sequence",
Self::Strong => "strong content",
Self::Emph => "emphasized content",
@ -981,6 +986,7 @@ impl Hash for NodeKind {
Self::NonBreakingSpace => {}
Self::EnDash => {}
Self::EmDash => {}
Self::Quote(d) => d.hash(state),
Self::Escape(c) => c.hash(state),
Self::Strong => {}
Self::Emph => {}