Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 68 additions & 60 deletions src/ui/components/emote_picker.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use log::warn;
use memchr::memmem;
use once_cell::sync::Lazy;
use std::cmp::max;
use tui::{
layout::Rect,
Expand Down Expand Up @@ -32,6 +33,8 @@ use crate::{
},
};

static FUZZY_FINDER: Lazy<SkimMatcherV2> = Lazy::new(SkimMatcherV2::default);

pub struct EmotePickerWidget {
config: SharedCompleteConfig,
emotes: SharedEmotes,
Expand Down Expand Up @@ -129,77 +132,82 @@ impl Component for EmotePickerWidget {
let mut items = Vec::with_capacity(max_len);
let mut bad_emotes = vec![];

let mut current_input = self.input.to_string();
let current_input = self.input.to_string();

let cell_size = *self
.emotes
.cell_size
.get()
.expect("Terminal cell size should be set when emotes are enabled.");

let finder = if current_input.is_empty() {
None
} else {
current_input.make_ascii_lowercase();
Some(memmem::Finder::new(&current_input))
};

for (name, (filename, zero_width)) in self
.emotes
.user_emotes
.borrow()
.iter()
.chain(self.emotes.global_emotes.borrow().iter())
// Enter a new scope to drop the user/global emotes borrow when we don't need them anymore.
{
if items.len() >= max_len {
break;
}
let user_emotes = self.emotes.user_emotes.borrow();
let global_emotes = self.emotes.global_emotes.borrow();

// First find all the emotes that match the input
let mut matched_emotes = user_emotes
.iter()
.chain(global_emotes.iter())
.filter_map(|(name, data)| {
Some((
name,
data,
FUZZY_FINDER.fuzzy_indices(&name.to_ascii_lowercase(), &current_input)?,
))
})
.collect::<Vec<_>>();

// Sort them by match score
matched_emotes.sort_by(|a, b| b.2 .0.cmp(&a.2 .0));

for (name, (filename, zero_width), (_, matched_indices)) in matched_emotes {
if items.len() >= max_len {
break;
}

// Skip emotes that do not contain the current input, if it is not empty.
let Some(pos) = finder
.as_ref()
.map_or_else(|| Some(0), |f| f.find(name.to_ascii_lowercase().as_bytes()))
else {
continue;
};

let Ok(loaded_emote) = load_picker_emote(
name,
filename,
*zero_width,
&mut self.emotes.info.borrow_mut(),
cell_size,
)
.map_err(|e| warn!("{e}")) else {
bad_emotes.push(name.clone());
continue;
};

let cols = (loaded_emote.width as f32 / cell_size.0).ceil() as u16;

#[cfg(not(target_os = "windows"))]
let underline_style = Style::default()
.fg(u32_to_color(loaded_emote.hash))
.underline_color(u32_to_color(1));

#[cfg(target_os = "windows")]
let underline_style = { Style::default().fg(u32_to_color(loaded_emote.hash)) };

let row = vec![
Span::raw(name[0..pos].to_owned()),
Span::styled(
name[pos..(pos + current_input.len())].to_owned(),
self.search_theme,
),
Span::raw(name[(pos + current_input.len())..].to_owned()),
Span::raw(" - "),
Span::styled(
let Ok(loaded_emote) = load_picker_emote(
name,
filename,
*zero_width,
&mut self.emotes.info.borrow_mut(),
cell_size,
)
.map_err(|e| warn!("{e}")) else {
bad_emotes.push(name.clone());
continue;
};

let cols = (loaded_emote.width as f32 / cell_size.0).ceil() as u16;

#[cfg(not(target_os = "windows"))]
let underline_style = Style::default()
.fg(u32_to_color(loaded_emote.hash))
.underline_color(u32_to_color(1));

#[cfg(target_os = "windows")]
let underline_style = { Style::default().fg(u32_to_color(loaded_emote.hash)) };

let mut row = name
.chars()
.enumerate()
.map(|(i, c)| {
if matched_indices.contains(&i) {
Span::styled(c.to_string(), self.search_theme)
} else {
Span::raw(c.to_string())
}
})
.collect::<Vec<Span>>();

row.push(Span::raw(" - "));
row.push(Span::styled(
UnicodePlaceholder::new(cols as usize).string(),
underline_style,
),
];
));

items.push((name.clone(), ListItem::new(vec![Line::from(row)])));
items.push((name.clone(), ListItem::new(vec![Line::from(row)])));
}
}

// Remove emotes that could not be loaded from list of emotes
Expand Down