Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzy matching conundrum #678

Open
TheLeoP opened this issue Jan 9, 2025 · 0 comments
Open

Fuzzy matching conundrum #678

TheLeoP opened this issue Jan 9, 2025 · 0 comments

Comments

@TheLeoP
Copy link
Contributor

TheLeoP commented Jan 9, 2025

The problem

I encountered this issue while using emmet-language-server. If I write

ul>li.item$*

with defaults settings, I get an snippet completion candidate that expands the snippet. However, if I write

ul>li.item$*5

I don't get anything.

I dug into the code and play with the configuration and found that the reason for this was fuzzy_cutoff (unifying_chars could technically also help, but keep reading), if I set it to 0, I get the expected completion result in both cases. This happens because

cword = cword_before(
match.unifying_chars,
lower=True,
context=context,
sort_by=sort_by,
)

In the first case, cword is an empty string. Since * is not isalnum and isn't in an unifyin_char (from is_word), the fuzzy matching is performed against context.l_words_before. Again, since * is a symbol because of is_word, context.l_words_before is empty. This makes ratio 1 and is passes the default fuzzy_cutoff value

ratio = multi_set_ratio(
cword,
lower(sort_by),
look_ahead=match.look_ahead,
)

In the second case, cword is 5 because 5 is isalnum. This makes ratio 0, so it doesn't pass the default fuzzy_cutoff value.

unyfing_chars

"But, that's what unyfing_chars is for, right?" I thought. For most symbols, yes. If I add all of the symbols used in the emmet syntax to unyfing_chars I always get the expected snippet completion. The problem is that said symbols contain things like ., which isn't a problem for html, but it is for other languages.

For example, lua_ls (when configured to use the Neovim runtime libraries) gives two completion candidates for

vim.keymap.

both are snippets for the functions set and del.

With the default values for unyfing_chars, set and del are matched against an empty string (because . is a symbol, but vim.keymap.set starts with a char that is is_word, so it returns context.l_words_before, which is empty), so ratio is 1. They are shown.

With the modified values for unyfing_chars, set and del are matched against vim.keymap, the ratio is 0. They aren't shown.

Proposed solutions

filetype specific configurations

Cons:

  • Could get cumbersome on the plugin side
  • Still won't work for tsx/jsx unless some kind of autocmd changes the configuration dinamically depending on the context on the user side

modify what the fuzzy matching is made against

Cons:

  • I can't think of a better alternative that won't also have its own limitations. In general, I thinks this is a hard problem without some kind of per-language configuration

allow manual triggered to (maybe optionally) skip fuzzy_cutoff check

Changing

use = ratio >= match.fuzzy_cutoff and (
isinstance(comp.primary_edit, SnippetEdit)
or bool(comp.secondary_edits)
or bool(comp.extern)
or not cword.startswith(comp.primary_edit.new_text)
)

to

        use = (
            ratio >= match.fuzzy_cutoff
            and (
                isinstance(comp.primary_edit, SnippetEdit)
                or bool(comp.secondary_edits)
                or bool(comp.extern)
                or not cword.startswith(comp.primary_edit.new_text)
            )
            or context.manual
        )

would allow users to skip this kind of check manually.

possible workarounds

Using something like

ul>li.item$*5.

doesn't changes the result from the language server and (incidentally) makes coq show the completion because . makes it match against an empty string (so ratio is 1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant