How about another style of paragraph wrapping?
WestXu opened this issue · comments
Currently according to the docs of Paragraph.wrap, we have two options:
let bullet_points = Text::from(r#"Some indented points:
- First thing goes here and is long so that it wraps
- Here is another point that is long enough to wrap"#);
// With leading spaces trimmed (window width of 30 chars):
Paragraph::new(bullet_points.clone()).wrap(Wrap { trim: true });
// Some indented points:
// - First thing goes here and is
// long so that it wraps
// - Here is another point that
// is long enough to wrap
// But without trimming, indentation is preserved:
Paragraph::new(bullet_points).wrap(Wrap { trim: false });
// Some indented points:
// - First thing goes here
// and is long so that it wraps
// - Here is another point
// that is long enough to wrap
They both are a bit counter-intuitive to me. What I expected is like this:
// Some indented points:
// - First thing goes here
// and is long so that it
// wraps
// - Here is another point
// that is long enough to
// wrap
To be specific, I expect the wrapped new lines to inherit the same indentation of the original line, which seems much clearer to me, especially in some multi-level indentation paragraphs like codes. Is it possible to achieve this?
So I've made an implementation of this, but I don't know how to integrate it to the repo's wrapping mechanism to make a PR.
It's a simple idea. The String-based implementation is like this:
fn wrap_line(ori: String, width: usize) -> String {
fn wrap(line: String, indent: usize, width: usize) -> Vec<String> {
if (line.len() <= width) || (indent >= width) {
vec![line]
} else {
let (a, b) = line.split_at(width);
let b = " ".repeat(indent) + b;
vec![vec![a.to_owned()], wrap(b, indent, width)].concat()
}
}
ori.split('\n')
.into_iter()
.map(|line| wrap(line.to_owned(), line.len() - line.trim().len(), width))
.flatten()
.collect::<Vec<String>>()
.join("\n")
}
fn main() {
println!(
"{}",
wrap_line(
String::from(
r#"Some indented points:
- First thing goes here and is long so that it wraps
- Here is another point that is long enough to wrap"#
),
28
)
)
}
And the output is like:
Some indented points:
- First thing goes here
and is long so that it w
raps
- Here is another point
that is long enough to w
rap
Then here is the actual implementation working on tui_rs::text::Text
:
fn wrap_text(ori: Text, width: usize) -> Text {
fn wrap(line: Spans, indent: usize, width: usize) -> Vec<Spans> {
if (line.width() <= width) || (indent >= width) {
vec![line]
} else {
let grs = line
.0
.iter()
.map(|span| span.styled_graphemes(span.style))
.flatten()
.collect::<Vec<StyledGrapheme>>();
let mut a = grs;
let b = a.split_off(width);
let b = vec![
vec![
StyledGrapheme {
symbol: " ",
style: a[0].style,
};
indent
],
b,
]
.concat();
let spans_a: Spans = a
.into_iter()
.map(|g| Span::styled(g.symbol.to_owned(), g.style))
.collect::<Vec<Span>>()
.into();
let spans_b: Spans = b
.into_iter()
.map(|g| Span::styled(g.symbol.to_owned(), g.style))
.collect::<Vec<Span>>()
.into();
vec![vec![spans_a], wrap(spans_b, indent, width)].concat()
}
}
ori.into_iter()
.map(|line| {
wrap(
line.to_owned(),
line.width()
- line
.0
.iter()
.map(|span| &span.content)
.join("")
.trim_start()
.len(),
width,
)
})
.flatten()
.collect::<Vec<Spans>>()
.into()
}
I don't know if people out there ever need a wrapping style like this other than me. It appears pretty neat when showing some code or yaml on a dashboard, by keeping all the hierarchy intact, being easily parsable by human eyes. Let me know if you need a PR on this (with some instructions on how to integrate it).