Skip to content

Commit a218cc6

Browse files
glepnirchrisbra
authored andcommitted
patch 9.1.0463: no fuzzy-matching support for insert-completion
Problem: no fuzzy-matching support for insert-completion Solution: enable insert-mode completion with fuzzy-matching using :set completopt+=fuzzy (glepnir). closes: #14878 Signed-off-by: glepnir <[email protected]> Signed-off-by: Christian Brabandt <[email protected]>
1 parent 734286e commit a218cc6

File tree

8 files changed

+191
-31
lines changed

8 files changed

+191
-31
lines changed

runtime/doc/options.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*options.txt* For Vim version 9.1. Last change: 2024 Jun 01
1+
*options.txt* For Vim version 9.1. Last change: 2024 Jun 03
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -2143,6 +2143,11 @@ A jump table for the options with a short description can be found at |Q_op|.
21432143
select one from the menu. Only works in combination with
21442144
"menu" or "menuone".
21452145

2146+
fuzzy Enable |fuzzy-matching| for completion candidates. This
2147+
allows for more flexible and intuitive matching, where
2148+
characters can be skipped and matches can be found even
2149+
if the exact sequence is not typed.
2150+
21462151
*'completepopup'* *'cpp'*
21472152
'completepopup' 'cpp' string (default empty)
21482153
global

runtime/doc/pattern.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*pattern.txt* For Vim version 9.1. Last change: 2024 Apr 26
1+
*pattern.txt* For Vim version 9.1. Last change: 2024 Jun 03
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1513,5 +1513,7 @@ the matching positions and the fuzzy match scores.
15131513

15141514
The "f" flag of `:vimgrep` enables fuzzy matching.
15151515

1516+
To enable fuzzy matching for |ins-completion|, add the "fuzzy" value to the
1517+
'completeopt' option.
15161518

15171519
vim:tw=78:ts=8:noet:ft=help:norl:

runtime/doc/version9.txt

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*version9.txt* For Vim version 9.1. Last change: 2024 May 17
1+
*version9.txt* For Vim version 9.1. Last change: 2024 Jun 03
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -31711,12 +31711,12 @@ in |vim9-class|. The following features are supported:
3171131711

3171231712
Support for creating a type alias for an existing type is added.
3171331713

31714-
Virtual text
31714+
Virtual text ~
3171531715
------------
3171631716
Support for adding |virtual-text| to a buffer is added. This is useful for
3171731717
language server features (e.g. inlay hints)
3171831718

31719-
Smooth Scroll
31719+
Smooth Scroll ~
3172031720
-------------
3172131721
Support for scrolling text using screen lines instead of file lines is added.
3172231722
Refer to the 'smoothscroll' option.
@@ -31726,7 +31726,8 @@ The EditorConfig (|editorconfig-install|) and the JSON formatting
3172631726

3172731727
OpenVMS x86_64 platform port: http://www.polarhome.com/vim/
3172831728

31729-
Other improvements *new-other-9.1*
31729+
*new-other-9.1*
31730+
Other improvements ~
3173031731
------------------
3173131732
- Support for undercurl (|t_Ce|), double underline (|t_Us|), dotted underline
3173231733
(|t_ds|) and dashed underline (|t_Ds|) termcap entries and
@@ -31783,7 +31784,8 @@ Other improvements *new-other-9.1*
3178331784
- xxd: reversing a bit dump (xxd -r).
3178431785
- xxd: customize the variable name used in the C include output (xxd -n).
3178531786

31786-
Changed *changed-9.1*
31787+
*changed-9.1*
31788+
Changed ~
3178731789
-------
3178831790
- The features |++builtin_terms|, |+cmdline_info|, |+cmdwin|, |+file_in_path|,
3178931791
|+float|, |+path_extra|, |+textobjects|, |+wildignore| and |+wildmenu| are
@@ -31815,7 +31817,8 @@ Changed *changed-9.1*
3181531817
- Migrate to autoconf 2.71.
3181631818
- Start using C99 feature (declare variable in for loops).
3181731819

31818-
Added *added-9.1*
31820+
*added-9.1*
31821+
Added ~
3181931822
-----
3182031823

3182131824
Various syntax, indent and other plugins were added.
@@ -41541,30 +41544,31 @@ VERSION 9.2 *version-9.2* *version9.2* *vim-9.2*
4154141544
This section is about improvements made between version 9.1 and 9.2
4154241545
and is a work in progress.
4154341546

