Wrapping works, searching only first line as well

This commit is contained in:
Xithrius 2023-03-09 07:25:37 -08:00
parent f3d5956cb2
commit 3cf493d574
No known key found for this signature in database
GPG Key ID: A867F27CC80B28C1
2 changed files with 185 additions and 178 deletions

View File

@ -4,7 +4,6 @@ use chrono::{offset::Local, DateTime};
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use lazy_static::lazy_static;
use regex::Regex;
use textwrap::{Options, WrapAlgorithm};
use tui::{
style::{Color, Color::Rgb, Modifier, Style},
text::{Span, Spans},
@ -18,13 +17,11 @@ use crate::{
styles::{
DATETIME_DARK, DATETIME_LIGHT, HIGHLIGHT_NAME_DARK, HIGHLIGHT_NAME_LIGHT, SYSTEM_CHAT,
},
text::wrap_once,
},
};
lazy_static! {
pub static ref FUZZY_FINDER: SkimMatcherV2 = SkimMatcherV2::default();
pub static ref WRAP_ALGORITHM: WrapAlgorithm = WrapAlgorithm::Custom(wrap_once);
}
#[derive(Debug, Clone)]
@ -68,37 +65,17 @@ impl MessageData {
fn wrap_message(
&self,
combined_message: String,
frontend_config: &FrontendConfig,
width: usize,
time_sent: String,
) -> Vec<String> {
let width_sub_margin = width - (frontend_config.margin as usize * 2);
// Total width of the window subtracted by any margin, then the two border line lengths.
let wrap_limit = width - (frontend_config.margin as usize * 2) - 2;
// Subtraction of 2 for the spaces and ':' in between the date, user, and message.
let first_line_limit = width_sub_margin - time_sent.len() - self.author.len() - 3;
let mut message_split = textwrap::wrap(
&self.payload,
Options::new(first_line_limit).wrap_algorithm(*WRAP_ALGORITHM),
)
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>();
if message_split.len() > 1 {
let extra = message_split[1].clone();
if extra.len() > width_sub_margin {
let extra_split = textwrap::wrap(&extra, width_sub_margin)
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>();
message_split.extend(extra_split);
}
}
message_split
textwrap::wrap(&combined_message, wrap_limit)
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
}
pub fn to_spans(
@ -113,104 +90,193 @@ impl MessageData {
.format(&frontend_config.date_format)
.to_string();
let message_spans = self
.wrap_message(frontend_config, width, time_sent.clone())
.iter()
.map(|s| {
if let Some(search) = search_highlight.clone() {
if let Some((_, indices)) = FUZZY_FINDER.fuzzy_indices(s, search.as_str()) {
return s
.chars()
.enumerate()
.map(|(i, c)| {
if indices.contains(&i) {
Span::styled(
c.to_string(),
Style::default()
.fg(Color::Red)
.add_modifier(Modifier::BOLD),
)
} else {
Span::raw(s.to_string())
}
})
.collect::<Vec<Span>>();
}
}
let raw_message_start = format!("{} {}: ", time_sent, &self.author);
return vec![Span::raw(s.clone())];
})
.flatten()
.collect::<Vec<Span>>();
let raw_message = format!("{}{}", raw_message_start, &self.payload);
let mut info = if frontend_config.date_shown {
vec![
Span::styled(
time_sent,
match frontend_config.theme {
Theme::Light => DATETIME_LIGHT,
_ => DATETIME_DARK,
},
),
Span::raw(" "),
]
let search = if let Some(user_search) = search_highlight {
FUZZY_FINDER.fuzzy_indices(&raw_message[raw_message_start.len()..], &user_search)
} else {
vec![]
None
};
if frontend_config.username_shown {
info.extend(vec![
Span::styled(
self.author.clone(),
if self.system {
SYSTEM_CHAT
let raw_message_wrapped = self.wrap_message(raw_message, frontend_config, width);
let mut wrapped_message_spans = vec![];
// let time_spent_styled_span = Span::styled(
// &wrapped_message[0][..time_sent.len()],
// match frontend_config.theme {
// Theme::Light => DATETIME_LIGHT,
// _ => DATETIME_DARK,
// },
// );
let mut start_vec = vec![
Span::styled(
raw_message_wrapped[0][..time_sent.len()].to_string(),
match frontend_config.theme {
Theme::Light => DATETIME_LIGHT,
_ => DATETIME_DARK,
},
),
Span::raw(" "),
Span::styled(
self.author.clone(),
if self.system {
SYSTEM_CHAT
} else {
Style::default().fg(self.hash_username(&frontend_config.palette))
},
),
Span::raw(": "),
];
start_vec.extend(if let Some((_, indices)) = search {
raw_message_wrapped[0][raw_message_start.len()..]
.chars()
.enumerate()
.map(|(i, c)| {
if indices.contains(&i) {
Span::styled(
c.to_string(),
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
)
} else {
Style::default().fg(self.hash_username(&frontend_config.palette))
},
),
Span::raw(": "),
message_spans[0].clone(),
]);
Span::raw(c.to_string())
}
})
.collect::<Vec<Span>>()
} else {
vec![Span::raw(
raw_message_wrapped[0][raw_message_start.len()..].to_string(),
)]
});
wrapped_message_spans.push(Spans::from(start_vec));
if raw_message_wrapped.len() > 1 {
wrapped_message_spans.extend(
raw_message_wrapped[1..]
.iter()
.map(|s| Spans::from(vec![Span::raw(s.to_string())]))
.collect::<Vec<Spans>>(),
);
}
let mut info_spans = vec![Spans::from(info)];
if message_spans.len() > 1 {
for extra in message_spans[1..].iter().cloned() {
info_spans.push(Spans::from(extra));
}
}
(info_spans, 0)
// let username_highlight_style = username_highlight.map_or_else(Style::default, |username| {
// if Regex::new(format!("^.*{username}.*$").as_str())
// .unwrap()
// .is_match(&message)
// {
// match frontend_config.theme {
// Theme::Light => HIGHLIGHT_NAME_LIGHT,
// _ => HIGHLIGHT_NAME_DARK,
// }
// } else {
// Style::default()
// }
// });
// let mut num_search_matches = 0;
// if msg_cells.len() > 1 {
// for cell in msg_cells.iter().skip(1) {
// let mut wrapped_msg = vec![Cell::from(""), cell.clone()];
// if frontend_config.date_shown {
// wrapped_msg.insert(0, Cell::from(""));
// }
// row_vector.push(Row::new(wrapped_msg));
// }
// }
(wrapped_message_spans, 0)
}
// pub fn to_spans(
// &self,
// frontend_config: &FrontendConfig,
// width: usize,
// search_highlight: Option<String>,
// username_highlight: Option<String>,
// ) -> (Vec<Spans>, u32) {
// let time_sent = self
// .time_sent
// .format(&frontend_config.date_format)
// .to_string();
// let message_spans = self
// .wrap_message(frontend_config, width, time_sent.clone())
// .iter()
// .map(|s| {
// if let Some(search) = search_highlight.clone() {
// if let Some((_, indices)) = FUZZY_FINDER.fuzzy_indices(s, search.as_str()) {
// return s
// .chars()
// .enumerate()
// .map(|(i, c)| {
// if indices.contains(&i) {
// Span::styled(
// c.to_string(),
// Style::default()
// .fg(Color::Red)
// .add_modifier(Modifier::BOLD),
// )
// } else {
// Span::raw(s.to_string())
// }
// })
// .collect::<Vec<Span>>();
// }
// }
// return vec![Span::raw(s.clone())];
// })
// .flatten()
// .collect::<Vec<Span>>();
// let mut info = if frontend_config.date_shown {
// vec![
// Span::styled(
// time_sent,
// match frontend_config.theme {
// Theme::Light => DATETIME_LIGHT,
// _ => DATETIME_DARK,
// },
// ),
// Span::raw(" "),
// ]
// } else {
// vec![]
// };
// if frontend_config.username_shown {
// info.extend(vec![
// Span::styled(
// self.author.clone(),
// if self.system {
// SYSTEM_CHAT
// } else {
// Style::default().fg(self.hash_username(&frontend_config.palette))
// },
// ),
// Span::raw(": "),
// message_spans[0].clone(),
// ]);
// }
// let mut info_spans = vec![Spans::from(info)];
// if message_spans.len() > 1 {
// for extra in message_spans[1..].iter().cloned() {
// info_spans.push(Spans::from(extra));
// }
// }
// (info_spans, 0)
// // let username_highlight_style = username_highlight.map_or_else(Style::default, |username| {
// // if Regex::new(format!("^.*{username}.*$").as_str())
// // .unwrap()
// // .is_match(&message)
// // {
// // match frontend_config.theme {
// // Theme::Light => HIGHLIGHT_NAME_LIGHT,
// // _ => HIGHLIGHT_NAME_DARK,
// // }
// // } else {
// // Style::default()
// // }
// // });
// // let mut num_search_matches = 0;
// // if msg_cells.len() > 1 {
// // for cell in msg_cells.iter().skip(1) {
// // let mut wrapped_msg = vec![Cell::from(""), cell.clone()];
// // if frontend_config.date_shown {
// // wrapped_msg.insert(0, Cell::from(""));
// // }
// // row_vector.push(Row::new(wrapped_msg));
// // }
// // }
// }
}
#[derive(Debug, Copy, Clone)]

View File

@ -60,35 +60,6 @@ pub fn first_similarity(possibilities: &[String], search: &str) -> Option<String
})
}
/// Wraps the first line according to the width, letting the second line go as long as it would like.
/// Modified version of function
/// [`wrap_first_fit`](<https://github.com/mgeisler/textwrap/blob/74b55209a75a49e4fadde3e07a6a33cdd2f24f5d/src/wrap_algorithms.rs#L347-L371/>)
pub fn wrap_once<'a, 'b>(words: &'b [Word<'a>], line_widths: &'b [usize]) -> Vec<&'b [Word<'a>]> {
let default_line_width = line_widths.last().copied().unwrap_or(0);
let mut lines = Vec::new();
let mut start = 0;
let mut width = 0;
for (idx, word) in words.iter().enumerate() {
let line_width = line_widths
.get(lines.len())
.copied()
.unwrap_or(default_line_width);
if width + word.width() > line_width && idx > start {
lines.push(&words[start..idx]);
start = idx;
break;
}
width += word.width();
}
lines.push(&words[start..]);
lines
}
#[cfg(test)]
mod tests {
use textwrap::{wrap, Options, WrapAlgorithm};
@ -159,34 +130,4 @@ mod tests {
assert_eq!(output, None);
}
#[test]
fn test_wrap_once_to_one_line() {
let options = Options::new(20).wrap_algorithm(WrapAlgorithm::Custom(wrap_once));
assert_eq!(
wrap("Something, another", options),
vec!["Something, another"]
);
}
#[test]
fn test_wrap_once_to_two_lines() {
let options = Options::new(10).wrap_algorithm(WrapAlgorithm::Custom(wrap_once));
assert_eq!(
wrap("First, second, third, fourth, fifth, sixth", options),
vec!["First,", "second, third, fourth, fifth, sixth"]
);
}
#[test]
fn test_wrap_once_one_long_word_to_two_lines() {
let options = Options::new(10).wrap_algorithm(WrapAlgorithm::Custom(wrap_once));
assert_eq!(
wrap("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", options),
vec!["aaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
);
}
}