From 1af194f3833766566de9f7e1ca9e937b77ad47c4 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 29 Oct 2021 16:26:47 +0200 Subject: [PATCH] Dedent code blocks --- src/parse/mod.rs | 3 ++- src/parse/resolve.rs | 45 +++++++++++++++++++++++++-------------- tests/ref/markup/raw.png | Bin 6324 -> 6770 bytes tests/typ/markup/raw.typ | 20 +++++++++++------ 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 8466d99f6..307874232 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -164,8 +164,9 @@ fn unicode_escape(p: &mut Parser, token: UnicodeEscapeToken) -> EcoString { /// Handle a raw block. fn raw(p: &mut Parser, token: RawToken) -> MarkupNode { + let column = p.column(p.next_start()); let span = p.peek_span(); - let raw = resolve::resolve_raw(span, token.text, token.backticks); + let raw = resolve::resolve_raw(span, column, token.backticks, token.text); if !token.terminated { p.error(span.end, "expected backtick(s)"); } diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs index 80e3bbc00..51ddac8f3 100644 --- a/src/parse/resolve.rs +++ b/src/parse/resolve.rs @@ -48,10 +48,10 @@ pub fn resolve_hex(sequence: &str) -> Option { } /// Resolve the language tag and trims the raw text. -pub fn resolve_raw(span: Span, text: &str, backticks: usize) -> RawNode { +pub fn resolve_raw(span: Span, column: usize, backticks: usize, text: &str) -> RawNode { if backticks > 1 { let (tag, inner) = split_at_lang_tag(text); - let (text, block) = trim_and_split_raw(inner); + let (text, block) = trim_and_split_raw(column, inner); RawNode { span, lang: Ident::new(tag, span.with_end(span.start + tag.len())), @@ -80,7 +80,7 @@ fn split_at_lang_tag(raw: &str) -> (&str, &str) { /// Trim raw text and splits it into lines. /// /// Returns whether at least one newline was contained in `raw`. -fn trim_and_split_raw(mut raw: &str) -> (String, bool) { +fn trim_and_split_raw(column: usize, mut raw: &str) -> (String, bool) { // Trims one space at the start. raw = raw.strip_prefix(' ').unwrap_or(raw); @@ -90,8 +90,17 @@ fn trim_and_split_raw(mut raw: &str) -> (String, bool) { } let mut lines = split_lines(raw); - let is_whitespace = |line: &String| line.chars().all(char::is_whitespace); + + // Dedent based on column, but not for the first line. + for line in lines.iter_mut().skip(1) { + let offset = line.chars().take(column).take_while(|c| c.is_whitespace()).count(); + if offset > 0 { + line.drain(.. offset); + } + } + let had_newline = lines.len() > 1; + let is_whitespace = |line: &String| line.chars().all(char::is_whitespace); // Trims a sequence of whitespace followed by a newline at the start. if lines.first().map_or(false, is_whitespace) { @@ -174,39 +183,43 @@ mod tests { fn test_resolve_raw() { #[track_caller] fn test( - raw: &str, + column: usize, backticks: usize, + raw: &str, lang: Option<&str>, text: &str, block: bool, ) { - let node = resolve_raw(Span::detached(), raw, backticks); + let node = resolve_raw(Span::detached(), column, backticks, raw); assert_eq!(node.lang.as_deref(), lang); assert_eq!(node.text, text); assert_eq!(node.block, block); } // Just one backtick. - test("py", 1, None, "py", false); - test("1\n2", 1, None, "1\n2", false); - test("1\r\n2", 1, None, "1\n2", false); + test(0, 1, "py", None, "py", false); + test(0, 1, "1\n2", None, "1\n2", false); + test(0, 1, "1\r\n2", None, "1\n2", false); // More than one backtick with lang tag. - test("js alert()", 2, Some("js"), "alert()", false); - test("py quit(\n\n)", 3, Some("py"), "quit(\n\n)", true); - test("♥", 2, None, "", false); + test(0, 2, "js alert()", Some("js"), "alert()", false); + test(0, 3, "py quit(\n\n)", Some("py"), "quit(\n\n)", true); + test(0, 2, "♥", None, "", false); // Trimming of whitespace (tested more thoroughly in separate test). - test(" a", 2, None, "a", false); - test(" a", 2, None, " a", false); - test(" \na", 2, None, "a", true); + test(0, 2, " a", None, "a", false); + test(0, 2, " a", None, " a", false); + test(0, 2, " \na", None, "a", true); + + // Dedenting + test(2, 3, " def foo():\n bar()", None, "def foo():\n bar()", true); } #[test] fn test_trim_raw() { #[track_caller] fn test(text: &str, expected: &str) { - assert_eq!(trim_and_split_raw(text).0, expected); + assert_eq!(trim_and_split_raw(0, text).0, expected); } test(" hi", "hi"); diff --git a/tests/ref/markup/raw.png b/tests/ref/markup/raw.png index 4da6a43e118b3d5a8fe95413c4ed6498b7d9a73d..bd8b811addc4ceef9bc8e8a7e679415386c45477 100644 GIT binary patch literal 6770 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 CxfYiI literal 6324 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 \ <``` trimmed```> -// Multiline trimming. -```py -import this +// Multiline trimming and dedenting. +#block[ + ```py + import this -def hi(): - print("Hi!") -``` + def hi(): + print("Hi!") + ``` +] + +--- +// First line is not dedented and leading space is still possible. + ``` A + B + C``` --- // Unterminated.