1use crate::{
2 ansi,
3 damage::{Damage, Rect},
4 BlitCell, BoxCharset, Cell, Frame, GlyphRegistry, Grid, RenderOp, Style, TruncateMode,
5};
6
7const DEFAULT_ELLIPSIS: &str = "…";
8const DAMAGE_MAX_RECTS: usize = 64;
9use crate::text::{
10 clip_to_cells_spans, clip_to_cells_text, ellipsis_to_cells_spans, ellipsis_to_cells_text,
11 normalize_spans, wrap_spans_wordwise, Span, WrapOpts,
12};
13use thiserror::Error;
14use unicode_segmentation::UnicodeSegmentation;
15
16#[derive(Debug, Error)]
17pub enum RenderError {
18 #[error("invalid grid size")]
19 InvalidGridSize,
20}
21
22#[derive(Debug, Clone)]
24pub struct Renderer {
25 grid: Grid,
26 reg: GlyphRegistry,
27}
28
29impl Renderer {
30 pub fn new(width: u16, height: u16, reg: GlyphRegistry) -> Result<Self, RenderError> {
31 if width == 0 || height == 0 {
32 return Err(RenderError::InvalidGridSize);
33 }
34 Ok(Self {
35 grid: Grid::new(width, height),
36 reg,
37 })
38 }
39
40 pub fn grid(&self) -> &Grid {
41 &self.grid
42 }
43
44 pub fn grid_mut(&mut self) -> &mut Grid {
45 &mut self.grid
46 }
47
48 pub fn apply(&mut self, frame: &Frame) {
49 for op in &frame.ops {
50 self.apply_op(op);
51 }
52 }
53
54 pub fn apply_with_damage(&mut self, frame: &Frame) -> Damage {
60 let mut dmg = Damage::empty();
61 for op in &frame.ops {
62 self.apply_op(op);
63 self.note_damage_for_op(op, &mut dmg);
64 if dmg.full_redraw {
65 break;
66 }
67 }
68 dmg
69 }
70
71 fn note_damage_for_op(&self, op: &RenderOp, dmg: &mut Damage) {
72 let w = self.grid.width;
73 let h = self.grid.height;
74
75 fn clip_rect(r: Rect, gw: u16, gh: u16) -> Rect {
76 if r.w == 0 || r.h == 0 {
77 return Rect::new(0, 0, 0, 0);
78 }
79 let x1 = r.x.min(gw);
80 let y1 = r.y.min(gh);
81 let x2 = r.right().min(gw);
82 let y2 = r.bottom().min(gh);
83 Rect::new(x1, y1, x2.saturating_sub(x1), y2.saturating_sub(y1))
84 }
85
86 let mut push = |r: Rect| {
87 let r = clip_rect(r, w, h);
88 dmg.push_rect(r, DAMAGE_MAX_RECTS);
89 };
90
91 match op {
92 RenderOp::Clear => {
93 dmg.full_redraw = true;
94 dmg.rects.clear();
95 }
96 RenderOp::ClearLine { y } => push(Rect::new(0, *y, w, 1)),
97 RenderOp::ClearEol { x, y } => push(Rect::new(*x, *y, w.saturating_sub(*x), 1)),
98 RenderOp::ClearBol { x, y } => push(Rect::new(0, *y, x.saturating_add(1), 1)),
99 RenderOp::ClearEos { x, y } => {
100 push(Rect::new(*x, *y, w.saturating_sub(*x), 1));
102 if y.saturating_add(1) < h {
103 push(Rect::new(
104 0,
105 y.saturating_add(1),
106 w,
107 h.saturating_sub(y.saturating_add(1)),
108 ));
109 }
110 }
111 RenderOp::ClearRect { x, y, w: rw, h: rh } => push(Rect::new(*x, *y, *rw, *rh)),
112 RenderOp::FillRect {
113 x, y, w: rw, h: rh, ..
114 } => push(Rect::new(*x, *y, *rw, *rh)),
115 RenderOp::Put { x, y, text, .. } => {
116 let mw = crate::text::measure_cells_text(&self.reg, text) as u16;
117 push(Rect::new(*x, *y, mw, 1));
118 }
119 RenderOp::PutGlyph { x, y, glyph, .. } => {
120 let mw = crate::text::measure_cells_text(&self.reg, glyph) as u16;
121 push(Rect::new(*x, *y, mw, 1));
122 }
123 RenderOp::Label { x, y, w: lw, .. } => push(Rect::new(*x, *y, *lw, 1)),
124 RenderOp::LabelStyled { x, y, w: lw, .. } => push(Rect::new(*x, *y, *lw, 1)),
125 RenderOp::PutStyled { x, y, w: lw, .. } => push(Rect::new(*x, *y, *lw, 1)),
126 RenderOp::PutWrapped {
127 x, y, w: lw, text, ..
128 } => {
129 let lines = self.estimate_wrapped_lines(text, *lw);
130 push(Rect::new(*x, *y, *lw, lines));
131 }
132 RenderOp::PutWrappedStyled {
133 x,
134 y,
135 w: lw,
136 spans,
137 wrap_opts,
138 max_lines,
139 } => {
140 let lines = self.estimate_wrapped_spans_lines(spans, *lw, wrap_opts, *max_lines);
141 push(Rect::new(*x, *y, *lw, lines));
142 }
143 RenderOp::Blit {
144 x, y, w: bw, h: bh, ..
145 } => push(Rect::new(*x, *y, *bw, *bh)),
146 RenderOp::HLine { x, y, len, .. } => push(Rect::new(*x, *y, *len, 1)),
147 RenderOp::VLine { x, y, len, .. } => push(Rect::new(*x, *y, 1, *len)),
148 RenderOp::Box {
149 x, y, w: bw, h: bh, ..
150 } => push(Rect::new(*x, *y, *bw, *bh)),
151 }
152 }
153
154 fn estimate_wrapped_lines(&self, text: &str, w: u16) -> u16 {
155 if w == 0 {
156 return 0;
157 }
158 if text.is_empty() {
159 return 0;
160 }
161 let max_w = w.min(self.grid.width);
162 if max_w == 0 {
163 return 0;
164 }
165 let mut lines: u16 = 0;
166 for para in text.split(['\n', '\r']) {
167 if para.is_empty() {
168 continue;
169 }
170 for _ in wrap_text_wordwise(para, max_w, &self.reg) {
171 lines = lines.saturating_add(1);
172 if lines == u16::MAX {
173 return lines;
174 }
175 }
176 }
177 lines.max(1)
178 }
179
180 fn estimate_wrapped_spans_lines(
181 &self,
182 spans: &[Span],
183 w: u16,
184 wrap_opts: &WrapOpts,
185 max_lines: Option<u16>,
186 ) -> u16 {
187 if w == 0 {
188 return 0;
189 }
190 let max_w = w.min(self.grid.width);
191 if max_w == 0 {
192 return 0;
193 }
194 let lines = wrap_spans_wordwise(&self.reg, spans, max_w as usize, wrap_opts);
195 let mut n = lines.len() as u16;
196 if let Some(lim) = max_lines {
197 n = n.min(lim);
198 }
199 n
200 }
201
202 #[cfg(feature = "debug-validate")]
203 #[inline]
204 fn debug_validate_after(&self, op: &RenderOp) {
205 if let Err(e) = self.grid.validate_invariants(&self.reg) {
206 panic!(
209 "termgrid-core invariant violation after op_ptr={:p}: {:?}",
210 op, e
211 );
212 }
213 }
214
215 #[cfg(not(feature = "debug-validate"))]
216 #[inline]
217 fn debug_validate_after(&self, _op: &RenderOp) {
218 }
220
221 pub fn apply_op(&mut self, op: &RenderOp) {
222 match op {
223 RenderOp::Clear => self.grid.clear(),
224
225 RenderOp::ClearLine { y } => self.clear_line(*y),
226
227 RenderOp::ClearEol { x, y } => self.clear_eol(*x, *y),
228
229 RenderOp::ClearBol { x, y } => self.clear_bol(*x, *y),
230
231 RenderOp::ClearEos { x, y } => self.clear_eos(*x, *y),
232
233 RenderOp::ClearRect { x, y, w, h } => self.clear_rect(*x, *y, *w, *h),
234
235 RenderOp::Put { x, y, text, style } => {
236 let s: Style = *style;
237 self.grid.put_text(*x, *y, text, s, &self.reg);
238 }
239
240 RenderOp::PutGlyph { x, y, glyph, style } => {
241 let s: Style = *style;
242 self.grid.put_text(*x, *y, glyph, s, &self.reg);
243 }
244
245 RenderOp::Label {
246 x,
247 y,
248 w,
249 text,
250 style,
251 truncate,
252 } => {
253 self.label(*x, *y, *w, text, *style, *truncate);
254 }
255
256 RenderOp::LabelStyled {
257 x,
258 y,
259 w,
260 spans,
261 truncate,
262 } => {
263 self.put_styled(*x, *y, *w, spans, *truncate);
264 }
265
266 RenderOp::PutStyled {
267 x,
268 y,
269 w,
270 spans,
271 truncate,
272 } => {
273 self.put_styled(*x, *y, *w, spans, *truncate);
274 }
275
276 RenderOp::PutWrapped {
277 x,
278 y,
279 w,
280 text,
281 style,
282 } => {
283 self.put_wrapped(*x, *y, *w, text, *style);
284 }
285
286 RenderOp::PutWrappedStyled {
287 x,
288 y,
289 w,
290 spans,
291 wrap_opts,
292 max_lines,
293 } => {
294 self.put_wrapped_styled(*x, *y, *w, spans, wrap_opts, *max_lines);
295 }
296
297 RenderOp::Blit { x, y, w, h, cells } => {
298 self.blit(*x, *y, *w, *h, cells);
299 }
300
301 RenderOp::FillRect { x, y, w, h, style } => {
302 self.fill_rect(*x, *y, *w, *h, *style);
303 }
304
305 RenderOp::HLine {
306 x,
307 y,
308 len,
309 glyph,
310 style,
311 } => {
312 self.hline(*x, *y, *len, glyph, *style);
313 }
314
315 RenderOp::VLine {
316 x,
317 y,
318 len,
319 glyph,
320 style,
321 } => {
322 self.vline(*x, *y, *len, glyph, *style);
323 }
324
325 RenderOp::Box {
326 x,
327 y,
328 w,
329 h,
330 style,
331 charset,
332 } => {
333 self.draw_box(*x, *y, *w, *h, *style, *charset);
334 }
335 }
336 self.debug_validate_after(op);
337 }
338
339 fn clear_line(&mut self, y: u16) {
340 if y >= self.grid.height {
341 return;
342 }
343 let line = " ".repeat(self.grid.width as usize);
344 self.grid.put_text(0, y, &line, Style::plain(), &self.reg);
345 }
346
347 fn clear_eol(&mut self, x: u16, y: u16) {
348 if y >= self.grid.height || x >= self.grid.width {
349 return;
350 }
351
352 if x > 0 && matches!(self.grid.get(x, y), Some(Cell::Continuation)) {
354 self.grid.put_text(x - 1, y, " ", Style::plain(), &self.reg);
355 }
356
357 let count = (self.grid.width - x) as usize;
358 let line = " ".repeat(count);
359 self.grid.put_text(x, y, &line, Style::plain(), &self.reg);
360 }
361
362 fn clear_bol(&mut self, x: u16, y: u16) {
363 if y >= self.grid.height || x >= self.grid.width {
364 return;
365 }
366 let count = (x + 1) as usize;
367 let line = " ".repeat(count);
368 self.grid.put_text(0, y, &line, Style::plain(), &self.reg);
369 }
370
371 fn clear_eos(&mut self, x: u16, y: u16) {
372 if y >= self.grid.height || x >= self.grid.width {
373 return;
374 }
375 self.clear_eol(x, y);
376 for yy in (y + 1)..self.grid.height {
377 self.clear_line(yy);
378 }
379 }
380
381 fn clear_rect(&mut self, x: u16, y: u16, w: u16, h: u16) {
382 self.fill_rect(x, y, w, h, Style::plain());
383 }
384
385 fn label(&mut self, x: u16, y: u16, w: u16, text: &str, style: Style, truncate: TruncateMode) {
386 if w == 0 {
387 return;
388 }
389 if x >= self.grid.width || y >= self.grid.height {
390 return;
391 }
392 let max_w = w.min(self.grid.width - x);
393 if max_w == 0 {
394 return;
395 }
396
397 let line = text.split(['\n', '\r']).next().unwrap_or("");
399
400 let rendered = match truncate {
401 TruncateMode::Clip => clip_to_cells_text(&self.reg, line, max_w as usize).0,
402 TruncateMode::Ellipsis => {
403 ellipsis_to_cells_text(&self.reg, line, max_w as usize, DEFAULT_ELLIPSIS)
404 }
405 };
406
407 let spans = [Span::new(rendered, style)];
408 self.put_spans_line(x, y, max_w, &spans);
409 }
410
411 fn put_wrapped(&mut self, x: u16, y: u16, w: u16, text: &str, style: Style) {
412 if w == 0 {
413 return;
414 }
415 if x >= self.grid.width || y >= self.grid.height {
416 return;
417 }
418 let max_w = w.min(self.grid.width - x);
419 if max_w == 0 {
420 return;
421 }
422
423 let mut cy = y;
424 for para in text.split(['\n', '\r']) {
425 if cy >= self.grid.height {
426 break;
427 }
428 if para.is_empty() {
430 cy = cy.saturating_add(1);
431 continue;
432 }
433
434 for line in wrap_text_wordwise(para, max_w, &self.reg) {
435 if cy >= self.grid.height {
436 break;
437 }
438 let spans = [Span::new(line, style)];
439 self.put_spans_line(x, cy, max_w, &spans);
440 cy = cy.saturating_add(1);
441 }
442 }
446 }
447
448 fn put_wrapped_styled(
449 &mut self,
450 x: u16,
451 y: u16,
452 w: u16,
453 spans: &[Span],
454 wrap_opts: &WrapOpts,
455 max_lines: Option<u16>,
456 ) {
457 if w == 0 {
458 return;
459 }
460 if x >= self.grid.width || y >= self.grid.height {
461 return;
462 }
463 let max_w = w.min(self.grid.width - x);
464 if max_w == 0 {
465 return;
466 }
467
468 let lines = wrap_spans_wordwise(&self.reg, spans, max_w as usize, wrap_opts);
469 let limit = max_lines.map(|v| v as usize);
470
471 let mut cy = y;
472 for (rendered, line_spans) in lines.into_iter().enumerate() {
473 if cy >= self.grid.height {
474 break;
475 }
476 if let Some(lim) = limit {
477 if rendered >= lim {
478 break;
479 }
480 }
481 self.put_spans_line(x, cy, max_w, &line_spans);
482 cy = cy.saturating_add(1);
483 }
484 }
485
486 fn put_styled(&mut self, x: u16, y: u16, w: u16, spans: &[Span], truncate: TruncateMode) {
487 if w == 0 {
488 return;
489 }
490 if x >= self.grid.width || y >= self.grid.height {
491 return;
492 }
493 let max_w = w.min(self.grid.width - x);
494 if max_w == 0 {
495 return;
496 }
497
498 let spans = normalize_spans(spans);
499
500 let rendered_spans = match truncate {
501 TruncateMode::Clip => {
502 let (s, _clipped) = clip_to_cells_spans(&self.reg, &spans, max_w as usize);
503 s
504 }
505 TruncateMode::Ellipsis => {
506 let ell = Span::new(DEFAULT_ELLIPSIS, Style::plain());
510 ellipsis_to_cells_spans(&self.reg, &spans, max_w as usize, &ell)
511 }
512 };
513
514 self.put_spans_line(x, y, max_w, &rendered_spans);
515 }
516
517 fn put_spans_line(&mut self, x: u16, y: u16, max_w: u16, spans: &[Span]) {
518 if max_w == 0 {
519 return;
520 }
521 let limit_x = x.saturating_add(max_w);
522
523 let mut cx = x;
524 for s in spans {
525 for g in s.text.graphemes(true) {
526 if cx >= limit_x {
528 return;
529 }
530 if cx >= self.grid.width {
531 return;
532 }
533 let gw = self.reg.width(g);
534 if gw == 0 {
535 continue;
536 }
537 if gw == 2 {
539 if cx + 1 >= self.grid.width {
540 return;
541 }
542 if cx + 1 >= limit_x {
543 return;
544 }
545 }
546 self.grid.put_text(cx, y, g, s.style, &self.reg);
547 cx = cx.saturating_add(gw as u16);
548 }
549 }
550 }
551
552 fn blit(&mut self, x: u16, y: u16, w: u16, h: u16, cells: &[Option<BlitCell>]) {
553 if w == 0 || h == 0 {
554 return;
555 }
556 if x >= self.grid.width || y >= self.grid.height {
557 return;
558 }
559
560 let max_w = w.min(self.grid.width.saturating_sub(x));
561 let max_h = h.min(self.grid.height.saturating_sub(y));
562 if max_w == 0 || max_h == 0 {
563 return;
564 }
565
566 let src_w = w as usize;
567
568 for sy in 0..max_h {
569 let ty = y.saturating_add(sy);
570 let mut sx: u16 = 0;
571
572 while sx < max_w {
573 let idx = sy as usize * src_w + sx as usize;
574 if idx >= cells.len() {
575 break;
576 }
577
578 let tx = x.saturating_add(sx);
579 if tx >= self.grid.width {
580 break;
581 }
582
583 if let Some(cell) = &cells[idx] {
584 let style = cell.style;
585 let glyph = cell.glyph.as_str();
586 self.grid.put_text(tx, ty, glyph, style, &self.reg);
587
588 let gw = self.reg.width(glyph) as u16;
589 if gw == 2 {
590 sx = sx.saturating_add(2);
592 continue;
593 }
594 }
595 sx = sx.saturating_add(1);
596 }
597 }
598 }
599
600 fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, style: Style) {
601 if w == 0 || h == 0 {
602 return;
603 }
604 if x >= self.grid.width || y >= self.grid.height {
605 return;
606 }
607
608 let max_w = self.grid.width.saturating_sub(x);
609 let max_h = self.grid.height.saturating_sub(y);
610 let w = w.min(max_w);
611 let h = h.min(max_h);
612 if w == 0 || h == 0 {
613 return;
614 }
615
616 let line = " ".repeat(w as usize);
617 for dy in 0..h {
618 let yy = y.saturating_add(dy);
619 self.grid.put_text(x, yy, &line, style, &self.reg);
620 }
621 }
622
623 fn hline(&mut self, x: u16, y: u16, len_cells: u16, glyph: &str, style: Style) {
624 if len_cells == 0 {
625 return;
626 }
627 if y >= self.grid.height {
628 return;
629 }
630 if x >= self.grid.width {
631 return;
632 }
633
634 let gw = self.reg.width(glyph) as u16;
635 let gw = gw.max(1);
636
637 let mut cx = x;
638 let mut remaining = len_cells;
639 while remaining > 0 && cx < self.grid.width {
640 if gw == 2 {
641 if remaining < 2 {
642 break;
643 }
644 if cx + 1 >= self.grid.width {
645 break;
646 }
647 self.grid.put_text(cx, y, glyph, style, &self.reg);
648 cx = cx.saturating_add(2);
649 remaining = remaining.saturating_sub(2);
650 } else {
651 self.grid.put_text(cx, y, glyph, style, &self.reg);
652 cx = cx.saturating_add(1);
653 remaining = remaining.saturating_sub(1);
654 }
655 }
656 }
657
658 fn vline(&mut self, x: u16, y: u16, len_rows: u16, glyph: &str, style: Style) {
659 if len_rows == 0 {
660 return;
661 }
662 if x >= self.grid.width {
663 return;
664 }
665 if y >= self.grid.height {
666 return;
667 }
668
669 if self.reg.width(glyph) == 2 && x + 1 >= self.grid.width {
671 return;
672 }
673
674 let max_len = self.grid.height.saturating_sub(y);
675 let len = len_rows.min(max_len);
676 for dy in 0..len {
677 let yy = y.saturating_add(dy);
678 self.grid.put_text(x, yy, glyph, style, &self.reg);
679 }
680 }
681
682 fn draw_box(&mut self, x: u16, y: u16, w: u16, h: u16, style: Style, charset: BoxCharset) {
683 if w < 2 || h < 2 {
684 return;
685 }
686 if x >= self.grid.width || y >= self.grid.height {
687 return;
688 }
689
690 let x1 = x.saturating_add(w - 1).min(self.grid.width - 1);
691 let y1 = y.saturating_add(h - 1).min(self.grid.height - 1);
692
693 if x1 <= x || y1 <= y {
694 return;
695 }
696
697 let (tl, tr, bl, br, hz, vt) = match charset {
698 BoxCharset::Ascii => ("+", "+", "+", "+", "-", "|"),
699 BoxCharset::UnicodeSingle => ("┌", "┐", "└", "┘", "─", "│"),
700 BoxCharset::UnicodeDouble => ("╔", "╗", "╚", "╝", "═", "║"),
701 };
702
703 self.grid.put_text(x, y, tl, style, &self.reg);
705 self.grid.put_text(x1, y, tr, style, &self.reg);
706 self.grid.put_text(x, y1, bl, style, &self.reg);
707 self.grid.put_text(x1, y1, br, style, &self.reg);
708
709 if x1 > x + 1 {
711 let count = (x1 - x - 1) as usize;
712 let line = hz.repeat(count);
713 self.grid.put_text(x + 1, y, &line, style, &self.reg);
714 self.grid.put_text(x + 1, y1, &line, style, &self.reg);
715 }
716
717 if y1 > y + 1 {
719 for yy in (y + 1)..y1 {
720 self.grid.put_text(x, yy, vt, style, &self.reg);
721 self.grid.put_text(x1, yy, vt, style, &self.reg);
722 }
723 }
724 }
725
726 pub fn to_ansi(&self) -> String {
728 ansi::grid_to_ansi(&self.grid)
729 }
730}
731
732fn clip_to_cells(text: &str, max_cells: u16, reg: &GlyphRegistry) -> String {
733 if max_cells == 0 {
734 return String::new();
735 }
736 let mut out = String::new();
737 let mut used: u16 = 0;
738 for g in UnicodeSegmentation::graphemes(text, true) {
739 let gw = reg.width(g) as u16;
740 if used + gw > max_cells {
741 break;
742 }
743 if gw == 2 && used + 1 == max_cells {
745 break;
746 }
747 out.push_str(g);
748 used += gw.max(1);
749 if used >= max_cells {
750 break;
751 }
752 }
753 out
754}
755
756fn visible_width_cells(text: &str, reg: &GlyphRegistry) -> u16 {
757 let mut w: u16 = 0;
758 for g in UnicodeSegmentation::graphemes(text, true) {
759 if g == "\n" || g == "\r" {
760 break;
761 }
762 w = w.saturating_add(reg.width(g) as u16);
763 }
764 w
765}
766
767fn wrap_text_wordwise(text: &str, max_cells: u16, reg: &GlyphRegistry) -> Vec<String> {
768 #[derive(Clone)]
770 struct Tok {
771 s: String,
772 is_space: bool,
773 }
774
775 let mut toks: Vec<Tok> = Vec::new();
776 let mut cur = String::new();
777 let mut cur_is_space: Option<bool> = None;
778
779 for g in UnicodeSegmentation::graphemes(text, true) {
780 let is_space = g.chars().all(|c| c.is_whitespace());
781 match cur_is_space {
782 None => {
783 cur_is_space = Some(is_space);
784 cur.push_str(g);
785 }
786 Some(same) if same == is_space => {
787 cur.push_str(g);
788 }
789 Some(_) => {
790 toks.push(Tok {
791 s: cur.clone(),
792 is_space: cur_is_space.unwrap(),
793 });
794 cur.clear();
795 cur_is_space = Some(is_space);
796 cur.push_str(g);
797 }
798 }
799 }
800 if !cur.is_empty() {
801 toks.push(Tok {
802 s: cur,
803 is_space: cur_is_space.unwrap_or(false),
804 });
805 }
806
807 let mut lines: Vec<String> = Vec::new();
808 let mut line = String::new();
809 let mut used: u16 = 0;
810
811 fn push_line(lines: &mut Vec<String>, line: &mut String, used: &mut u16) {
812 lines.push(std::mem::take(line));
813 *used = 0;
814 }
815
816 let mut i = 0;
817 while i < toks.len() {
818 let tok = toks[i].clone();
819 if tok.is_space {
820 if used == 0 {
822 i += 1;
823 continue;
824 }
825 let s = " ";
827 let sw = reg.width(s) as u16;
828
829 let mut next_word_w: Option<u16> = None;
833 for tok in toks.iter().skip(i + 1) {
834 if !tok.is_space {
835 next_word_w = Some(visible_width_cells(&tok.s, reg));
836 break;
837 }
838 }
839
840 if used + sw > max_cells {
841 push_line(&mut lines, &mut line, &mut used);
842 } else if let Some(ww) = next_word_w {
843 if used + sw + ww > max_cells {
844 push_line(&mut lines, &mut line, &mut used);
846 } else {
847 line.push_str(s);
848 used += sw.max(1);
849 }
850 } else {
851 }
853 i += 1;
854 continue;
855 }
856
857 let word = tok.s;
858 let ww = visible_width_cells(&word, reg);
859 if ww <= max_cells {
860 if used == 0 {
861 line.push_str(&word);
862 used = ww;
863 } else if used + ww <= max_cells {
864 line.push_str(&word);
865 used += ww;
866 } else {
867 push_line(&mut lines, &mut line, &mut used);
868 line.push_str(&word);
869 used = ww;
870 }
871 i += 1;
872 continue;
873 }
874
875 let mut remainder = word.as_str();
877 loop {
878 if remainder.is_empty() {
879 break;
880 }
881 if used != 0 {
882 push_line(&mut lines, &mut line, &mut used);
883 }
884 let part = clip_to_cells(remainder, max_cells, reg);
885 if part.is_empty() {
886 break;
887 }
888 line.push_str(&part);
889 used = visible_width_cells(&part, reg);
890 push_line(&mut lines, &mut line, &mut used);
891 remainder = &remainder[part.len()..];
892 }
893 i += 1;
894 }
895
896 if !line.is_empty() {
897 lines.push(line);
898 }
899 if lines.is_empty() {
900 lines.push(String::new());
901 }
902 lines
903}