41544-
Support for Wayland UI.
41545-
41546-
Support for the XDG Desktop Specification |xdg-base-dir|
41547-
41548-
Vim9 script
41547+
Vim9 script ~
4154941548
-----------
4155041549
Add support for internal builtin functions with vim9 objects, see
4155141550
|builtin-object-methods|
4155241551

4155341552
Enum support for Vim9 script |:enum|
4155441553

41555-
Other improvements *new-other-9.2*
41554+
*new-other-9.2*
41555+
Other new features ~
4155641556
------------------
4155741557
The comment plugin |comment-install| is included.
4155841558

41559-
Changed *changed-9.2*
41560-
-------
41559+
Support for Wayland UI.
4156141560

41561+
Support for the XDG Desktop Specification |xdg-base-dir|
41562+
41563+
*changed-9.2*
41564+
Changed~
41565+
-------
4156241566
- use 'smoothscroll' logic for CTRL-F and CTRL-B for pagewise scrolling
4156341567
- use 'smoothscroll' logic for CTRL-D and CTRL-U for half-pagewise scrolling
4156441568

41565-
Added *added-9.2*
41569+
*added-9.2*
41570+
Added ~
4156641571
-----
41567-
4156841572
Various syntax, indent and other plugins were added.
4156941573

4157041574
Functions: ~
@@ -41598,6 +41602,7 @@ Options: ~
4159841602

4159941603
'winfixbuf' Keep buffer focused in a window
4160041604
't_xo' Terminal uses XON/XOFF handshaking (e.g. vt420)
41605+
't_CF' Support for alternate font highlighting terminal code
4160141606

4160241607
==============================================================================
4160341608
INCOMPATIBLE CHANGES *incompatible-9.2*
@@ -41606,22 +41611,22 @@ Improved/Different MS-Windows mapping support
4160641611
|w32-experimental-keycode-trans-strategy|
4160741612

4160841613
==============================================================================
41609-
IMPROVEMENTS *improvements-9.2*
41614+
IMPROVEMENTS *improvements-9.2*
4161041615

4161141616
Support for command-line completion of 'keymap' option values.
4161241617

4161341618
Support for compiling all the methods in a Vim9 class using |:defcompile|.
4161441619

41615-
Support for alternate font highlighting using |t_CF| terminal code.
41616-
4161741620
Support for Super key mappings in GTK using <D-Key>.
4161841621

4161941622
Improved visual highlighting.
4162041623

4162141624
Python3 support in OpenVMS.
4162241625

41626+
Support |fuzzy-matching| during |ins-completion| with "fuzzy" item for 'completeopt'
41627+
4162341628
==============================================================================
41624-
COMPILE TIME CHANGES *compile-changes-9.2*
41629+
COMPILE TIME CHANGES *compile-changes-9.2*
4162541630

4162641631
Support for building with Ruby 3.3.
4162741632

src/insexpand.c

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct compl_S
113113
// cp_flags has CP_FREE_FNAME
114114
int cp_flags; // CP_ values
115115
int cp_number; // sequence number
116+
int cp_score; // fuzzy match score
116117
};
117118

118119
// values for cp_flags
@@ -153,6 +154,7 @@ static int compl_no_select = FALSE; // FALSE: select & insert
153154
// TRUE: noselect
154155
static int compl_longest = FALSE; // FALSE: insert full match
155156
// TRUE: insert longest prefix
157+
static int compl_fuzzy_match = FALSE; // True: fuzzy match enabled
156158

157159
// Selected one of the matches. When FALSE the match was edited or using the
158160
// longest common string.
@@ -207,6 +209,8 @@ static int compl_cont_status = 0;
207209
static int compl_opt_refresh_always = FALSE;
208210
static int compl_opt_suppress_empty = FALSE;
209211

212+
static int compl_selected_item = -1;
213+
210214
static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, typval_T *user_data, int cdir, int flags, int adup);
211215
static void ins_compl_longest_match(compl_T *match);
212216
static void ins_compl_del_pum(void);
@@ -1059,12 +1063,15 @@ completeopt_was_set(void)
10591063
compl_no_insert = FALSE;
10601064
compl_no_select = FALSE;
10611065
compl_longest = FALSE;
1066+
compl_fuzzy_match = FALSE;
10621067
if (strstr((char *)p_cot, "noselect") != NULL)
10631068
compl_no_select = TRUE;
10641069
if (strstr((char *)p_cot, "noinsert") != NULL)
10651070
compl_no_insert = TRUE;
10661071
if (strstr((char *)p_cot, "longest") != NULL)
10671072
compl_longest = TRUE;
1073+
if (strstr((char *)p_cot, "fuzzy") != NULL)
1074+
compl_fuzzy_match = TRUE;
10681075
}
10691076

