-
Notifications
You must be signed in to change notification settings - Fork 487
Version 0.10: Spans can't contain line-breaks #339
Comments
Hi @fdehau ! Following on from the discussion in #341, I figured I'd give more examples of my problem. One of my biggest grievances is how difficult it's become to apply a style to a multi-line string. If there is no style, I can just use fn print_question(quiz: &QuizRef) -> Vec<Span> {
let style = Style::default().add_modifier(Modifier::BOLD);
vec![Span::styled(format!("{}\n\n", dbg!(quiz.borrow().ask())), style)]
} Where [src/console/render.rs:72] quiz.borrow().ask() = "*verb*\n 1) to soothe in temper of disposition : appease\n 2) to reduce the rigidity of : soften\n 3) to reduce in intensity : assuage, temper" Becomes something like this (untested pseudo-code) in 0.10: fn print_question(quiz: &QuizRef) -> Vec<Spans> {
let style = Style::default().add_modifier(Modifier::BOLD);
let mut result = quiz.borrow().ask().lines()
.map(|line| Spans::from(Span::styled(line, style)))
.collect();
// Whoops, not even this would work:
// result.push(Spans::from(Span::raw("\n\n"));
result.push(Spans::from(""));
result.push(Spans::from(""));
result
} I find that a lot of boilerplate and bloat for, in this case, no extra functionality. When I already have a string with line-breaks, it's become unreasonably difficult to apply a style to them. It also makes inserting empty lines, particularly several in a row, somewhat annoying, as I now need two repetitive lines of Some of this ultimately falls in again with the last PR I opened I suppose, when the user has a string with whitespaces and newlines, it's very unexpected for the library to go stripping them and changing the formatting. I'd love to know what you think about all of this! |
Same here with |
(Moved here from #341, sorry for the noise) First off, the new Text/Spans/Span approach allowed me to remove my custom multi-line List hack, thanks for that! Ignoring the issue of nested vectors (which IMHO needs addressing as well, but I have not though of a comprehensive solution, yet), the main point for me is that things seem to be only "half way there". Converting a For my use case, I guess I'd already be pretty happy with just two methods on One note on allocations: about 95% of my |
FWIW, I just scrambled together this extension trait, which isn't quite ideal, but makes life a lot easier for me. pub trait TextExt<'a> {
fn append<T: Into<Cow<'a, str>>>(&mut self, s: T) -> &mut Self;
fn append_styled<T: Into<Cow<'a, str>>>(&mut self, s: T, style: Style) -> &mut Self;
}
impl<'a> TextExt<'a> for Text<'a> {
fn append<T: Into<Cow<'a, str>>>(&mut self, s: T) -> &mut Self {
self.append_styled(s, Style::default())
}
fn append_styled<T: Into<Cow<'a, str>>>(&mut self, s: T, style: Style) -> &mut Self {
match s.into() {
Cow::Borrowed(s) => {
let mut lines = s.lines();
if let Some(last) = self.lines.last_mut() {
if let Some(line) = lines.next() {
last.0.push(Span::styled(line, style));
}
}
self.lines.extend(lines.map(|line| Spans(vec![Span::styled(line, style)])));
}
Cow::Owned(s) => {
let mut lines = s.lines();
if let Some(last) = self.lines.last_mut() {
if let Some(line) = lines.next() {
last.0.push(Span::styled(line.to_owned(), style));
}
}
self.lines.extend(lines.map(|line| Spans(vec![Span::styled(line.to_owned(), style)])));
}
}
self
}
} |
Ok I can understand that and as I suggested and as @dotdash showed, this might simply be a matter of providing "builder" methods on
I don't agree with this statement when the library clearly defines
Would the proposed solution work with your use case ?
Are you simply worried about that or is it a perf issue for
Again, the documentation clearly states
I would like to see numbers before applying that kind of optimization which will make |
@fdehau @extrawurst and @dotdash , I've tossed together a PR (#361) that I hope addresses the ergonomics of this issue (I've not included any optimisation changes). The added code is short, consistent with the rest of the library (particularly I'll post some code snippets here soon showing off this new abstraction and how it addresses some of the issues I'd been having. |
I'm happy to report that, working with this new abstraction, I've ended up with even nicer code than before 0.10 while retaining all of the guarantees of the new text system. Here is a code snippet from earlier in this conversation, using my old system that just allowed newlines in fn print_question(quiz: &QuizRef) -> Vec<Span> {
let style = Style::default().add_modifier(Modifier::BOLD);
vec![Span::styled(format!("{}\n\n", dbg!(quiz.borrow().ask())), style)]
} And here is the current working version (with my new PR): fn print_question(quiz: &QuizRef) -> Text {
let style = Style::default().add_modifier(Modifier::BOLD);
Text::styled(format!("{}\n\n", quiz.borrow().ask()), style)
} It's shorter, more semantic, and maintains the abstraction. Some other wonderful things? Implementing Hardly needed to change at all: Yup! Almost exactly the same as the vector system! Even shorter because I could drop all the silly If you are only ever dealing with external, pre-formatted, multi-line strings, like I am and (I presume) I'd love to know what you think about all of this! I'm quite pleased myself, but I'm only a sample-size of one. |
Closed by #361 ! 🎉 |
Describe the bug
I understand that multiple lines in a paragraph are intended to be represented using
Text
, but I seem unable to apply a span over several lines which I think is still very useful.To Reproduce
Using the current development version from Git, I'm ultimately building the following:
Which results in the following (note that all of my
\n
's were ignored):Expected behavior
I'm really looking for what I had before the text refactoring:
Desktop:
The text was updated successfully, but these errors were encountered: