broot/display/
matched_string.rs1use {
2 super::{
3 CropWriter,
4 SPACE_FILLING,
5 },
6 crate::pattern::NameMatch,
7 termimad::{
8 CompoundStyle,
9 StrFit,
10 minimad::Alignment,
11 },
12 unicode_width::{
13 UnicodeWidthChar,
14 UnicodeWidthStr,
15 },
16};
17
18pub struct MatchedString<'a> {
19 pub name_match: Option<NameMatch>,
20 pub string: &'a str,
21 pub base_style: &'a CompoundStyle,
22 pub match_style: &'a CompoundStyle,
23 pub display_width: Option<usize>,
24 pub align: Alignment,
25}
26
27impl<'a> MatchedString<'a> {
28 pub fn new(
29 name_match: Option<NameMatch>,
30 string: &'a str,
31 base_style: &'a CompoundStyle,
32 match_style: &'a CompoundStyle,
33 ) -> Self {
34 Self {
35 name_match,
36 string,
37 base_style,
38 match_style,
39 display_width: None,
40 align: Alignment::Left,
41 }
42 }
43 pub fn split_on_last(
48 &mut self,
49 sep: char,
50 ) -> Option<Self> {
51 if self.display_width.is_some() {
52 None
54 } else {
55 self.string.rfind(sep).map(|sep_idx| {
56 let right = &self.string[sep_idx + 1..];
57 self.string = &self.string[..=sep_idx];
58 let left_chars_count = self.string.chars().count();
59 let right_name_match = self
60 .name_match
61 .as_mut()
62 .map(|nm| nm.cut_after(left_chars_count));
63 MatchedString {
64 name_match: right_name_match,
65 string: right,
66 base_style: self.base_style,
67 match_style: self.match_style,
68 display_width: None,
69 align: self.align,
70 }
71 })
72 }
73 }
74 pub fn fill(
75 &mut self,
76 width: usize,
77 align: Alignment,
78 ) {
79 self.display_width = Some(width);
80 self.align = align;
81 }
82 pub fn width(&self) -> usize {
83 UnicodeWidthStr::width(self.string)
84 }
85 pub fn cut_left_to_fit(
88 &mut self,
89 max_width: usize,
90 ) -> usize {
91 let mut removed_char_count = 0;
92 let mut break_idx = 0;
93 let mut width = self.width();
94 for (idx, c) in self.string.char_indices() {
95 if width <= max_width {
96 break;
97 }
98 break_idx = idx + c.len_utf8();
99 let char_width = c.width().unwrap_or(0);
100 if char_width > width {
101 warn!("inconsistent char/str widths");
102 break;
103 }
104 width -= char_width;
105 removed_char_count += 1;
106 }
107 if removed_char_count > 0 {
108 self.string = &self.string[break_idx..];
109 self.name_match = self
110 .name_match
111 .take()
112 .map(|mut nm| nm.cut_after(removed_char_count - 1));
113 }
114 removed_char_count
115 }
116 pub fn queue_on<W>(
117 &self,
118 cw: &mut CropWriter<'_, W>,
119 ) -> Result<(), termimad::Error>
120 where
121 W: std::io::Write,
122 {
123 if let Some(m) = &self.name_match {
124 let mut pos_idx: usize = 0;
125 let mut combined_style = self.base_style.clone();
126 combined_style.overwrite_with(self.match_style);
127 let mut right_filling = 0;
128 let mut s = self.string;
129 if let Some(dw) = self.display_width {
130 let w = unicode_width::UnicodeWidthStr::width(s);
131 #[allow(clippy::comparison_chain)]
132 if w > dw {
133 let (count_bytes, _) = StrFit::count_fitting(s, dw);
134 s = &s[0..count_bytes];
135 } else if w < dw {
136 match self.align {
137 Alignment::Right => {
138 cw.repeat(self.base_style, &SPACE_FILLING, dw - w)?;
139 }
140 Alignment::Center => {
141 right_filling = (dw - w) / 2;
142 cw.repeat(self.base_style, &SPACE_FILLING, dw - w - right_filling)?;
143 }
144 _ => {
145 right_filling = dw - w;
146 }
147 }
148 }
149 }
150 for (cand_idx, cand_char) in s.chars().enumerate() {
153 if pos_idx < m.pos.len() && m.pos[pos_idx] == cand_idx {
154 cw.queue_char(&combined_style, cand_char)?;
155 pos_idx += 1;
156 } else {
157 cw.queue_char(self.base_style, cand_char)?;
158 }
159 }
160 if right_filling > 0 {
161 cw.repeat(self.base_style, &SPACE_FILLING, right_filling)?;
162 }
163 } else if let Some(w) = self.display_width {
164 match self.align {
165 Alignment::Center => {
166 cw.queue_str(self.base_style, &format!("{:^w$}", self.string, w = w))?;
167 }
168 Alignment::Right => {
169 cw.queue_str(self.base_style, &format!("{:>w$}", self.string, w = w))?;
170 }
171 _ => {
172 cw.queue_str(self.base_style, &format!("{:<w$}", self.string, w = w))?;
173 }
174 }
175 } else {
176 cw.queue_str(self.base_style, self.string)?;
177 }
178 Ok(())
179 }
180}