1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
use {
crossterm::{
QueueableCommand,
style::Print,
},
termimad::{
CompoundStyle,
Result,
},
unicode_width::{UnicodeWidthChar, UnicodeWidthStr},
};
static TAB_REPLACEMENT: &str = " ";
/// wrap a writer to ensure that at most `allowed` chars are
/// written.
/// Note: tab replacement managment is only half designed/coded
pub struct CropWriter<'w, W>
where
W: std::io::Write,
{
pub w: &'w mut W,
pub allowed: usize,
}
impl<'w, W> CropWriter<'w, W>
where
W: std::io::Write,
{
pub fn new(w: &'w mut W, limit: usize) -> Self {
Self { w, allowed: limit }
}
pub fn is_full(&self) -> bool {
self.allowed == 0
}
pub fn cropped_str(&self, s: &str) -> (String, usize) {
let mut string = s.replace('\t', TAB_REPLACEMENT);
let mut len = UnicodeWidthStr::width(&*string);
if len > self.allowed {
len = 0;
let mut ns = String::new();
for c in string.chars() {
let char_width = UnicodeWidthChar::width(c).unwrap_or(0);
if char_width + len > self.allowed {
break;
}
ns.push(c);
len += char_width;
}
string = ns
}
(string, len)
}
pub fn queue_unstyled_str(&mut self, s: &str) -> Result<()> {
if self.is_full() {
return Ok(());
}
let (string, len) = self.cropped_str(s);
self.allowed -= len;
self.w.queue(Print(string))?;
Ok(())
}
pub fn queue_str(&mut self, cs: &CompoundStyle, s: &str) -> Result<()> {
if self.is_full() {
return Ok(());
}
let (string, len) = self.cropped_str(s);
self.allowed -= len;
cs.queue(self.w, string)
}
pub fn queue_char(&mut self, cs: &CompoundStyle, c: char) -> Result<()> {
let width = UnicodeWidthChar::width(c).unwrap_or(0);
if width < self.allowed {
self.allowed -= width;
cs.queue(self.w, c)?;
}
Ok(())
}
pub fn queue_unstyled_char(&mut self, c: char) -> Result<()> {
if c == '\t' {
return self.queue_unstyled_str(TAB_REPLACEMENT);
}
let width = UnicodeWidthChar::width(c).unwrap_or(0);
if width < self.allowed {
self.allowed -= width;
self.w.queue(Print(c))?;
}
Ok(())
}
/// a "g_string" is a "gentle" one: each char takes one column on screen.
/// This function must thus not be used for unknown strings.
pub fn queue_g_string(&mut self, cs: &CompoundStyle, mut s: String) -> Result<()> {
if self.is_full() {
return Ok(());
}
let mut len = 0;
for (idx, _) in s.char_indices() {
len += 1;
if len > self.allowed {
s.truncate(idx);
self.allowed = 0;
return cs.queue(self.w, s)
}
}
self.allowed -= len;
cs.queue(self.w, s)
}
pub fn queue_bg(&mut self, cs: &CompoundStyle) -> Result<()> {
cs.queue_bg(self.w)
}
pub fn fill(&mut self, cs: &CompoundStyle, filling: &'static str) -> Result<()> {
self.repeat(cs, filling, self.allowed)
}
pub fn repeat(&mut self, cs: &CompoundStyle, filling: &'static str, mut len: usize) -> Result<()> {
loop {
let slice_len = len.min(self.allowed).min(filling.len());
if slice_len == 0 {
break;
}
cs.queue_str(self.w, &filling[0..slice_len])?;
self.allowed -= slice_len;
len -= slice_len;
}
Ok(())
}
}
|