Skip to content

Commit

Permalink
feat: expose typo resistance, update frizbee
Browse files Browse the repository at this point in the history
  • Loading branch information
Saghen committed Oct 21, 2024
1 parent 9f4f93e commit 63b7b22
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 30 deletions.
30 changes: 15 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
lockFile = ./Cargo.lock;
outputHashes = {
"frizbee-0.1.0" =
"sha256-zO2S282DVCjnALMXu3GxmAfjCXsPNUZ7+xgiqITfGmU=";
"sha256-/O+XLuRc1/a7FQLnLV8peQ4BfSXoiHvcEGLGE6rbegc=";
};
};
};
Expand Down
4 changes: 4 additions & 0 deletions lua/blink/cmp/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
--- @field forceVersion? string | nil

--- @class blink.cmp.FuzzyConfig
--- @field use_typo_resistance? boolean
--- @field use_frecency? boolean
--- @field use_proximity? boolean
--- @field max_items? number
Expand Down Expand Up @@ -218,6 +219,9 @@ local config = {
},

fuzzy = {
-- when enabled, allows for a number of typos relative to the length of the query
-- disabling this matches the behavior of fzf
use_typo_resistance = false,
-- frencency tracks the most recently/frequently used items and boosts the score of the item
use_frecency = true,
-- proximity bonus boosts the score of items with a value in the buffer
Expand Down
42 changes: 30 additions & 12 deletions lua/blink/cmp/fuzzy/fuzzy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ pub struct MatchedLspItem {

#[derive(Clone, Serialize, Deserialize, Hash)]
pub struct FuzzyOptions {
use_typo_resistance: bool,
use_frecency: bool,
use_proximity: bool,
nearby_words: Option<Vec<String>>,
min_score: u16,
max_items: u32,
Expand All @@ -68,14 +70,18 @@ pub struct FuzzyOptions {
impl FromLua<'_> for FuzzyOptions {
fn from_lua(value: LuaValue<'_>, _lua: &'_ Lua) -> LuaResult<Self> {
if let Some(tab) = value.as_table() {
let use_typo_resistance: bool = tab.get("use_typo_resistance").unwrap_or_default();
let use_frecency: bool = tab.get("use_frecency").unwrap_or_default();
let use_proximity: bool = tab.get("use_proximity").unwrap_or_default();
let nearby_words: Option<Vec<String>> = tab.get("nearby_words").ok();
let min_score: u16 = tab.get("min_score").unwrap_or_default();
let max_items: u32 = tab.get("max_items").unwrap_or_default();
let sorts: Vec<String> = tab.get("sorts").unwrap_or_default();

Ok(FuzzyOptions {
use_typo_resistance,
use_frecency,
use_proximity,
nearby_words,
min_score,
max_items,
Expand All @@ -102,17 +108,23 @@ pub fn fuzzy(
// Fuzzy match with fzrs
let haystack_labels = haystack
.iter()
.map(|s| s.label.as_str())
.map(|s| {
if let Some(filter_text) = &s.filter_text {
filter_text.as_str()
} else {
s.label.as_str()
}
})
.collect::<Vec<_>>();
let options = frizbee::Options {
prefilter: !opts.use_typo_resistance,
min_score: opts.min_score,
stable_sort: false,
..Default::default()
};
let mut matches = frizbee::match_list(&needle, &haystack_labels, options);

// Sort by scores
// TODO: boost exact matches
let match_scores = matches
.iter()
.map(|mtch| {
Expand All @@ -121,23 +133,29 @@ pub fn fuzzy(
} else {
0
};
let nearby_words_score = nearby_words
.get(&haystack[mtch.index_in_haystack].label)
.map(|_| 2)
.unwrap_or(0);
let nearby_words_score = if opts.use_proximity {
nearby_words
.get(&haystack[mtch.index_in_haystack].label)
.map(|_| 2)
.unwrap_or(0)
} else {
0
};
let score_offset = haystack[mtch.index_in_haystack].score_offset.unwrap_or(0);

(mtch.score as i32) + frecency_score + nearby_words_score + score_offset
})
.collect::<Vec<_>>();

// Find the highest score and filter out matches that are unreasonably lower than it
let max_score = matches.iter().map(|mtch| mtch.score).max().unwrap_or(0);
let secondary_min_score = max_score.max(16) - 16;
matches = matches
.into_iter()
.filter(|mtch| mtch.score >= secondary_min_score)
.collect::<Vec<_>>();
if opts.use_typo_resistance {
let max_score = matches.iter().map(|mtch| mtch.score).max().unwrap_or(0);
let secondary_min_score = max_score.max(16) - 16;
matches = matches
.into_iter()
.filter(|mtch| mtch.score >= secondary_min_score)
.collect::<Vec<_>>();
}

// Sort matches by sort criteria
for sort in opts.sorts.iter() {
Expand Down
5 changes: 3 additions & 2 deletions lua/blink/cmp/fuzzy/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function fuzzy.access(item) fuzzy.rust.access(item) end
function fuzzy.get_words(lines) return fuzzy.rust.get_words(lines) end

---@param needle string
---@param items blink.cmp.CompletionItem[]?
---@param haystack blink.cmp.CompletionItem[]?
---@return blink.cmp.CompletionItem[]
function fuzzy.filter_items(needle, haystack)
haystack = haystack or {}
Expand All @@ -44,8 +44,9 @@ function fuzzy.filter_items(needle, haystack)
-- each matching char is worth 4 points and it receives a bonus for capitalization, delimiter and prefix
-- so this should generally be good
-- TODO: make this configurable
min_score = 6 * needle:len(),
min_score = config.fuzzy.use_typo_resistance and (6 * needle:len()) or 0,
max_items = config.fuzzy.max_items,
use_typo_resistance = config.fuzzy.use_typo_resistance,
use_frecency = config.fuzzy.use_frecency,
use_proximity = config.fuzzy.use_proximity,
sorts = config.fuzzy.sorts,
Expand Down

0 comments on commit 63b7b22

Please sign in to comment.