diff --git a/compiler/frontend/src/string_to_rcst/text.rs b/compiler/frontend/src/string_to_rcst/text.rs index 003963867..4bdc1f008 100644 --- a/compiler/frontend/src/string_to_rcst/text.rs +++ b/compiler/frontend/src/string_to_rcst/text.rs @@ -9,6 +9,7 @@ use crate::{ rcst::Rcst, }; use itertools::Itertools; +use std::mem; use tracing::instrument; // TODO: It might be a good idea to ignore text interpolations in patterns @@ -36,10 +37,10 @@ pub fn text(input: &str, indentation: usize) -> Option<(&str, Rcst)> { .map(|(i, _)| i) .nth(1) { - let (first_whitespace, rest) = opening_whitespace.split_at(second_newline_index); + let rest = opening_whitespace.split_off(second_newline_index); ( - first_whitespace.to_vec(), - convert_whitespace_into_text_newlines(rest.to_vec()), + opening_whitespace, + convert_whitespace_into_text_newlines(rest), ) } else { (opening_whitespace, vec![]) @@ -57,7 +58,7 @@ pub fn text(input: &str, indentation: usize) -> Option<(&str, Rcst)> { input = input_after_part; parts.push(part); } else { - let (input_after_whitespace, whitespace) = + let (input_after_whitespace, mut whitespace) = whitespaces_and_newlines(input, indentation + 1, false); input = input_after_whitespace; @@ -68,16 +69,16 @@ pub fn text(input: &str, indentation: usize) -> Option<(&str, Rcst)> { .map(|(i, _)| i) .next_back() { - let (whitespace, rest) = whitespace.split_at(last_newline_index); - let mut newlines = convert_whitespace_into_text_newlines(whitespace.to_vec()); + let rest = whitespace.split_off(last_newline_index); + let mut newlines = convert_whitespace_into_text_newlines(whitespace); parts.append(&mut newlines); - rest.to_vec() + rest } else { whitespace }; // Allow closing quotes to have the same indentation level as the opening quotes - let (input_after_whitespace, whitespace) = if newline(input).is_some() { + let (input_after_whitespace, mut whitespace) = if newline(input).is_some() { whitespaces_and_newlines(input, indentation, false) } else { (input, Vec::new()) @@ -98,10 +99,10 @@ pub fn text(input: &str, indentation: usize) -> Option<(&str, Rcst)> { .map(|(i, _)| i) .next_back() { - let (whitespace, rest) = whitespace.split_at(last_newline_index); - let mut newlines = convert_whitespace_into_text_newlines(whitespace.to_vec()); + let rest = whitespace.split_off(last_newline_index); + let mut newlines = convert_whitespace_into_text_newlines(whitespace); parts.append(&mut newlines); - rest.to_vec() + rest } else { let mut newlines = convert_whitespace_into_text_newlines(whitespace_before_closing_quote); @@ -252,25 +253,38 @@ fn text_part(mut input: &str, single_quotes_count: usize) -> Option<(&str, Rcst) } } +/// Converts a vec of `CstKind::Newline(…)` and `CstKind::Whitespace(…)` into +/// a vec of `CstKind::TrailingWhitespace { child: CstKind::TextNewline(…), … }`. #[instrument(level = "trace")] fn convert_whitespace_into_text_newlines(whitespace: Vec) -> Vec { + fn commit_last_newline( + parts: &mut Vec, + last_newline: Option, + whitespace_after_last_newline: Vec, + ) { + if let Some(last_newline) = last_newline { + parts.push(last_newline.wrap_in_whitespace(whitespace_after_last_newline)); + } + } + + let mut parts: Vec = vec![]; let mut last_newline: Option = None; let mut whitespace_after_last_newline: Vec = vec![]; - let mut parts: Vec = vec![]; - for whitespace in whitespace - .iter() - .chain(std::iter::once(&CstKind::Newline("\n".to_string()).into())) - { - if let CstKind::Newline(newline) = whitespace.kind.clone() { - if let Some(last_newline) = last_newline { - parts.push(last_newline.wrap_in_whitespace(whitespace_after_last_newline)); - whitespace_after_last_newline = vec![]; - } + + for whitespace in whitespace { + if let CstKind::Newline(newline) = whitespace.kind { + commit_last_newline( + &mut parts, + last_newline, + mem::take(&mut whitespace_after_last_newline), + ); last_newline = Some(CstKind::TextNewline(newline).into()); } else { - whitespace_after_last_newline.push(whitespace.clone()); + whitespace_after_last_newline.push(whitespace); } } + commit_last_newline(&mut parts, last_newline, whitespace_after_last_newline); + parts } diff --git a/packages/AnsiEscapeSequences/example.candy b/packages/AnsiEscapeSequences/example.candy index e09967535..35fba4ba9 100644 --- a/packages/AnsiEscapeSequences/example.candy +++ b/packages/AnsiEscapeSequences/example.candy @@ -3,10 +3,6 @@ "AnsiEscapeSequences" [equals, function, int, iterator, text, toDebugText] = use "Core" -## TODO(JonasWanke): Use normal space in text (instead of NBSP) when -## https://github.com/candy-lang/candy/issues/896 is fixed. -nbsp = " " - showGraphicRendition graphicRenditionText content = # n m SGR Sets colors and style of the characters following this code needs (text.is graphicRenditionText) @@ -35,7 +31,7 @@ showStyles stdout = iterator.range 9 | iterator.map { index -> index | int.add 1 } | iterator.map { number -> showGraphicRendition (graphicRenditions.alternativeFont number) "Alternative Font {number}" } - | iterator.joinToTextWithSeparator nbsp + | iterator.joinToTextWithSeparator " " stdout (showGraphicRendition graphicRenditions.fraktur "Fraktur") stdout (showGraphicRendition graphicRenditions.doublyUnderlined "Doubly Underlined") stdout (showGraphicRendition graphicRenditions.normalIntensity "Normal Intensity") @@ -78,9 +74,9 @@ showColors3Or4Bit stdout = line graphicRenditionFunction content = needs (function.is1 graphicRenditionFunction) needs (text.is content) - coloredLine 0 8 graphicRenditionFunction { color -> content } nbsp - columnHeaders = iterator.range 8 | iterator.map { color -> "{nbsp}{color} " } - | iterator.joinToTextWithSeparator nbsp + coloredLine 0 8 graphicRenditionFunction { color -> content } " " + columnHeaders = + iterator.range 8 | iterator.map { color -> " {color} " } | iterator.joinToTextWithSeparator " " stdout "# Colors: 3 and 4 Bit" stdout "" @@ -89,8 +85,8 @@ showColors3Or4Bit stdout = stdout "Bright: {line graphicRenditions.setBrightForegroundColor3Or4Bit "▓▓▓"}" stdout "" stdout "Background {columnHeaders}" - stdout "Normal: {line graphicRenditions.setBackgroundColor3Or4Bit "{nbsp} "}" - stdout "Bright: {line graphicRenditions.setBrightBackgroundColor3Or4Bit "{nbsp} "}" + stdout "Normal: {line graphicRenditions.setBackgroundColor3Or4Bit " "}" + stdout "Bright: {line graphicRenditions.setBrightBackgroundColor3Or4Bit " "}" showColors8Bit stdout = needs (function.is1 stdout) @@ -126,7 +122,7 @@ showColors8Bit stdout = stdout "" block graphicRenditions.setForegroundColor8Or24Bit "▓" stdout "" - block graphicRenditions.setBackgroundColor8Or24Bit nbsp + block graphicRenditions.setBackgroundColor8Or24Bit " " main := { environment -> showStyles environment.stdout diff --git a/packages/Csv/encode.candy b/packages/Csv/encode.candy index 9b54f278d..ac2934487 100644 --- a/packages/Csv/encode.candy +++ b/packages/Csv/encode.candy @@ -28,7 +28,7 @@ encode lines := | iterator.joinToText testEncodeLine = - # TODO(JonaWanke): Add tests cases for leading/trailing whitespace when our parser is fixed, + # TODO(JonaWanke): Add test cases for leading/trailing whitespace when our parser is fixed, # https://github.com/candy-lang/candy/issues/896 checkEquals (encodeLine (,)) "" checkEquals (encodeLine ("aaa",)) "aaa"