Skip to content

Commit 63b7b22

Browse files
committed
feat: expose typo resistance, update frizbee
1 parent 9f4f93e commit 63b7b22

File tree

5 files changed

+53
-30
lines changed

5 files changed

+53
-30
lines changed

Cargo.lock

+15-15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
lockFile = ./Cargo.lock;
5252
outputHashes = {
5353
"frizbee-0.1.0" =
54-
"sha256-zO2S282DVCjnALMXu3GxmAfjCXsPNUZ7+xgiqITfGmU=";
54+
"sha256-/O+XLuRc1/a7FQLnLV8peQ4BfSXoiHvcEGLGE6rbegc=";
5555
};
5656
};
5757
};

lua/blink/cmp/config.lua

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
--- @field forceVersion? string | nil
7272

7373
--- @class blink.cmp.FuzzyConfig
74+
--- @field use_typo_resistance? boolean
7475
--- @field use_frecency? boolean
7576
--- @field use_proximity? boolean
7677
--- @field max_items? number
@@ -218,6 +219,9 @@ local config = {
218219
},
219220

220221
fuzzy = {
222+
-- when enabled, allows for a number of typos relative to the length of the query
223+
-- disabling this matches the behavior of fzf
224+
use_typo_resistance = false,
221225
-- frencency tracks the most recently/frequently used items and boosts the score of the item
222226
use_frecency = true,
223227
-- proximity bonus boosts the score of items with a value in the buffer

lua/blink/cmp/fuzzy/fuzzy.rs

+30-12
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ pub struct MatchedLspItem {
5858

5959
#[derive(Clone, Serialize, Deserialize, Hash)]
6060
pub struct FuzzyOptions {
61+
use_typo_resistance: bool,
6162
use_frecency: bool,
63+
use_proximity: bool,
6264
nearby_words: Option<Vec<String>>,
6365
min_score: u16,
6466
max_items: u32,
@@ -68,14 +70,18 @@ pub struct FuzzyOptions {
6870
impl FromLua<'_> for FuzzyOptions {
6971
fn from_lua(value: LuaValue<'_>, _lua: &'_ Lua) -> LuaResult<Self> {
7072
if let Some(tab) = value.as_table() {
73+
let use_typo_resistance: bool = tab.get("use_typo_resistance").unwrap_or_default();
7174
let use_frecency: bool = tab.get("use_frecency").unwrap_or_default();
75+
let use_proximity: bool = tab.get("use_proximity").unwrap_or_default();
7276
let nearby_words: Option<Vec<String>> = tab.get("nearby_words").ok();
7377
let min_score: u16 = tab.get("min_score").unwrap_or_default();
7478
let max_items: u32 = tab.get("max_items").unwrap_or_default();
7579
let sorts: Vec<String> = tab.get("sorts").unwrap_or_default();
7680

7781
Ok(FuzzyOptions {
82+
use_typo_resistance,
7883
use_frecency,
84+
use_proximity,
7985
nearby_words,
8086
min_score,
8187
max_items,
@@ -102,17 +108,23 @@ pub fn fuzzy(
102108
// Fuzzy match with fzrs
103109
let haystack_labels = haystack
104110
.iter()
105-
.map(|s| s.label.as_str())
111+
.map(|s| {
112+
if let Some(filter_text) = &s.filter_text {
113+
filter_text.as_str()
114+
} else {
115+
s.label.as_str()
116+
}
117+
})
106118
.collect::<Vec<_>>();
107119
let options = frizbee::Options {
120+
prefilter: !opts.use_typo_resistance,
108121
min_score: opts.min_score,
109122
stable_sort: false,
110123
..Default::default()
111124
};
112125
let mut matches = frizbee::match_list(&needle, &haystack_labels, options);
113126

114127
// Sort by scores
115-
// TODO: boost exact matches
116128
let match_scores = matches
117129
.iter()
118130
.map(|mtch| {
@@ -121,23 +133,29 @@ pub fn fuzzy(
121133
} else {
122134
0
123135
};
124-
let nearby_words_score = nearby_words
125-
.get(&haystack[mtch.index_in_haystack].label)
126-
.map(|_| 2)
127-
.unwrap_or(0);
136+
let nearby_words_score = if opts.use_proximity {
137+
nearby_words
138+
.get(&haystack[mtch.index_in_haystack].label)
139+
.map(|_| 2)
140+
.unwrap_or(0)
141+
} else {
142+
0
143+
};
128144
let score_offset = haystack[mtch.index_in_haystack].score_offset.unwrap_or(0);
129145

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

134150
// Find the highest score and filter out matches that are unreasonably lower than it
135-
let max_score = matches.iter().map(|mtch| mtch.score).max().unwrap_or(0);
136-
let secondary_min_score = max_score.max(16) - 16;
137-
matches = matches
138-
.into_iter()
139-
.filter(|mtch| mtch.score >= secondary_min_score)
140-
.collect::<Vec<_>>();
151+
if opts.use_typo_resistance {
152+
let max_score = matches.iter().map(|mtch| mtch.score).max().unwrap_or(0);
153+
let secondary_min_score = max_score.max(16) - 16;
154+
matches = matches
155+
.into_iter()
156+
.filter(|mtch| mtch.score >= secondary_min_score)
157+
.collect::<Vec<_>>();
158+
}
141159

142160
// Sort matches by sort criteria
143161
for sort in opts.sorts.iter() {

lua/blink/cmp/fuzzy/init.lua

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function fuzzy.access(item) fuzzy.rust.access(item) end
2626
function fuzzy.get_words(lines) return fuzzy.rust.get_words(lines) end
2727

2828
---@param needle string
29-
---@param items blink.cmp.CompletionItem[]?
29+
---@param haystack blink.cmp.CompletionItem[]?
3030
---@return blink.cmp.CompletionItem[]
3131
function fuzzy.filter_items(needle, haystack)
3232
haystack = haystack or {}
@@ -44,8 +44,9 @@ function fuzzy.filter_items(needle, haystack)
4444
-- each matching char is worth 4 points and it receives a bonus for capitalization, delimiter and prefix
4545
-- so this should generally be good
4646
-- TODO: make this configurable
47-
min_score = 6 * needle:len(),
47+
min_score = config.fuzzy.use_typo_resistance and (6 * needle:len()) or 0,
4848
max_items = config.fuzzy.max_items,
49+
use_typo_resistance = config.fuzzy.use_typo_resistance,
4950
use_frecency = config.fuzzy.use_frecency,
5051
use_proximity = config.fuzzy.use_proximity,
5152
sorts = config.fuzzy.sorts,

0 commit comments

Comments
 (0)