From 5957e8e5508a3772b2229fc9d8ac30ce4173d356 Mon Sep 17 00:00:00 2001 From: Joscha Date: Thu, 4 Aug 2022 02:04:27 +0200 Subject: [PATCH] Fix tab width calculations when word wrapping --- src/wrap.rs | 52 +++++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/src/wrap.rs b/src/wrap.rs index 21e1814..4ebed46 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -12,10 +12,10 @@ pub fn wrap(widthdb: &mut WidthDB, text: &str, width: usize) -> Vec { // The last valid break point encountered and its width let mut valid_break = None; - let mut valid_break_width = 0; - // Width of the line at the current grapheme (with and without trailing - // whitespace) + // Starting index and width of the line at the current grapheme (with and + // without trailing whitespace) + let mut current_start = 0; let mut current_width = 0; let mut current_width_trimmed = 0; @@ -36,65 +36,55 @@ pub fn wrap(widthdb: &mut WidthDB, text: &str, width: usize) -> Vec { BreakOpportunity::Mandatory => { breaks.push(bi); valid_break = None; - valid_break_width = 0; + current_start = bi; current_width = 0; current_width_trimmed = 0; } BreakOpportunity::Allowed => { valid_break = Some(bi); - valid_break_width = current_width; } } } // Calculate widths after current grapheme let g_is_whitespace = g.chars().all(|c| c.is_whitespace()); - let g_width = if g == "\t" { - widthdb.tab_width_at_column(current_width) as usize - } else { - widthdb.grapheme_width(g) as usize - }; - let g_width_trimmed = if g_is_whitespace { 0 } else { g_width }; - let mut new_width = current_width + g_width; - let mut new_width_trimmed = if g_is_whitespace { - current_width_trimmed - } else { - new_width - }; + let g_width = widthdb.grapheme_width(g, current_width) as usize; + current_width += g_width; + if !g_is_whitespace { + current_width_trimmed = current_width; + } // Wrap at last break point if necessary - if new_width_trimmed > width { + if current_width_trimmed > width { if let Some(bi) = valid_break { + let new_line = &text[bi..gi + g.len()]; + breaks.push(bi); - new_width -= valid_break_width; - new_width_trimmed = new_width_trimmed.saturating_sub(valid_break_width); valid_break = None; - valid_break_width = 0; + current_start = bi; + current_width = widthdb.width(new_line); + current_width_trimmed = widthdb.width(new_line.trim_end()); } } // Perform a forced break if still necessary - if new_width_trimmed > width { - if new_width == g_width { + if current_width_trimmed > width { + if current_start == gi { // The grapheme is the only thing on the current line and it is // wider than the maximum width, so we'll allow it, thereby // forcing the following grapheme to break no matter what // (either because of a mandatory or allowed break, or via a // forced break). } else { - // Forced break in the midde of a normally non-breakable chunk + // Forced break in the middle of a normally non-breakable chunk // because there are no valid break points. breaks.push(gi); - new_width = g_width; - new_width_trimmed = g_width_trimmed; valid_break = None; - valid_break_width = 0; + current_start = gi; + current_width = widthdb.grapheme_width(g, 0).into(); + current_width_trimmed = if g_is_whitespace { 0 } else { current_width }; } } - - // Update current width - current_width = new_width; - current_width_trimmed = new_width_trimmed; } breaks