1use std::cmp::min;
2use std::fmt;
3use std::usize;
4
5use unicode_segmentation::UnicodeSegmentation;
6use unicode_width::UnicodeWidthStr;
7
8use crate::layout::Rect;
9use crate::style::{Color, Modifier, Style};
10
11#[derive(Debug, Clone, PartialEq)]
13pub struct Cell {
14 pub symbol: String,
15 pub style: Style,
16}
17
18impl Cell {
19 pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
20 self.symbol.clear();
21 self.symbol.push_str(symbol);
22 self
23 }
24
25 pub fn set_char(&mut self, ch: char) -> &mut Cell {
26 self.symbol.clear();
27 self.symbol.push(ch);
28 self
29 }
30
31 pub fn set_fg(&mut self, color: Color) -> &mut Cell {
32 self.style.fg = color;
33 self
34 }
35
36 pub fn set_bg(&mut self, color: Color) -> &mut Cell {
37 self.style.bg = color;
38 self
39 }
40
41 pub fn set_modifier(&mut self, modifier: Modifier) -> &mut Cell {
42 self.style.modifier = modifier;
43 self
44 }
45
46 pub fn set_style(&mut self, style: Style) -> &mut Cell {
47 self.style = style;
48 self
49 }
50
51 pub fn reset(&mut self) {
52 self.symbol.clear();
53 self.symbol.push(' ');
54 self.style.reset();
55 }
56}
57
58impl Default for Cell {
59 fn default() -> Cell {
60 Cell {
61 symbol: " ".into(),
62 style: Default::default(),
63 }
64 }
65}
66
67#[derive(Clone, PartialEq)]
96pub struct Buffer {
97 pub area: Rect,
99 pub content: Vec<Cell>,
102}
103
104impl Default for Buffer {
105 fn default() -> Buffer {
106 Buffer {
107 area: Default::default(),
108 content: Vec::new(),
109 }
110 }
111}
112
113impl fmt::Debug for Buffer {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 writeln!(f, "Buffer: {:?}", self.area)?;
116 f.write_str("Content (quoted lines):\n")?;
117 for cells in self.content.chunks(self.area.width as usize) {
118 let mut line = String::new();
119 let mut overwritten = vec![];
120 let mut skip: usize = 0;
121 for (x, c) in cells.iter().enumerate() {
122 if skip == 0 {
123 line.push_str(&c.symbol);
124 } else {
125 overwritten.push((x, &c.symbol))
126 }
127 skip = std::cmp::max(skip, c.symbol.width()).saturating_sub(1);
128 }
129 f.write_fmt(format_args!("{:?},", line))?;
130 if !overwritten.is_empty() {
131 f.write_fmt(format_args!(
132 " Hidden by multi-width symbols: {:?}",
133 overwritten
134 ))?;
135 }
136 f.write_str("\n")?;
137 }
138 f.write_str("Style:\n")?;
139 for cells in self.content.chunks(self.area.width as usize) {
140 f.write_str("|")?;
141 for cell in cells {
142 write!(
143 f,
144 "{} {} {}|",
145 cell.style.fg.code(),
146 cell.style.bg.code(),
147 cell.style.modifier.code()
148 )?;
149 }
150 f.write_str("\n")?;
151 }
152 Ok(())
153 }
154}
155
156impl Buffer {
157 pub fn empty(area: Rect) -> Buffer {
159 let cell: Cell = Default::default();
160 Buffer::filled(area, &cell)
161 }
162
163 pub fn filled(area: Rect, cell: &Cell) -> Buffer {
165 let size = area.area() as usize;
166 let content = vec![cell.clone(); size];
167 Buffer { area, content }
168 }
169
170 pub fn with_lines<S>(lines: Vec<S>) -> Buffer
172 where
173 S: AsRef<str>,
174 {
175 let height = lines.len() as u16;
176 let width = lines.iter().fold(0, |acc, item| {
177 std::cmp::max(acc, item.as_ref().width() as u16)
178 });
179 let mut buffer = Buffer::empty(Rect {
180 x: 0,
181 y: 0,
182 width,
183 height,
184 });
185 for (y, line) in lines.iter().enumerate() {
186 buffer.set_string(0, y as u16, line, Style::default());
187 }
188 buffer
189 }
190
191 pub fn content(&self) -> &[Cell] {
193 &self.content
194 }
195
196 pub fn area(&self) -> &Rect {
198 &self.area
199 }
200
201 pub fn get(&self, x: u16, y: u16) -> &Cell {
203 let i = self.index_of(x, y);
204 &self.content[i]
205 }
206
207 pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
209 let i = self.index_of(x, y);
210 &mut self.content[i]
211 }
212
213 pub fn index_of(&self, x: u16, y: u16) -> usize {
242 debug_assert!(
243 x >= self.area.left()
244 && x < self.area.right()
245 && y >= self.area.top()
246 && y < self.area.bottom(),
247 "Trying to access position outside the buffer: x={}, y={}, area={:?}",
248 x,
249 y,
250 self.area
251 );
252 ((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
253 }
254
255 pub fn pos_of(&self, i: usize) -> (u16, u16) {
283 debug_assert!(
284 i < self.content.len(),
285 "Trying to get the coords of a cell outside the buffer: i={} len={}",
286 i,
287 self.content.len()
288 );
289 (
290 self.area.x + i as u16 % self.area.width,
291 self.area.y + i as u16 / self.area.width,
292 )
293 }
294
295 pub fn set_string<S>(&mut self, x: u16, y: u16, string: S, style: Style)
297 where
298 S: AsRef<str>,
299 {
300 self.set_stringn(x, y, string, usize::MAX, style);
301 }
302
303 pub fn set_stringn<S>(
306 &mut self,
307 x: u16,
308 y: u16,
309 string: S,
310 width: usize,
311 style: Style,
312 ) -> (u16, u16)
313 where
314 S: AsRef<str>,
315 {
316 let mut index = self.index_of(x, y);
317 let mut x_offset = x as usize;
318 let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true);
319 let max_offset = min(self.area.right() as usize, width.saturating_add(x as usize));
320 for s in graphemes {
321 let width = s.width();
322 if width > max_offset.saturating_sub(x_offset) {
325 break;
326 }
327
328 self.content[index].set_symbol(s);
329 self.content[index].set_style(style);
330 for i in index + 1..index + width {
332 self.content[i].reset();
333 }
334 index += width;
335 x_offset += width;
336 }
337 (x_offset as u16, y)
338 }
339
340 pub fn set_background(&mut self, area: Rect, color: Color) {
341 for y in area.top()..area.bottom() {
342 for x in area.left()..area.right() {
343 self.get_mut(x, y).set_bg(color);
344 }
345 }
346 }
347
348 pub fn resize(&mut self, area: Rect) {
351 let length = area.area() as usize;
352 if self.content.len() > length {
353 self.content.truncate(length);
354 } else {
355 self.content.resize(length, Default::default());
356 }
357 self.area = area;
358 }
359
360 pub fn reset(&mut self) {
362 for c in &mut self.content {
363 c.reset();
364 }
365 }
366
367 pub fn merge(&mut self, other: &Buffer) {
369 let area = self.area.union(other.area);
370 let cell: Cell = Default::default();
371 self.content.resize(area.area() as usize, cell.clone());
372
373 let size = self.area.area() as usize;
375 for i in (0..size).rev() {
376 let (x, y) = self.pos_of(i);
377 let k = ((y - area.y) * area.width + x - area.x) as usize;
379 if i != k {
380 self.content[k] = self.content[i].clone();
381 self.content[i] = cell.clone();
382 }
383 }
384
385 let size = other.area.area() as usize;
388 for i in 0..size {
389 let (x, y) = other.pos_of(i);
390 let k = ((y - area.y) * area.width + x - area.x) as usize;
392 self.content[k] = other.content[i].clone();
393 }
394 self.area = area;
395 }
396
397 pub fn diff<'a>(&self, other: &'a Buffer) -> Vec<(u16, u16, &'a Cell)> {
426 let previous_buffer = &self.content;
427 let next_buffer = &other.content;
428 let width = self.area.width;
429
430 let mut updates: Vec<(u16, u16, &Cell)> = vec![];
431 let mut invalidated: usize = 0;
433 let mut to_skip: usize = 0;
436 for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() {
437 if (current != previous || invalidated > 0) && to_skip == 0 {
438 let x = i as u16 % width;
439 let y = i as u16 / width;
440 updates.push((x, y, &next_buffer[i]));
441 }
442
443 to_skip = current.symbol.width().saturating_sub(1);
444
445 let affected_width = std::cmp::max(current.symbol.width(), previous.symbol.width());
446 invalidated = std::cmp::max(affected_width, invalidated).saturating_sub(1);
447 }
448 updates
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455
456 fn cell(s: &str) -> Cell {
457 let mut cell = Cell::default();
458 cell.set_symbol(s);
459 cell
460 }
461
462 #[test]
463 fn it_translates_to_and_from_coordinates() {
464 let rect = Rect::new(200, 100, 50, 80);
465 let buf = Buffer::empty(rect);
466
467 assert_eq!(buf.pos_of(0), (200, 100));
469 assert_eq!(buf.index_of(200, 100), 0);
470
471 assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
473 assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
474 }
475
476 #[test]
477 #[should_panic(expected = "outside the buffer")]
478 fn pos_of_panics_on_out_of_bounds() {
479 let rect = Rect::new(0, 0, 10, 10);
480 let buf = Buffer::empty(rect);
481
482 buf.pos_of(100);
484 }
485
486 #[test]
487 #[should_panic(expected = "outside the buffer")]
488 fn index_of_panics_on_out_of_bounds() {
489 let rect = Rect::new(0, 0, 10, 10);
490 let buf = Buffer::empty(rect);
491
492 buf.index_of(10, 0);
494 }
495
496 #[test]
497 fn buffer_set_string() {
498 let area = Rect::new(0, 0, 5, 1);
499 let mut buffer = Buffer::empty(area);
500
501 buffer.set_stringn(0, 0, "aaa", 0, Style::default());
503 assert_eq!(buffer, Buffer::with_lines(vec![" "]));
504
505 buffer.set_string(0, 0, "aaa", Style::default());
506 assert_eq!(buffer, Buffer::with_lines(vec!["aaa "]));
507
508 buffer.set_stringn(0, 0, "bbbbbbbbbbbbbb", 4, Style::default());
510 assert_eq!(buffer, Buffer::with_lines(vec!["bbbb "]));
511
512 buffer.set_string(0, 0, "12345", Style::default());
513 assert_eq!(buffer, Buffer::with_lines(vec!["12345"]));
514
515 buffer.set_string(0, 0, "123456", Style::default());
517 assert_eq!(buffer, Buffer::with_lines(vec!["12345"]));
518 }
519
520 #[test]
521 fn buffer_set_string_double_width() {
522 let area = Rect::new(0, 0, 5, 1);
523 let mut buffer = Buffer::empty(area);
524 buffer.set_string(0, 0, "コン", Style::default());
525 assert_eq!(buffer, Buffer::with_lines(vec!["コン "]));
526
527 buffer.set_string(0, 0, "コンピ", Style::default());
529 assert_eq!(buffer, Buffer::with_lines(vec!["コン "]));
530 }
531
532 #[test]
533 fn buffer_with_lines() {
534 let buffer =
535 Buffer::with_lines(vec!["┌────────┐", "│コンピュ│", "│ーa 上で│", "└────────┘"]);
536 assert_eq!(buffer.area.x, 0);
537 assert_eq!(buffer.area.y, 0);
538 assert_eq!(buffer.area.width, 10);
539 assert_eq!(buffer.area.height, 4);
540 }
541
542 #[test]
543 fn buffer_diffing_empty_empty() {
544 let area = Rect::new(0, 0, 40, 40);
545 let prev = Buffer::empty(area);
546 let next = Buffer::empty(area);
547 let diff = prev.diff(&next);
548 assert_eq!(diff, vec![]);
549 }
550
551 #[test]
552 fn buffer_diffing_empty_filled() {
553 let area = Rect::new(0, 0, 40, 40);
554 let prev = Buffer::empty(area);
555 let next = Buffer::filled(area, Cell::default().set_symbol("a"));
556 let diff = prev.diff(&next);
557 assert_eq!(diff.len(), 40 * 40);
558 }
559
560 #[test]
561 fn buffer_diffing_filled_filled() {
562 let area = Rect::new(0, 0, 40, 40);
563 let prev = Buffer::filled(area, Cell::default().set_symbol("a"));
564 let next = Buffer::filled(area, Cell::default().set_symbol("a"));
565 let diff = prev.diff(&next);
566 assert_eq!(diff, vec![]);
567 }
568
569 #[test]
570 fn buffer_diffing_single_width() {
571 let prev = Buffer::with_lines(vec![
572 " ",
573 "┌Title─┐ ",
574 "│ │ ",
575 "│ │ ",
576 "└──────┘ ",
577 ]);
578 let next = Buffer::with_lines(vec![
579 " ",
580 "┌TITLE─┐ ",
581 "│ │ ",
582 "│ │ ",
583 "└──────┘ ",
584 ]);
585 let diff = prev.diff(&next);
586 assert_eq!(
587 diff,
588 vec![
589 (2, 1, &cell("I")),
590 (3, 1, &cell("T")),
591 (4, 1, &cell("L")),
592 (5, 1, &cell("E")),
593 ]
594 );
595 }
596
597 #[test]
598 #[rustfmt::skip]
599 fn buffer_diffing_multi_width() {
600 let prev = Buffer::with_lines(vec![
601 "┌Title─┐ ",
602 "└──────┘ ",
603 ]);
604 let next = Buffer::with_lines(vec![
605 "┌称号──┐ ",
606 "└──────┘ ",
607 ]);
608 let diff = prev.diff(&next);
609 assert_eq!(
610 diff,
611 vec![
612 (1, 0, &cell("称")),
613 (3, 0, &cell("号")),
615 (5, 0, &cell("─")),
617 ]
618 );
619 }
620
621 #[test]
622 fn buffer_diffing_multi_width_offset() {
623 let prev = Buffer::with_lines(vec!["┌称号──┐"]);
624 let next = Buffer::with_lines(vec!["┌─称号─┐"]);
625
626 let diff = prev.diff(&next);
627 assert_eq!(
628 diff,
629 vec![(1, 0, &cell("─")), (2, 0, &cell("称")), (4, 0, &cell("号")),]
630 );
631 }
632
633 #[test]
634 fn buffer_merge() {
635 let mut one = Buffer::filled(
636 Rect {
637 x: 0,
638 y: 0,
639 width: 2,
640 height: 2,
641 },
642 Cell::default().set_symbol("1"),
643 );
644 let two = Buffer::filled(
645 Rect {
646 x: 0,
647 y: 2,
648 width: 2,
649 height: 2,
650 },
651 Cell::default().set_symbol("2"),
652 );
653 one.merge(&two);
654 assert_eq!(one, Buffer::with_lines(vec!["11", "11", "22", "22"]));
655 }
656
657 #[test]
658 fn buffer_merge2() {
659 let mut one = Buffer::filled(
660 Rect {
661 x: 2,
662 y: 2,
663 width: 2,
664 height: 2,
665 },
666 Cell::default().set_symbol("1"),
667 );
668 let two = Buffer::filled(
669 Rect {
670 x: 0,
671 y: 0,
672 width: 2,
673 height: 2,
674 },
675 Cell::default().set_symbol("2"),
676 );
677 one.merge(&two);
678 assert_eq!(
679 one,
680 Buffer::with_lines(vec!["22 ", "22 ", " 11", " 11"])
681 );
682 }
683
684 #[test]
685 fn buffer_merge3() {
686 let mut one = Buffer::filled(
687 Rect {
688 x: 3,
689 y: 3,
690 width: 2,
691 height: 2,
692 },
693 Cell::default().set_symbol("1"),
694 );
695 let two = Buffer::filled(
696 Rect {
697 x: 1,
698 y: 1,
699 width: 3,
700 height: 4,
701 },
702 Cell::default().set_symbol("2"),
703 );
704 one.merge(&two);
705 let mut merged = Buffer::with_lines(vec!["222 ", "222 ", "2221", "2221"]);
706 merged.area = Rect {
707 x: 1,
708 y: 1,
709 width: 4,
710 height: 4,
711 };
712 assert_eq!(one, merged);
713 }
714}