1use nu_color_config::StyleComputer;
2
3use tabled::{
4 grid::{
5 ansi::{ANSIBuf, ANSIStr},
6 records::vec_records::Text,
7 util::string::get_text_width,
8 },
9 settings::{
10 width::{Truncate, Wrap},
11 Color,
12 },
13};
14
15use crate::common::get_leading_trailing_space_style;
16
17pub fn string_width(text: &str) -> usize {
18 get_text_width(text)
19}
20
21pub fn string_wrap(text: &str, width: usize, keep_words: bool) -> String {
22 if text.is_empty() {
23 return String::new();
24 }
25
26 let text_width = string_width(text);
27 if text_width <= width {
28 return text.to_owned();
29 }
30
31 Wrap::wrap(text, width, keep_words)
32}
33
34pub fn string_truncate(text: &str, width: usize) -> String {
35 let line = match text.lines().next() {
36 Some(line) => line,
37 None => return String::new(),
38 };
39
40 Truncate::truncate(line, width).into_owned()
41}
42
43pub fn clean_charset(text: &str) -> String {
44 let mut buf = String::with_capacity(text.len());
57
58 for c in text.chars() {
65 match c {
66 '\r' => continue,
67 '\t' => {
68 buf.push(' ');
69 buf.push(' ');
70 buf.push(' ');
71 buf.push(' ');
72 }
73 c => {
74 buf.push(c);
75 }
76 }
77 }
78
79 buf
80}
81
82pub fn colorize_space(data: &mut [Vec<Text<String>>], style_computer: &StyleComputer<'_>) {
83 let style = match get_leading_trailing_space_style(style_computer).color_style {
84 Some(color) => color,
85 None => return,
86 };
87
88 let style = ANSIBuf::from(convert_style(style));
89 let style = style.as_ref();
90 if style.is_empty() {
91 return;
92 }
93
94 colorize_list(data, style, style);
95}
96
97pub fn colorize_space_str(text: &mut String, style_computer: &StyleComputer<'_>) {
98 let style = match get_leading_trailing_space_style(style_computer).color_style {
99 Some(color) => color,
100 None => return,
101 };
102
103 let style = ANSIBuf::from(convert_style(style));
104 let style = style.as_ref();
105 if style.is_empty() {
106 return;
107 }
108
109 *text = colorize_space_one(text, style, style);
110}
111
112fn colorize_list(data: &mut [Vec<Text<String>>], lead: ANSIStr<'_>, trail: ANSIStr<'_>) {
113 for row in data.iter_mut() {
114 for cell in row {
115 let buf = colorize_space_one(cell.as_ref(), lead, trail);
116 *cell = Text::new(buf);
117 }
118 }
119}
120
121fn colorize_space_one(text: &str, lead: ANSIStr<'_>, trail: ANSIStr<'_>) -> String {
122 use fancy_regex::Captures;
123 use fancy_regex::Regex;
124 use std::sync::LazyLock;
125
126 static RE_LEADING: LazyLock<Regex> = LazyLock::new(|| {
127 Regex::new(r"(?m)(?P<beginsp>^\s+)").expect("error with leading space regex")
128 });
129 static RE_TRAILING: LazyLock<Regex> = LazyLock::new(|| {
130 Regex::new(r"(?m)(?P<endsp>\s+$)").expect("error with trailing space regex")
131 });
132
133 let mut buf = text.to_owned();
134
135 if !lead.is_empty() {
136 buf = RE_LEADING
137 .replace_all(&buf, |cap: &Captures| {
138 let spaces = cap.get(1).expect("valid").as_str();
139 format!("{}{}{}", lead.get_prefix(), spaces, lead.get_suffix())
140 })
141 .into_owned();
142 }
143
144 if !trail.is_empty() {
145 buf = RE_TRAILING
146 .replace_all(&buf, |cap: &Captures| {
147 let spaces = cap.get(1).expect("valid").as_str();
148 format!("{}{}{}", trail.get_prefix(), spaces, trail.get_suffix())
149 })
150 .into_owned();
151 }
152
153 buf
154}
155
156pub fn convert_style(style: nu_ansi_term::Style) -> Color {
157 Color::new(style.prefix().to_string(), style.suffix().to_string())
158}
159
160pub fn is_color_empty(c: &Color) -> bool {
161 c.get_prefix().is_empty() && c.get_suffix().is_empty()
162}