Skip to content

Commit

Permalink
Sort the emote picker list by match score instead of alphabetical ord…
Browse files Browse the repository at this point in the history
…er (#656)
  • Loading branch information
Nogesma authored Nov 10, 2024
1 parent 18a10b6 commit acbfc1d
Showing 1 changed file with 68 additions and 60 deletions.
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

0 comments on commit acbfc1d

Please sign in to comment.