From 67e9313b9127b70b9d7dad6540853025ae90b4a5 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 13 Apr 2022 14:48:19 +0200 Subject: [PATCH] Soft breaks and shy hyphens --- src/eval/content.rs | 14 ++++--- src/eval/mod.rs | 2 +- src/library/text/par.rs | 20 +++++---- src/library/text/raw.rs | 2 +- src/parse/mod.rs | 3 +- src/parse/tokens.rs | 78 +++++++++++++++++++++-------------- src/syntax/ast.rs | 8 ++-- src/syntax/highlight.rs | 3 +- src/syntax/mod.rs | 16 ++++--- tests/ref/text/hyphenate.png | Bin 20776 -> 22150 bytes tests/ref/text/linebreak.png | Bin 14308 -> 18645 bytes tests/typ/text/hyphenate.typ | 10 +++++ tests/typ/text/linebreak.typ | 5 +++ 13 files changed, 103 insertions(+), 58 deletions(-) diff --git a/src/eval/content.rs b/src/eval/content.rs index 274b64b0c..605abe516 100644 --- a/src/eval/content.rs +++ b/src/eval/content.rs @@ -39,8 +39,9 @@ use crate::util::EcoString; pub enum Content { /// A word space. Space, - /// A line break. - Linebreak, + /// A forced line break. If soft (`true`), the preceding line can still be + /// justified, if hard (`false`) not. + Linebreak(bool), /// Horizontal spacing. Horizontal(Spacing), /// Plain text. @@ -213,10 +214,10 @@ impl Debug for Content { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Self::Space => f.pad("Space"), - Self::Linebreak => f.pad("Linebreak"), + Self::Linebreak(soft) => write!(f, "Linebreak({soft})"), Self::Horizontal(kind) => write!(f, "Horizontal({kind:?})"), Self::Text(text) => write!(f, "Text({text:?})"), - Self::Quote(double) => write!(f, "Quote({double:?})"), + Self::Quote(double) => write!(f, "Quote({double})"), Self::Inline(node) => { f.write_str("Inline(")?; node.fmt(f)?; @@ -376,8 +377,9 @@ impl<'a> Builder<'a> { Content::Space => { self.par.weak(ParChild::Text(' '.into()), 0, styles); } - Content::Linebreak => { - self.par.destructive(ParChild::Text('\n'.into()), styles); + Content::Linebreak(soft) => { + let c = if *soft { '\u{2028}' } else { '\n' }; + self.par.destructive(ParChild::Text(c.into()), styles); } Content::Horizontal(kind) => { let child = ParChild::Spacing(*kind); diff --git a/src/eval/mod.rs b/src/eval/mod.rs index f2c03c0f3..4a616b58b 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -108,8 +108,8 @@ impl Eval for MarkupNode { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult { Ok(match self { Self::Space => Content::Space, - Self::Linebreak => Content::Linebreak, Self::Parbreak => Content::Parbreak, + Self::Linebreak(soft) => Content::Linebreak(*soft), Self::Text(text) => Content::Text(text.clone()), Self::Quote(double) => Content::Quote(*double), Self::Strong(strong) => strong.eval(ctx, scp)?, diff --git a/src/library/text/par.rs b/src/library/text/par.rs index 8dcbfeb39..19ab10824 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -167,8 +167,9 @@ pub struct LinebreakNode; #[node] impl LinebreakNode { - fn construct(_: &mut Context, _: &mut Args) -> TypResult { - Ok(Content::Linebreak) + fn construct(_: &mut Context, args: &mut Args) -> TypResult { + let soft = args.named("soft")?.unwrap_or(false); + Ok(Content::Linebreak(soft)) } } @@ -315,8 +316,8 @@ struct Line<'a> { last: Option>, /// The width of the line. width: Length, - /// Whether the line ends at a mandatory break. - mandatory: bool, + /// Whether the line is allowed to be justified. + justify: bool, /// Whether the line ends with a hyphen or dash, either naturally or through /// hyphenation. dash: bool, @@ -856,7 +857,7 @@ fn line<'a>( items: &[], last: None, width: Length::zero(), - mandatory, + justify: !mandatory, dash: false, }; } @@ -879,15 +880,18 @@ fn line<'a>( // Reshape the last item if it's split in half. let mut last = None; let mut dash = false; + let mut justify = !mandatory; if let Some((Item::Text(shaped), before)) = items.split_last() { // Compute the range we want to shape, trimming whitespace at the // end of the line. let base = last_offset; let start = range.start.max(last_offset); let end = range.end; - let trimmed = p.bidi.text[start .. end].trim_end(); + let text = &p.bidi.text[start .. end]; + let trimmed = text.trim_end(); let shy = trimmed.ends_with('\u{ad}'); dash = hyphen || shy || trimmed.ends_with(['-', '–', '—']); + justify |= text.ends_with('\u{2028}'); // Usually, we don't want to shape an empty string because: // - We don't want the height of trimmed whitespace in a different @@ -947,7 +951,7 @@ fn line<'a>( items, last, width, - mandatory, + justify, dash, } } @@ -1050,7 +1054,7 @@ fn commit( let mut justification = Length::zero(); if remaining < Length::zero() || (justify - && !line.mandatory + && line.justify && line.range.end < line.bidi.text.len() && fr.is_zero()) { diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs index 80b6ef2a2..db97da075 100644 --- a/src/library/text/raw.rs +++ b/src/library/text/raw.rs @@ -84,7 +84,7 @@ impl Show for RawNode { let mut highlighter = HighlightLines::new(syntax, &THEME); for (i, line) in self.text.lines().enumerate() { if i != 0 { - seq.push(Content::Linebreak); + seq.push(Content::Linebreak(false)); } for (style, piece) in highlighter.highlight(line, &SYNTAXES) { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 92e864507..47cba1119 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -213,10 +213,11 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { // Text and markup. NodeKind::Text(_) | NodeKind::NonBreakingSpace + | NodeKind::Shy | NodeKind::EnDash | NodeKind::EmDash | NodeKind::Quote(_) - | NodeKind::Linebreak + | NodeKind::Linebreak(_) | NodeKind::Raw(_) | NodeKind::Math(_) | NodeKind::Escape(_) => { diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index a98ef2649..053a7f61e 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -264,42 +264,52 @@ impl<'s> Tokens<'s> { } fn backslash(&mut self) -> NodeKind { - match self.s.peek() { - Some(c) => match c { - // Backslash and comments. - '\\' | '/' | - // Parenthesis and hashtag. - '[' | ']' | '{' | '}' | '#' | - // Markup. - '~' | '\'' | '"' | '*' | '_' | '`' | '$' | '=' | '-' | '.' => { - self.s.eat_assert(c) ; - NodeKind::Escape(c) - } - 'u' if self.s.rest().starts_with("u{") => { - self.s.eat_assert('u'); - self.s.eat_assert('{'); - let sequence = self.s.eat_while(|c| c.is_ascii_alphanumeric()); - if self.s.eat_if('}') { - if let Some(c) = resolve_hex(sequence) { - NodeKind::Escape(c) - } else { - NodeKind::Error( - ErrorPos::Full, - "invalid unicode escape sequence".into(), - ) - } + let c = match self.s.peek() { + Some(c) => c, + None => return NodeKind::Linebreak(false), + }; + + match c { + // Backslash and comments. + '\\' | '/' | + // Parenthesis and hashtag. + '[' | ']' | '{' | '}' | '#' | + // Markup. + '~' | '\'' | '"' | '*' | '_' | '`' | '$' | '=' | '-' | '.' => { + self.s.eat_assert(c) ; + NodeKind::Escape(c) + } + 'u' if self.s.rest().starts_with("u{") => { + self.s.eat_assert('u'); + self.s.eat_assert('{'); + let sequence = self.s.eat_while(|c| c.is_ascii_alphanumeric()); + if self.s.eat_if('}') { + if let Some(c) = resolve_hex(sequence) { + NodeKind::Escape(c) } else { - self.terminated = false; NodeKind::Error( - ErrorPos::End, - "expected closing brace".into(), + ErrorPos::Full, + "invalid unicode escape sequence".into(), ) } + } else { + self.terminated = false; + NodeKind::Error( + ErrorPos::End, + "expected closing brace".into(), + ) } - c if c.is_whitespace() => NodeKind::Linebreak, - _ => NodeKind::Text('\\'.into()), - }, - None => NodeKind::Linebreak, + } + + // Linebreaks. + c if c.is_whitespace() => NodeKind::Linebreak(false), + '+' => { + self.s.eat_assert(c); + NodeKind::Linebreak(true) + } + + // Just the backslash. + _ => NodeKind::Text('\\'.into()), } } @@ -323,6 +333,8 @@ impl<'s> Tokens<'s> { } else { NodeKind::EnDash } + } else if self.s.eat_if('?') { + NodeKind::Shy } else { NodeKind::Minus } @@ -845,8 +857,10 @@ mod tests { t!(Markup: "_" => Underscore); t!(Markup[""]: "===" => Eq, Eq, Eq); t!(Markup["a1/"]: "= " => Eq, Space(0)); + t!(Markup[" "]: r"\" => Linebreak(false)); + t!(Markup[" "]: r"\+" => Linebreak(true)); t!(Markup: "~" => NonBreakingSpace); - t!(Markup[" "]: r"\" => Linebreak); + t!(Markup["a1/"]: "-?" => Shy); t!(Markup["a "]: r"a--" => Text("a"), EnDash); t!(Markup["a1/"]: "- " => Minus, Space(0)); t!(Markup[" "]: "." => EnumNumbering(None)); diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index d629b1fdc..b01eeb47a 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -62,10 +62,11 @@ impl Markup { self.0.children().filter_map(|node| match node.kind() { NodeKind::Space(2 ..) => Some(MarkupNode::Parbreak), NodeKind::Space(_) => Some(MarkupNode::Space), - NodeKind::Linebreak => Some(MarkupNode::Linebreak), + NodeKind::Linebreak(s) => Some(MarkupNode::Linebreak(*s)), NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())), NodeKind::Escape(c) => Some(MarkupNode::Text((*c).into())), NodeKind::NonBreakingSpace => Some(MarkupNode::Text('\u{00A0}'.into())), + NodeKind::Shy => Some(MarkupNode::Text('\u{00AD}'.into())), NodeKind::EnDash => Some(MarkupNode::Text('\u{2013}'.into())), NodeKind::EmDash => Some(MarkupNode::Text('\u{2014}'.into())), NodeKind::Quote(d) => Some(MarkupNode::Quote(*d)), @@ -86,8 +87,9 @@ impl Markup { pub enum MarkupNode { /// Whitespace containing less than two newlines. Space, - /// A forced line break: `\`. - Linebreak, + /// A forced line break. If soft (`\`, `true`), the preceding line can still + /// be justified, if hard (`\+`, `false`) not. + Linebreak(bool), /// A paragraph break: Two or more newlines. Parbreak, /// Plain text. diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs index 90f1c548c..34e5b4a70 100644 --- a/src/syntax/highlight.rs +++ b/src/syntax/highlight.rs @@ -126,8 +126,9 @@ impl Category { _ => Some(Category::Operator), }, NodeKind::EnumNumbering(_) => Some(Category::List), - NodeKind::Linebreak => Some(Category::Shortcut), + NodeKind::Linebreak(_) => Some(Category::Shortcut), NodeKind::NonBreakingSpace => Some(Category::Shortcut), + NodeKind::Shy => Some(Category::Shortcut), NodeKind::EnDash => Some(Category::Shortcut), NodeKind::EmDash => Some(Category::Shortcut), NodeKind::Escape(_) => Some(Category::Escape), diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index f0d3cdd4a..1f02217a1 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -586,12 +586,15 @@ pub enum NodeKind { Markup(usize), /// One or more whitespace characters. Space(usize), - /// A forced line break: `\`. - Linebreak, /// A consecutive non-markup string. Text(EcoString), + /// A forced line break. If soft (`\`, `true`), the preceding line can still + /// be justified, if hard (`\+`, `false`) not. + Linebreak(bool), /// A non-breaking space: `~`. NonBreakingSpace, + /// A soft hyphen: `-?`. + Shy, /// An en-dash: `--`. EnDash, /// An em-dash: `---`. @@ -766,7 +769,7 @@ impl NodeKind { pub fn only_in_mode(&self) -> Option { match self { Self::Markup(_) - | Self::Linebreak + | Self::Linebreak(_) | Self::Text(_) | Self::NonBreakingSpace | Self::EnDash @@ -859,9 +862,11 @@ impl NodeKind { Self::Markup(_) => "markup", Self::Space(2 ..) => "paragraph break", Self::Space(_) => "space", - Self::Linebreak => "forced linebreak", + Self::Linebreak(false) => "hard linebreak", + Self::Linebreak(true) => "soft linebreak", Self::Text(_) => "text", Self::NonBreakingSpace => "non-breaking space", + Self::Shy => "soft hyphen", Self::EnDash => "en dash", Self::EmDash => "em dash", Self::Quote(false) => "single quote", @@ -981,9 +986,10 @@ impl Hash for NodeKind { Self::From => {} Self::Markup(c) => c.hash(state), Self::Space(n) => n.hash(state), - Self::Linebreak => {} + Self::Linebreak(s) => s.hash(state), Self::Text(s) => s.hash(state), Self::NonBreakingSpace => {} + Self::Shy => {} Self::EnDash => {} Self::EmDash => {} Self::Quote(d) => d.hash(state), diff --git a/tests/ref/text/hyphenate.png b/tests/ref/text/hyphenate.png index 48338f586cac5de46094b71999783369b8887318..47a8ffa5428242d98c3046083b3134c387b4d352 100644 GIT binary patch delta 6592 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 diff --git a/tests/ref/text/linebreak.png b/tests/ref/text/linebreak.png index 1498a845331a75059c716ebd8980e0d2a5651b42..43ac9c68a77acf0cec7ee2534059442c000f33b8 100644 GIT binary patch literal 18645 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 IM6N<$f~PU&jsO4v literal 14308 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 e{ts(B>J}79^nDG7AJqWd>4}26dB| diff --git a/tests/typ/text/hyphenate.typ b/tests/typ/text/hyphenate.typ index 02a332770..6bb87b13b 100644 --- a/tests/typ/text/hyphenate.typ +++ b/tests/typ/text/hyphenate.typ @@ -30,6 +30,16 @@ Welcome to wo#text(hyphenate: true)[nd]erful experiences. \ #set text(lang: "en", hyphenate: true) It's a #emph[Tree]beard. +--- +// Test shy hyphens. +#set text(lang: "de", hyphenate: true) +#grid( + columns: 2 * (20pt,), + gutter: 20pt, + [Barankauf], + [Bar-?ankauf], +) + --- // This sequence would confuse hypher if we passed trailing / leading // punctuation instead of just the words. So this tests that we don't diff --git a/tests/typ/text/linebreak.typ b/tests/typ/text/linebreak.typ index ff8559d6a..bee17c6bc 100644 --- a/tests/typ/text/linebreak.typ +++ b/tests/typ/text/linebreak.typ @@ -26,3 +26,8 @@ Two consecutive \ \ breaks and three \ \ \ more. --- // Test forcing an empty trailing line. Trailing break \ \ + +--- +// Test soft breaks. +#set par(justify: true) +With a soft \+ break you can force a break without breaking justification.