Wrap a list of styled spans into multiple Lines, breaking at `max_width` display columns. Preserves the style of each span across line breaks. Breaks prefer word boundaries (spaces) when possible.
(spans: Vec<Span<'static>>, max_width: usize, out: &mut Vec<Line<'static>>)
| 388 | /// display columns. Preserves the style of each span across line breaks. |
| 389 | /// Breaks prefer word boundaries (spaces) when possible. |
| 390 | fn wrap_spans_to_lines(spans: Vec<Span<'static>>, max_width: usize, out: &mut Vec<Line<'static>>) { |
| 391 | if max_width == 0 { |
| 392 | out.push(Line::from(spans)); |
| 393 | return; |
| 394 | } |
| 395 | |
| 396 | let total_width: usize = spans |
| 397 | .iter() |
| 398 | .map(|s| UnicodeWidthStr::width(s.content.as_ref())) |
| 399 | .sum(); |
| 400 | if total_width <= max_width { |
| 401 | out.push(Line::from(spans)); |
| 402 | return; |
| 403 | } |
| 404 | |
| 405 | let mut current_spans: Vec<Span<'static>> = Vec::new(); |
| 406 | let mut current_width: usize = 0; |
| 407 | |
| 408 | for span in spans { |
| 409 | let span_text: &str = span.content.as_ref(); |
| 410 | let span_w = UnicodeWidthStr::width(span_text); |
| 411 | |
| 412 | if current_width + span_w <= max_width { |
| 413 | current_spans.push(span); |
| 414 | current_width += span_w; |
| 415 | continue; |
| 416 | } |
| 417 | |
| 418 | // Need to split this span across lines |
| 419 | let style = span.style; |
| 420 | let mut remaining = span_text.to_string(); |
| 421 | |
| 422 | while !remaining.is_empty() { |
| 423 | let avail = max_width.saturating_sub(current_width); |
| 424 | |
| 425 | if avail == 0 { |
| 426 | out.push(Line::from(std::mem::take(&mut current_spans))); |
| 427 | current_width = 0; |
| 428 | continue; |
| 429 | } |
| 430 | |
| 431 | let rem_w = UnicodeWidthStr::width(remaining.as_str()); |
| 432 | if rem_w <= avail { |
| 433 | current_width += rem_w; |
| 434 | current_spans.push(Span::styled(remaining, style)); |
| 435 | break; |
| 436 | } |
| 437 | |
| 438 | // Find a split point: prefer space near the boundary |
| 439 | let (split, take_w) = find_wrap_point(&remaining, avail); |
| 440 | |
| 441 | if split > 0 { |
| 442 | let piece: String = remaining[..split].to_string(); |
| 443 | remaining = remaining[split..].to_string(); |
| 444 | // Trim leading space on the continuation line |
| 445 | if remaining.starts_with(' ') { |
| 446 | remaining = remaining[1..].to_string(); |
| 447 | } |