mirror of
https://github.com/Xithrius/twitch-tui.git
synced 2024-09-11 05:35:26 +03:00
Wrapping works, searching only first line as well
This commit is contained in:
parent
f3d5956cb2
commit
3cf493d574
@ -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)]
|
||||
|
@ -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"]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user