10701077

@@ -1212,6 +1219,17 @@ trigger_complete_changed_event(int cur)
12121219
}
12131220
#endif
12141221

1222+
/*
1223+
* pumitem qsort compare func
1224+
*/
1225+
static int
1226+
ins_compl_fuzzy_sort(const void *a, const void *b)
1227+
{
1228+
const int sa = (*(pumitem_T *)a).pum_score;
1229+
const int sb = (*(pumitem_T *)b).pum_score;
1230+
return sa == sb ? 0 : sa < sb ? 1 : -1;
1231+
}
1232+
12151233
/*
12161234
* Build a popup menu to show the completion matches.
12171235
* Returns the popup menu entry that should be selected. Returns -1 if nothing
@@ -1227,6 +1245,7 @@ ins_compl_build_pum(void)
12271245
int i;
12281246
int cur = -1;
12291247
int lead_len = 0;
1248+
int max_fuzzy_score = 0;
12301249

12311250
// Need to build the popup menu list.
12321251
compl_match_arraysize = 0;
@@ -1236,9 +1255,15 @@ ins_compl_build_pum(void)
12361255

12371256
do
12381257
{
1258+
// when completeopt include fuzzy option and leader is not null or empty
1259+
// set the cp_score for after compare.
1260+
if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
1261+
compl->cp_score = fuzzy_match_str(compl->cp_str, compl_leader);
1262+
12391263
if (!match_at_original_text(compl)
12401264
&& (compl_leader == NULL
1241-
|| ins_compl_equal(compl, compl_leader, lead_len)))
1265+
|| ins_compl_equal(compl, compl_leader, lead_len)
1266+
|| (compl_fuzzy_match && compl->cp_score > 0)))
12421267
++compl_match_arraysize;
12431268
compl = compl->cp_next;
12441269
} while (compl != NULL && !is_first_match(compl));
@@ -1267,9 +1292,10 @@ ins_compl_build_pum(void)
12671292
{
12681293
if (!match_at_original_text(compl)
12691294
&& (compl_leader == NULL
1270-
|| ins_compl_equal(compl, compl_leader, lead_len)))
1295+
|| ins_compl_equal(compl, compl_leader, lead_len)
1296+
|| (compl_fuzzy_match && compl->cp_score > 0)))
12711297
{
1272-
if (!shown_match_ok)
1298+
if (!shown_match_ok && !compl_fuzzy_match)
12731299
{
12741300
if (compl == compl_shown_match || did_find_shown_match)
12751301
{
@@ -1285,6 +1311,24 @@ ins_compl_build_pum(void)
12851311
shown_compl = compl;
12861312
cur = i;
12871313
}
1314+
else if (compl_fuzzy_match)
1315+
{
1316+
if (compl->cp_score > max_fuzzy_score)
1317+
{
1318+
did_find_shown_match = TRUE;
1319+
max_fuzzy_score = compl->cp_score;
1320+
compl_shown_match = compl;
1321+
shown_match_ok = TRUE;
1322+
}
1323+
1324+
if (!compl_no_select
1325+
&& (max_fuzzy_score > 0
1326+
|| (compl_leader == NULL || lead_len == 0)))
1327+
{
1328+
shown_match_ok = TRUE;
1329+
cur = 0;
1330+
}
1331+
}
12881332

12891333
if (compl->cp_text[CPT_ABBR] != NULL)
12901334
compl_match_array[i].pum_text =
@@ -1293,14 +1337,15 @@ ins_compl_build_pum(void)
12931337
compl_match_array[i].pum_text = compl->cp_str;
12941338
compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
12951339
compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
1340+
compl_match_array[i].pum_score = compl->cp_score;
12961341
if (compl->cp_text[CPT_MENU] != NULL)
12971342
compl_match_array[i++].pum_extra =
12981343
compl->cp_text[CPT_MENU];
12991344
else
13001345
compl_match_array[i++].pum_extra = compl->cp_fname;
13011346
}
13021347

1303-
if (compl == compl_shown_match)
1348+
if (compl == compl_shown_match && !compl_fuzzy_match)
13041349
{
13051350
did_find_shown_match = TRUE;
13061351

@@ -1320,6 +1365,10 @@ ins_compl_build_pum(void)
13201365
compl = compl->cp_next;
13211366
} while (compl != NULL && !is_first_match(compl));
13221367

1368+
if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
1369+
// sort by the largest score of fuzzy match
1370+
qsort(compl_match_array, (size_t)compl_match_arraysize, sizeof(pumitem_T), ins_compl_fuzzy_sort);
1371+
13231372
if (!shown_match_ok) // no displayed match at all
13241373
cur = -1;
13251374

@@ -1376,6 +1425,7 @@ ins_compl_show_pum(void)
13761425
// Use the cursor to get all wrapping and other settings right.
13771426
col = curwin->w_cursor.col;
13781427
curwin->w_cursor.col = compl_col;
1428+
compl_selected_item = cur;
13791429
pum_display(compl_match_array, compl_match_arraysize, cur);
13801430
curwin->w_cursor.col = col;
13811431

@@ -4025,6 +4075,40 @@ ins_compl_show_filename(void)
40254075
redraw_cmdline = FALSE; // don't overwrite!
40264076
}
40274077

4078+
static compl_T *
4079+
find_comp_when_fuzzy(void)
4080+
{
4081+
int score;
4082+
char_u* str;
4083+
int target_idx = -1;
4084+
int is_forward = compl_shows_dir_forward();
4085+
int is_backward = compl_shows_dir_backward();
4086+
compl_T *comp = NULL;
4087+
4088+
if (compl_match_array == NULL ||
4089+
(is_forward && compl_selected_item == compl_match_arraysize - 1)
4090+
|| (is_backward && compl_selected_item == 0))
4091+
return compl_first_match;
4092+
4093+
if (is_forward)
4094+
target_idx = compl_selected_item + 1;
4095+
else if (is_backward)
4096+
target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1
4097+
: compl_selected_item - 1;
4098+
4099+
score = compl_match_array[target_idx].pum_score;
4100+
str = compl_match_array[target_idx].pum_text;
4101+
4102+
comp = compl_first_match;
4103+
do {
4104+
if (comp->cp_score == score && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR]))
4105+
return comp;
4106+
comp = comp->cp_next;
4107+
} while (comp != NULL && !is_first_match(comp));
4108+
4109+
return NULL;
4110+
}
4111+
40284112
/*
40294113
* Find the next set of matches for completion. Repeat the completion "todo"
40304114
* times. The number of matches found is returned in 'num_matches'.
@@ -4052,7 +4136,8 @@ find_next_completion_match(
40524136
{
40534137
if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL)
40544138
{
4055-
compl_shown_match = compl_shown_match->cp_next;
4139+
compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_next
4140+
: find_comp_when_fuzzy();
40564141
found_end = (compl_first_match != NULL
40574142
&& (is_first_match(compl_shown_match->cp_next)
40584143
|| is_first_match(compl_shown_match)));
@@ -4061,7 +4146,8 @@ find_next_completion_match(
40614146
&& compl_shown_match->cp_prev != NULL)
40624147
{
40634148
found_end = is_first_match(compl_shown_match);
4064-
compl_shown_match = compl_shown_match->cp_prev;
4149+
compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_prev
4150+
: find_comp_when_fuzzy();
40654151
found_end |= is_first_match(compl_shown_match);
40664152
}
40674153
else
@@ -4111,7 +4197,8 @@ find_next_completion_match(
41114197
if (!match_at_original_text(compl_shown_match)
41124198
&& compl_leader != NULL
41134199
&& !ins_compl_equal(compl_shown_match,
4114-
compl_leader, (int)STRLEN(compl_leader)))
4200+
compl_leader, (int)STRLEN(compl_leader))
4201+
&& !(compl_fuzzy_match && compl_shown_match->cp_score > 0))
41154202
++todo;
41164203
else
41174204
// Remember a matching item.
@@ -4167,7 +4254,9 @@ ins_compl_next(
41674254
if (compl_shown_match == NULL)
41684255
return -1;
41694256

4170-
if (compl_leader != NULL && !match_at_original_text(compl_shown_match))
4257+
if (compl_leader != NULL
4258+
&& !match_at_original_text(compl_shown_match)
4259+
&& !compl_fuzzy_match)
41714260
// Update "compl_shown_match" to the actually shown match
41724261
ins_compl_update_shown_match();
41734262

0 commit comments

Comments
 (0)