From 93d37649ae63312548bb319e5d6d7305834e502f Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Mon, 1 Nov 2021 22:31:33 +0100 Subject: Remove +/- line prefix instead of substituting a space Simplifies line handling and printing by removing a "magical" 1-offset previously required in various locations. Now explicitly prepend "" in `tokenize()`. --- src/align.rs | 8 +- src/edits.rs | 31 +++--- src/features/side_by_side.rs | 18 ++-- src/handlers/hunk_header.rs | 11 +- src/paint.rs | 59 ++++------- src/tests/ansi_test_utils.rs | 2 +- src/tests/test_example_diffs.rs | 7 +- src/wrapping.rs | 228 ++++++++++++++++++++-------------------- 8 files changed, 179 insertions(+), 185 deletions(-) (limited to 'src') diff --git a/src/align.rs b/src/align.rs index 4cd102c6..6da05cc1 100644 --- a/src/align.rs +++ b/src/align.rs @@ -17,13 +17,14 @@ use Operation::*; /// Needleman-Wunsch / Wagner-Fischer table for computation of edit distance and associated /// alignment. -#[derive(Clone)] +#[derive(Clone, Debug)] struct Cell { parent: usize, operation: Operation, cost: usize, } +#[derive(Debug)] pub struct Alignment<'a> { pub x: Vec<&'a str>, pub y: Vec<&'a str>, @@ -34,9 +35,8 @@ pub struct Alignment<'a> { impl<'a> Alignment<'a> { /// Fill table for Levenshtein distance / alignment computation pub fn new(x: Vec<&'a str>, y: Vec<&'a str>) -> Self { - // TODO: Something about the alignment algorithm requires that the first two items in the - // token stream are ["", " "]. In practice this means that the line must have a leading - // space, and that the tokenization regex cooperates. + // TODO: Something downstream of the alignment algorithm requires that the first token in + // both x and y is "", so this is explicitly inserted in `tokenize()`. let dim = [y.len() + 1, x.len() + 1]; let table = vec![ Cell { diff --git a/src/edits.rs b/src/edits.rs index 06d934ff..11979aa8 100644 --- a/src/edits.rs +++ b/src/edits.rs @@ -30,8 +30,7 @@ pub fn infer_edits<'a, EditOperation>( Vec<(Option, Option)>, // line alignment ) where - EditOperation: Copy, - EditOperation: PartialEq, + EditOperation: Copy + PartialEq + std::fmt::Debug, { let mut annotated_minus_lines = Vec::>::new(); let mut annotated_plus_lines = Vec::>::new(); @@ -95,7 +94,9 @@ where /// Split line into tokens for alignment. The alignment algorithm aligns sequences of substrings; /// not individual characters. fn tokenize<'a>(line: &'a str, regex: &Regex) -> Vec<&'a str> { - let mut tokens = Vec::new(); + // Starting with "", see comment in Alignment::new(). Historical note: Replacing the '+/-' + // prefix with a space implicitly generated this. + let mut tokens = vec![""]; let mut offset = 0; for m in regex.find_iter(line) { if offset == 0 && m.start() > 0 { @@ -136,8 +137,7 @@ fn annotate<'a, Annotation>( plus_line: &'a str, ) -> (Vec<(Annotation, &'a str)>, Vec<(Annotation, &'a str)>, f64) where - Annotation: Copy, - Annotation: PartialEq, + Annotation: Copy + PartialEq + std::fmt::Debug, { let mut annotated_minus_line = Vec::new(); let mut annotated_plus_line = Vec::new(); @@ -467,7 +467,10 @@ mod tests { fn assert_tokenize(text: &str, expected_tokens: &[&str]) { let actual_tokens = tokenize(text, &*DEFAULT_TOKENIZATION_REGEXP); assert_eq!(text, expected_tokens.iter().join("")); - assert_eq!(actual_tokens, expected_tokens); + // tokenize() guarantees that the first element of the token stream is "". + // See comment in Alignment::new() + assert_eq!(actual_tokens[0], ""); + assert_eq!(&actual_tokens[1..], expected_tokens); } #[test] @@ -476,8 +479,8 @@ mod tests { vec!["aaa"], vec!["aba"], ( - vec![vec![(Deletion, "aaa")]], - vec![vec![(Insertion, "aba")]], + vec![vec![(MinusNoop, ""), (Deletion, "aaa")]], + vec![vec![(PlusNoop, ""), (Insertion, "aba")]], ), ) } @@ -488,8 +491,12 @@ mod tests { vec!["aaa ccc"], vec!["aba ccc"], ( - vec![vec![(Deletion, "aaa"), (MinusNoop, " ccc")]], - vec![vec![(Insertion, "aba"), (PlusNoop, " ccc")]], + vec![vec![ + (MinusNoop, ""), + (Deletion, "aaa"), + (MinusNoop, " ccc"), + ]], + vec![vec![(PlusNoop, ""), (Insertion, "aba"), (PlusNoop, " ccc")]], ), ) } @@ -500,8 +507,8 @@ mod tests { vec!["áaa"], vec!["ááb"], ( - vec![vec![(Deletion, "áaa")]], - vec![vec![(Insertion, "ááb")]], + vec![vec![(MinusNoop, ""), (Deletion, "áaa")]], + vec![vec![(PlusNoop, ""), (Insertion, "ááb")]], ), ) } diff --git a/src/features/side_by_side.rs b/src/features/side_by_side.rs index ddc7f55d..7f944772 100644 --- a/src/features/side_by_side.rs +++ b/src/features/side_by_side.rs @@ -62,7 +62,8 @@ pub fn available_line_width( ) -> line_numbers::SideBySideLineWidth { let linennumbers_width = data.formatted_width(); - // The width can be reduced by the line numbers and/or a possibly kept 1-wide "+/-/ " prefix. + // The width can be reduced by the line numbers and/or + // a possibly added/restored 1-wide "+/-/ " prefix. let line_width = |side: PanelSide| { config.side_by_side_data[side] .width @@ -76,15 +77,15 @@ pub fn available_line_width( pub fn line_is_too_long(line: &str, line_width: usize) -> bool { let line_sum = line.graphemes(true).count(); - // `line_sum` is too large, because both a leading "+/-/ " and a trailing - // newline are present, counted, but are never printed. So allow two more - // characters. - line_sum > line_width + 2 + debug_assert!(line.ends_with('\n')); + // `line_sum` is too large because a trailing newline is present, + // so allow one more character. + line_sum > line_width + 1 } /// Return whether any of the input lines is too long, and a data -/// structure indicating which are too long. This avoids -/// calculating the length again later. +/// structure indicating which of the input lines are too long. This avoids +/// recalculating the length later. pub fn has_long_lines( lines: &LeftRight<&Vec<(String, State)>>, line_width: &line_numbers::SideBySideLineWidth, @@ -325,8 +326,7 @@ fn get_right_fill_style_for_panel<'a>( // wish to display the right panel, with its line number container, but without any line number // (and without any line contents). We do this by passing (HunkMinus, Right) to `paint_line`, since // what this will do is set the line number pair in that function to `(Some(minus_number), None)`, -// and then only emit the right field (which has a None number, i.e. blank). However, it will also -// increment the minus line number, so we need to knock that back down. +// and then only emit the right field (which has a None number, i.e. blank). #[allow(clippy::too_many_arguments)] fn paint_minus_or_plus_panel_line<'a>( line_index: Option, diff --git a/src/handlers/hunk_header.rs b/src/handlers/hunk_header.rs index a48ad834..525b749d 100644 --- a/src/handlers/hunk_header.rs +++ b/src/handlers/hunk_header.rs @@ -166,7 +166,7 @@ fn write_hunk_header( let (mut draw_fn, _, decoration_ansi_term_style) = draw::get_draw_function(config.hunk_header_style.decoration_style); let line = if config.color_only { - format!(" {}", &line) + line.to_string() } else if !code_fragment.is_empty() { format!("{} ", code_fragment) } else { @@ -242,7 +242,14 @@ fn write_to_output_buffer( ); } if !file_with_line_number.is_empty() { - let _ = write!(&mut painter.output_buffer, "{}: ", file_with_line_number); + // The code fragment in "line" adds whitespace, but if only a line number is printed + // then the trailing space must be added. + let space = if line.is_empty() { " " } else { "" }; + let _ = write!( + &mut painter.output_buffer, + "{}:{}", + file_with_line_number, space + ); } if !line.is_empty() { painter.syntax_highlight_and_paint_line( diff --git a/src/paint.rs b/src/paint.rs index 0315b19f..264cecdc 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -109,8 +109,7 @@ impl<'p> Painter<'p> { }; } - /// Replace initial -/+ character with ' ', expand tabs as spaces, and optionally terminate with - /// newline. + /// Remove initial -/+ character, expand tabs as spaces, and terminate with newline. // Terminating with newline character is necessary for many of the sublime syntax definitions to // highlight correctly. // See https://docs.rs/syntect/3.2.0/syntect/parsing/struct.SyntaxSetBuilder.html#method.add_from_folder @@ -118,15 +117,11 @@ impl<'p> Painter<'p> { if !line.is_empty() { let mut line = line.graphemes(true); - // The first column contains a -/+/space character, added by git. We substitute it for - // a space now, so that it is not present during syntax highlighting. When emitting the - // line in Painter::paint_line, we drop the space (unless --keep-plus-minus-markers is - // in effect in which case we replace it with the appropriate marker). - // TODO: Things should, but do not, work if this leading space is omitted at this stage. - // See comment in align::Alignment::new. - // Note that a wrapped line also has a leading character added to remain compatible. + // The first column contains a -/+/space character, added by git. We remove it now so that + // it is not present during syntax highlighting or wrapping. If --keep-plus-minus-markers is + // in effect this character is re-inserted in Painter::paint_line. line.next(); - format!(" {}\n", self.expand_tabs(line)) + format!("{}\n", self.expand_tabs(line)) } else { "\n".to_string() } @@ -301,10 +296,12 @@ impl<'p> Painter<'p> { pub fn paint_zero_line(&mut self, line: &str) { let state = State::HunkZero; let painted_prefix = if self.config.keep_plus_minus_markers && !line.is_empty() { + // A zero line here still contains the " " prefix, so use it. Some(self.config.zero_style.paint(&line[..1])) } else { None }; + let lines = vec![(self.prepare(line), state.clone())]; let syntax_style_sections = Painter::get_syntax_style_sections_for_lines( &lines, @@ -567,11 +564,7 @@ impl<'p> Painter<'p> { // This line has been identified as one which should be emitted unchanged, // including any ANSI escape sequences that it has. return ( - format!( - "{}{}", - ansi_term::ANSIStrings(&ansi_strings).to_string(), - raw_line - ), + format!("{}{}", ansi_term::ANSIStrings(&ansi_strings), raw_line), false, ); } @@ -587,24 +580,15 @@ impl<'p> Painter<'p> { let mut handled_prefix = false; for (section_style, text) in &superimposed { - let text = if handled_prefix { - &text - } else { - // Remove what was originally the +/- prefix, see `prepare()`, after - // (if requested) re-inserting it with proper styling. + // If requested re-insert the +/- prefix with proper styling. + if !handled_prefix { if let Some(ref painted_prefix) = painted_prefix { ansi_strings.push(painted_prefix.clone()); } - - if !text.is_empty() { - &text[1..] - } else { - &text - } - }; + } if !text.is_empty() { - ansi_strings.push(section_style.paint(text)); + ansi_strings.push(section_style.paint(text.as_str())); } handled_prefix = true; } @@ -656,13 +640,8 @@ impl<'p> Painter<'p> { Painter::should_compute_syntax_highlighting(state, config), ) { (Some(highlighter), true) => { - // The first character is a space injected by delta. See comment in - // Painter:::prepare. for (line, _) in lines.iter() { - let mut this_line_sections = - highlighter.highlight(&line[1..], &config.syntax_set); - this_line_sections.insert(0, (config.null_syntect_style, &line[..1])); - line_sections.push(this_line_sections); + line_sections.push(highlighter.highlight(line, &config.syntax_set)); } } _ => { @@ -774,16 +753,14 @@ fn style_sections_contain_more_than_one_style(sections: &[(Style, &str)]) -> boo } /// True iff the line represented by `sections` constitutes a whitespace error. -// Note that a space is always present as the first character in the line (it was put there as a -// replacement for the leading +/- marker; see paint::prepare()). A line is a whitespace error iff, -// beyond the initial space character, (a) there are more characters and (b) they are all -// whitespace characters. -// TODO: Git recognizes blank lines at end of file (blank-at-eof) as a whitespace error but delta -// does not yet. +// A line is a whitespace error iff it is non-empty and contains only whitespace +// characters. +// TODO: Git recognizes blank lines at end of file (blank-at-eof) +// as a whitespace error but delta does not yet. // https://git-scm.com/docs/git-config#Documentation/git-config.txt-corewhitespace fn is_whitespace_error(sections: &[(Style, &str)]) -> bool { let mut any_chars = false; - for c in sections.iter().flat_map(|(_, s)| s.chars()).skip(1) { + for c in sections.iter().flat_map(|(_, s)| s.chars()) { if c == '\n' { return any_chars; } else if c != ' ' && c != '\t' { diff --git a/src/tests/ansi_test_utils.rs b/src/tests/ansi_test_utils.rs index 3f4fbe96..e63f0ea1 100644 --- a/src/tests/ansi_test_utils.rs +++ b/src/tests/ansi_test_utils.rs @@ -129,7 +129,7 @@ pub mod ansi_test_utils { }; painter.set_syntax(Some(language_extension)); painter.set_highlighter(); - let lines = vec![(format!(" {}", line), state.clone())]; + let lines = vec![(line.to_string(), state.clone())]; let syntax_style_sections = paint::Painter::get_syntax_style_sections_for_lines( &lines, &state, diff --git a/src/tests/test_example_diffs.rs b/src/tests/test_example_diffs.rs index d60067b6..dda5bcf6 100644 --- a/src/tests/test_example_diffs.rs +++ b/src/tests/test_example_diffs.rs @@ -972,11 +972,10 @@ src/align.rs if args.contains(&"--max-line-length") { return; } - for n in 0..input_lines.len() { - let input_line = input_lines[n]; + for (n, input_line) in input_lines.into_iter().enumerate() { // If config.line_numbers is enabled, // we should remove line_numbers decoration while checking. - let output_line = if config.line_numbers && n > 11 && n < input_lines.len() { + let output_line = if config.line_numbers && n > 11 { &output_lines[n][14..] } else { output_lines[n] @@ -1532,7 +1531,7 @@ src/align.rs:71: impl<'a> Alignment<'a> { │ } #[test] - fn test_color_only() { + fn test_color_only_mode() { let config = integration_test_utils::make_config_from_args(&["--color-only"]); let output = integration_test_utils::run_delta(GIT_DIFF_SINGLE_HUNK, &config); ansi_test_utils::assert_line_has_syntax_highlighted_substring( diff --git a/src/wrapping.rs b/src/wrapping.rs index d4fd851a..5dc44701 100644 --- a/src/wrapping.rs +++ b/src/wrapping.rs @@ -29,6 +29,12 @@ pub struct WrapConfig { pub inline_hint_syntect_style: SyntectStyle, } +#[derive(PartialEq)] +enum Stop { + StackEmpty, + LineLimit, +} + /// Wrap the given `line` if it is longer than `line_width`. Wrap to at most /// [Config::WrapConfig::max_lines](WrapConfig::max_lines) lines, /// then truncate again - but never truncate if it is `0`. Place @@ -40,9 +46,6 @@ pub struct WrapConfig { /// on the next line [right_prefix_symbol](WrapConfig::right_prefix_symbol). /// The inserted characters will follow the /// [inline_hint_syntect_style](WrapConfig::inline_hint_syntect_style). -/// -/// The input `line` is expected to start with an (ultimately not printed) `+`, `-` or ` ` prefix. -/// The prefix `_` is also added to the start of wrapped lines. pub fn wrap_line<'a, I, S>( config: &'a Config, line: I, @@ -59,18 +62,6 @@ where let wrap_config = &config.wrap_config; - // Symbol which: - // - represents the additional "+/-/ " prefix on the unwrapped input line, its - // length is added to the line_width. - // - can be more prominent than a space because syntax highlighting has already - // been done. - // - is added at the beginning of wrapped lines so the wrapped lines also have - // a prefix (which is not printed). - const LINEPREFIX: &str = "_"; - assert_eq!(LINEPREFIX.len(), INLINE_SYMBOL_WIDTH_1); // (args are const, optimized out) - - let max_len = line_width + LINEPREFIX.len(); - // The current line being assembled from the input to fit exactly into the given width. // A somewhat leaky abstraction as the fields are also accessed directly. struct CurrLine<'a, S: Default> { @@ -80,8 +71,8 @@ where impl<'a, S: Default> CurrLine<'a, S> { fn reset() -> Self { CurrLine { - line_segments: vec![(S::default(), LINEPREFIX)], - len: LINEPREFIX.len(), + line_segments: Vec::new(), + len: 0, } } fn push_and_set_len(&mut self, text: (S, &'a str), len: usize) { @@ -89,24 +80,14 @@ where self.len = len; } fn has_text(&self) -> bool { - self.len > LINEPREFIX.len() + self.len > 0 } fn text_len(&self) -> usize { - if self.len > LINEPREFIX.len() { - self.len - LINEPREFIX.len() - } else { - debug_assert!(false, "push or reset first"); - 0 - } + self.len } } - // The first `push_and_set_len` will include the "+/-/ " prefix, subsequent - // `reset()` add `LINEPREFIX`. Thus each line starts with a prefix. - let mut curr_line: CurrLine = CurrLine { - line_segments: Vec::new(), - len: 0, - }; + let mut curr_line = CurrLine::reset(); // Determine the background (diff) and color (syntax) of an inserted symbol. let symbol_style = match inline_hint_style { @@ -116,18 +97,22 @@ where let mut stack = line.into_iter().rev().collect::>(); - let line_limit_reached = |result: &Vec<_>| { - // If only the wrap symbol and no extra text fits, then wrapping is not possible. - let max_lines = if line_width <= INLINE_SYMBOL_WIDTH_1 { - 1 - } else { - wrap_config.max_lines - }; - - max_lines > 0 && result.len() + 1 >= max_lines + // If only the wrap symbol and no extra text fits, then wrapping is not possible. + let max_lines = if line_width <= INLINE_SYMBOL_WIDTH_1 { + 1 + } else { + wrap_config.max_lines }; - while !stack.is_empty() && !line_limit_reached(&result) && max_len > LINEPREFIX.len() { + let line_limit_reached = |result: &Vec<_>| max_lines > 0 && result.len() + 1 >= max_lines; + + let stop = loop { + if stack.is_empty() { + break Stop::StackEmpty; + } else if line_limit_reached(&result) { + break Stop::LineLimit; + } + let (style, text, graphemes) = stack .pop() .map(|(style, text)| (style, text, text.grapheme_indices(true).collect::>())) @@ -135,10 +120,10 @@ where let new_len = curr_line.len + graphemes.len(); - let must_split = if new_len < max_len { + let must_split = if new_len < line_width { curr_line.push_and_set_len((style, text), new_len); false - } else if new_len == max_len { + } else if new_len == line_width { match stack.last() { // Perfect fit, no need to make space for a `wrap_symbol`. None => { @@ -156,7 +141,7 @@ where } _ => true, } - } else if new_len == max_len + 1 && stack.is_empty() { + } else if new_len == line_width + 1 && stack.is_empty() { // If the one overhanging char is '\n' then keep it on the current line. if text.ends_with('\n') { // Do not count the included '\n': - 1 @@ -172,7 +157,7 @@ where // Text must be split, one part (or just `wrap_symbol`) is added to the // current line, the other is pushed onto the stack. if must_split { - let grapheme_split_pos = graphemes.len() - (new_len - max_len) - 1; + let grapheme_split_pos = graphemes.len() - (new_len - line_width) - 1; // The length does not matter anymore and `curr_line` will be reset // at the end, so move the line segments out. @@ -193,18 +178,17 @@ where curr_line = CurrLine::reset(); } - } + }; // Right-align wrapped line: // Done if wrapping adds exactly one line and this line is less than the given // permille wide. Also change the wrap symbol at the end of the previous (first) line. if result.len() == 1 && curr_line.has_text() { - let current_permille = (curr_line.text_len() * 1000) / max_len; + let current_permille = (curr_line.text_len() * 1000) / line_width; - let pad_len = max_len.saturating_sub(curr_line.text_len() + INLINE_SYMBOL_WIDTH_1); + let pad_len = line_width.saturating_sub(curr_line.text_len()); - if wrap_config.use_wrap_right_permille > current_permille && pad_len > INLINE_SYMBOL_WIDTH_1 - { + if wrap_config.use_wrap_right_permille > current_permille && pad_len > 0 { // The inserted spaces, which align a line to the right, point into this string. const SPACES: &str = " "; @@ -215,7 +199,7 @@ where _ => unreachable!("wrap result must not be empty"), } - let mut right_aligned_line = vec![(S::default(), LINEPREFIX)]; + let mut right_aligned_line = Vec::new(); for _ in 0..(pad_len / SPACES.len()) { right_aligned_line.push((*fill_style, SPACES)); @@ -228,19 +212,22 @@ where right_aligned_line.push((symbol_style, &wrap_config.right_prefix_symbol)); - // skip LINEPREFIX which `CurrLine::reset()` adds - right_aligned_line.extend(curr_line.line_segments.into_iter().skip(1)); + right_aligned_line.extend(curr_line.line_segments.into_iter()); curr_line.line_segments = right_aligned_line; - // curr_line.len not updated, as only 0 / 1 / > 1 is required now + // curr_line.len not updated, as only 0 / >0 for `has_text()` is required. } } - if curr_line.len > 0 { + if curr_line.has_text() { result.push(curr_line.line_segments); } + if stop == Stop::LineLimit && result.len() != max_lines { + result.push(Vec::new()); + } + // Anything that is left will be added to the (last) line. If this is too long it will // be truncated later. if !stack.is_empty() { @@ -641,60 +628,83 @@ mod tests { let cfg = mk_wrap_cfg(&TEST_WRAP_CFG); { - // Empty input without a "+/-/ "-prefix usually does not happen - let line = vec![(*S1, "")]; + let line = vec![(*SY, "0")]; let lines = wrap_test(&cfg, line, 6); - assert!(lines.is_empty()); + assert_eq!(lines, vec![vec![(*SY, "0")]]); } - { - let line = vec![(*SY, "_0")]; + let line = vec![(*S1, "012"), (*S2, "34")]; let lines = wrap_test(&cfg, line, 6); - assert_eq!(lines, vec![vec![(*SY, "_0")]]); + assert_eq!(lines, vec![vec![(*S1, "012"), (*S2, "34")]]); } - { - let line = vec![(*S1, "_")]; + let line = vec![(*S1, "012"), (*S2, "345")]; let lines = wrap_test(&cfg, line, 6); - assert_eq!(lines, vec![vec![(*S1, "_")]]); + assert_eq!(lines, vec![vec![(*S1, "012"), (*S2, "345")]]); + } + { + // Empty input usually does not happen + let line = vec![(*S1, "")]; + let lines = wrap_test(&cfg, line, 6); + assert!(lines.is_empty()); } - { - let line = vec![(*S1, "_"), (*S2, "0")]; + // Partially empty should not happen either + let line = vec![(*S1, ""), (*S2, "0")]; let lines = wrap_test(&cfg, line, 6); - assert_eq!(lines, vec![vec![(*S1, "_"), (*S2, "0")]]); + assert_eq!(lines, vec![vec![(*S1, ""), (*S2, "0")]]); } - { - let line = vec![(*S1, "_012"), (*S2, "34")]; + let line = vec![(*S1, "0"), (*S2, "")]; let lines = wrap_test(&cfg, line, 6); - assert_eq!(lines, vec![vec![(*S1, "_012"), (*S2, "34")]]); + assert_eq!(lines, vec![vec![(*S1, "0"), (*S2, "")]]); } - { - let line = vec![(*S1, "_012"), (*S2, "345")]; + let line = vec![ + (*S1, "0"), + (*S2, ""), + (*S1, ""), + (*S2, ""), + (*S1, ""), + (*S2, ""), + (*S1, ""), + (*S2, ""), + (*S1, ""), + (*S2, ""), + ]; let lines = wrap_test(&cfg, line, 6); - assert_eq!(lines, vec![vec![(*S1, "_012"), (*S2, "345")]]); + assert_eq!( + lines, + vec![vec![ + (*S1, "0"), + (*S2, ""), + (*S1, ""), + (*S2, ""), + (*S1, ""), + (*S2, ""), + (*S1, ""), + (*S2, ""), + (*S1, ""), + (*S2, "") + ]] + ); } } #[test] - fn test_wrap_line_align_right() { + fn test_wrap_line_align_right_1() { let cfg = mk_wrap_cfg(&TEST_WRAP_CFG); - let line = vec![(*S1, "_0123456789ab")]; + let line = vec![(*S1, "0123456789ab")]; let lines = wrap_test(&cfg, line, 11); assert_eq!(lines.len(), 2); assert_eq!(lines[0].last().unwrap().1, WR); - assert_eq!( - lines[1], - vec![(*SD, "_"), (*SD, " "), (*SD, ">"), (*S1, "ab")] - ); + assert_eq!(lines[1], [(*SD, " "), (*SD, ">"), (*S1, "ab")]); } #[test] fn test_wrap_line_align_right_2() { - let line = vec![(*S1, "_012"), (*S2, "3456")]; + let line = vec![(*S1, "012"), (*S2, "3456")]; { // Right align lines on the second line @@ -704,8 +714,8 @@ mod tests { assert_eq!( lines, vec![ - vec![(*S1, "_012"), (*S2, "34"), (*SD, WR)], - vec![(*SD, "_"), (*SD, " "), (*SD, RA), (*S2, "56")] + vec![(*S1, "012"), (*S2, "34"), (*SD, WR)], + vec![(*SD, " "), (*SD, RA), (*S2, "56")] ] ); } @@ -719,10 +729,7 @@ mod tests { let lines = wrap_test(&cfg_no_align_right, line, 6); assert_eq!( lines, - vec![ - vec![(*S1, "_012"), (*S2, "34"), (*SD, W)], - vec![(*SD, "_"), (*S2, "56")] - ] + vec![vec![(*S1, "012"), (*S2, "34"), (*SD, W)], vec![(*S2, "56")]] ); } } @@ -730,7 +737,7 @@ mod tests { #[test] fn test_wrap_line_newlines<'a>() { fn mk_input(len: usize) -> LineSegments<'static, Style> { - const IN: &str = "_0123456789abcdefZ"; + const IN: &str = "0123456789abcdefZ"; let v = &[*S1, *S2]; let s1s2 = v.iter().cycle(); let text: Vec<_> = IN.matches(|_| true).take(len + 1).collect(); @@ -745,7 +752,6 @@ mod tests { line } fn mk_expected<'a>( - prepend: Option<(Style, &'a str)>, vec: &LineSegments<'a, Style>, from: usize, to: usize, @@ -755,22 +761,19 @@ mod tests { if let Some(val) = append { result.push(val); } - if let Some(val) = prepend { - result.insert(0, val); - } result } let cfg = mk_wrap_cfg(&TEST_WRAP_CFG); { - let line = vec![(*S1, "_012"), (*S2, "345\n")]; + let line = vec![(*S1, "012"), (*S2, "345\n")]; let lines = wrap_test(&cfg, line, 6); - assert_eq!(lines, vec![vec![(*S1, "_012"), (*S2, "345\n")]]); + assert_eq!(lines, vec![vec![(*S1, "012"), (*S2, "345\n")]]); } { - for i in 0..=6 { + for i in 0..=5 { let line = mk_input(i); let lines = wrap_test(&cfg, line, 6); assert_eq!(lines, vec![mk_input(i)]); @@ -785,27 +788,28 @@ mod tests { let line = mk_input_nl(9); let lines = wrap_test(&cfg, line, 3); let expected = mk_input_nl(9); - let line1 = mk_expected(None, &expected, 0, 3, Some((*SD, &W))); - let line2 = mk_expected(Some((*SD, "_")), &expected, 3, 5, Some((*SD, &W))); - let line3 = mk_expected(Some((*SD, "_")), &expected, 5, 7, Some((*SD, &W))); - let line4 = mk_expected(Some((*SD, "_")), &expected, 7, 11, None); - assert_eq!(lines, vec![line1, line2, line3, line4]); + let line1 = mk_expected(&expected, 0, 2, Some((*SD, &W))); + let line2 = mk_expected(&expected, 2, 4, Some((*SD, &W))); + let line3 = mk_expected(&expected, 4, 6, Some((*SD, &W))); + let line4 = mk_expected(&expected, 6, 8, Some((*SD, &W))); + let line5 = mk_expected(&expected, 8, 11, None); + assert_eq!(lines, vec![line1, line2, line3, line4, line5]); } { let line = mk_input_nl(10); let lines = wrap_test(&cfg, line, 3); let expected = mk_input_nl(10); - let line1 = mk_expected(None, &expected, 0, 3, Some((*SD, &W))); - let line2 = mk_expected(Some((*SD, "_")), &expected, 3, 5, Some((*SD, &W))); - let line3 = mk_expected(Some((*SD, "_")), &expected, 5, 7, Some((*SD, &W))); - let line4 = mk_expected(Some((*SD, "_")), &expected, 7, 9, Some((*SD, &W))); - let line5 = mk_expected(Some((*SD, "_")), &expected, 9, 11, Some((*S2, "\n"))); + let line1 = mk_expected(&expected, 0, 2, Some((*SD, &W))); + let line2 = mk_expected(&expected, 2, 4, Some((*SD, &W))); + let line3 = mk_expected(&expected, 4, 6, Some((*SD, &W))); + let line4 = mk_expected(&expected, 6, 8, Some((*SD, &W))); + let line5 = mk_expected(&expected, 8, 11, Some((*S2, "\n"))); assert_eq!(lines, vec![line1, line2, line3, line4, line5]); } { - let line = vec![(*S1, "_abc"), (*S2, "01230123012301230123"), (*S1, "ZZZZZ")]; + let line = vec![(*S1, "abc"), (*S2, "01230123012301230123"), (*S1, "ZZZZZ")]; let wcfg1 = mk_wrap_cfg(&WrapConfig { max_lines: 1, @@ -838,26 +842,26 @@ mod tests { // from UnicodeSegmentation documentation and the linked // Unicode Standard Annex #29 - let line = vec![(*S1, "_abc"), (*S2, "mnö̲"), (*S1, "xyz")]; + let line = vec![(*S1, "abc"), (*S2, "mnö̲"), (*S1, "xyz")]; let lines = wrap_test(&cfg, line, 4); assert_eq!( lines, vec![ - vec![(*S1, "_abc"), (*SD, &W)], - vec![(*SD, "_"), (*S2, "mnö̲"), (*SD, &W)], - vec![(*SD, "_"), (*S1, "xyz")] + vec![(*S1, "abc"), (*SD, &W)], + vec![(*S2, "mnö̲"), (*SD, &W)], + vec![(*S1, "xyz")] ] ); // Not working: Tailored grapheme clusters: क्षि = क् + षि - let line = vec![(*S1, "_abc"), (*S2, "deநி"), (*S1, "ghij")]; + let line = vec![(*S1, "abc"), (*S2, "deநி"), (*S1, "ghij")]; let lines = wrap_test(&cfg, line, 4); assert_eq!( lines, vec![ - vec![(*S1, "_abc"), (*SD, &W)], - vec![(*SD, "_"), (*S2, "deநி"), (*SD, &W)], - vec![(*SD, "_"), (*S1, "ghij")] + vec![(*S1, "abc"), (*SD, &W)], + vec![(*S2, "deநி"), (*SD, &W)], + vec![(*S1, "ghij")] ] ); } -- cgit v1.2.3