Improve word wrapping
Now supports long trailing whitespace as well as tabs.
This commit is contained in:
parent
9b0d80873f
commit
11b2211fad
5 changed files with 106 additions and 41 deletions
72
src/wrap.rs
72
src/wrap.rs
|
|
@ -5,9 +5,11 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||
|
||||
use crate::widthdb::WidthDB;
|
||||
|
||||
// TODO Handle tabs separately?
|
||||
// TODO Convert into an iterator?
|
||||
pub fn wrap(text: &str, width: usize, widthdb: &mut WidthDB) -> Vec<usize> {
|
||||
pub fn tab_width_at_column(tab_width: u8, col: usize) -> u8 {
|
||||
tab_width - (col % tab_width as usize) as u8
|
||||
}
|
||||
|
||||
pub fn wrap(widthdb: &mut WidthDB, tab_width: u8, text: &str, width: usize) -> Vec<usize> {
|
||||
let mut breaks = vec![];
|
||||
|
||||
let mut break_options = unicode_linebreak::linebreaks(text).peekable();
|
||||
|
|
@ -16,8 +18,10 @@ pub fn wrap(text: &str, width: usize, widthdb: &mut WidthDB) -> Vec<usize> {
|
|||
let mut valid_break = None;
|
||||
let mut valid_break_width = 0;
|
||||
|
||||
// Width of the line at the current grapheme
|
||||
// Width of the line at the current grapheme (with and without trailing
|
||||
// whitespace)
|
||||
let mut current_width = 0;
|
||||
let mut current_width_trimmed = 0;
|
||||
|
||||
for (gi, g) in text.grapheme_indices(true) {
|
||||
// Advance break options
|
||||
|
|
@ -38,6 +42,7 @@ pub fn wrap(text: &str, width: usize, widthdb: &mut WidthDB) -> Vec<usize> {
|
|||
valid_break = None;
|
||||
valid_break_width = 0;
|
||||
current_width = 0;
|
||||
current_width_trimmed = 0;
|
||||
}
|
||||
BreakOpportunity::Allowed => {
|
||||
valid_break = Some(bi);
|
||||
|
|
@ -46,31 +51,52 @@ pub fn wrap(text: &str, width: usize, widthdb: &mut WidthDB) -> Vec<usize> {
|
|||
}
|
||||
}
|
||||
|
||||
let grapheme_width: usize = widthdb.grapheme_width(g).into();
|
||||
if current_width + grapheme_width > width {
|
||||
if current_width == 0 {
|
||||
// The grapheme 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 if let Some(bi) = valid_break {
|
||||
// We can't fit the grapheme onto the current line, so we'll
|
||||
// just break at the last valid break point.
|
||||
// Calculate widths after current grapheme
|
||||
let g_width = if g == "\t" {
|
||||
tab_width_at_column(tab_width, current_width) as usize
|
||||
} else {
|
||||
widthdb.grapheme_width(g) as usize
|
||||
};
|
||||
let mut new_width = current_width + g_width;
|
||||
let mut new_width_trimmed = if g.chars().all(|c| c.is_whitespace()) {
|
||||
current_width_trimmed
|
||||
} else {
|
||||
new_width
|
||||
};
|
||||
|
||||
// Wrap at last break point if necessary
|
||||
if new_width_trimmed > width {
|
||||
if let Some(bi) = valid_break {
|
||||
breaks.push(bi);
|
||||
current_width -= valid_break_width;
|
||||
new_width -= valid_break_width;
|
||||
new_width_trimmed = new_width_trimmed.saturating_sub(valid_break_width);
|
||||
valid_break = None;
|
||||
valid_break_width = 0;
|
||||
} else {
|
||||
// Forced break in the midde of a normally non-breakable chunk
|
||||
// because there have been no valid break points yet.
|
||||
breaks.push(gi);
|
||||
valid_break = None;
|
||||
valid_break_width = 0;
|
||||
current_width = 0;
|
||||
}
|
||||
}
|
||||
|
||||
current_width += grapheme_width;
|
||||
// Perform a forced break if still necessary
|
||||
if new_width_trimmed > width {
|
||||
if new_width == g_width {
|
||||
// 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
|
||||
// because there are no valid break points.
|
||||
breaks.push(gi);
|
||||
new_width = 0;
|
||||
new_width_trimmed = 0;
|
||||
valid_break = None;
|
||||
valid_break_width = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update current width
|
||||
current_width = new_width;
|
||||
current_width_trimmed = new_width_trimmed;
|
||||
}
|
||||
|
||||
breaks
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue