Skip to content

Add: Support conditional custom matchers - #639

Open
Prajjwal wants to merge 1 commit into
ctrlpvim:masterfrom
Prajjwal:sin.use-match-func-conditionally
Open

Prajjwal wants to merge 1 commit into
ctrlpvim:masterfrom
Prajjwal:sin.use-match-func-conditionally

Conversation

@Prajjwal

@Prajjwal Prajjwal commented Oct 3, 2026

Copy link
Copy Markdown

Ctrlp accepts an override to its built-in match func. It would be nice if custom matchers could conditionally fall back to the built-in search. It would allow for smaller, cleaner matchers that only run in some cases.

As a concrete example:

I'm in a monorepo with ~200k+ files. I'd like to register a matcher that uses fzy, but only in path mode because that is where the fzy algorithm shines. For other modes, it should fall back to ctrlp's built-in.

Implementation:

Supply an optional match_if predicate, which ctrlp uses at runtime to decide whether the matcher should run. For backwards compatibility, the matcher runs unconditionally if the predicate is absent.

fu! MatchFzy(items, str, limit, mmode, ispath, crfile, regex)
  return systemlist("fzy -e '" . a:str . "' | head -n " . a:limit, a:items)
endfu

fu! ShouldMatchFzy(items, str, limit, mmode, ispath, crfile, regex)
  return a:mmode == "full-line"
endfu

let g:ctrlp_match_func = { 'match': 'MatchFzy', 'match_if': 'ShouldMatchFzy' }

Other considerations:

An alternative approach would be to have the custom matcher return a sentinel value (as opposed to a list) that functions as a skip signal to ctrlp. I did not go that route because it seems like poor design, but I'm open to input.

The rest of the plugin is currently unaware of this, and relies on a bunch of manual checks of the form s:matcher == {} throughout. That's a larger refactor, though, and I'll wait until there's interest in merging this before implementing.

TODO:

  • Add docs.
  • Make the rest of the plugin aware of when a custom matcher was used.

Ctrlp accepts an override to its built-in match func. It would be nice
if custom matchers could conditionally fall back to the built-in search.
It would allow for smaller, cleaner matchers that only run in some
cases.

As a concrete example:

I'm in a monorepo with ~200k+ files. I'd like to register a matcher that
uses [fzy](https://github.com/Prajjwal/ctrlp.vim.git), but only in
`path` mode because that is where the fzy algorithm shines. For other
modes, it should fall back to ctrlp's built-in.

**Implementation:**

Supply an optional `match_if` predicate, which ctrlp uses at runtime to
decide whether the matcher should run. For backwards compatibility, the
matcher runs unconditionally if the predicate is absent.

```vimscript
fu! MatchFzy(items, str, limit, mmode, ispath, crfile, regex)
  return systemlist("fzy -e '" . a:str . "' | head -n " . a:limit, a:items)
endfu

fu! ShouldMatchFzy(items, str, limit, mmode, ispath, crfile, regex)
  return a:mmode == "full-line"
endfu

let g:ctrlp_match_func = { 'match': 'MatchFzy', 'match_if': 'ShouldMatchFzy' }
```

**Other considerations:**

An alternative approach would be to have the custom matcher return a
sentinel value (as opposed to a list) that functions as a `skip` signal
to ctrlp. I did not go that route because it seems like poor design, but
I'm open to input.

The rest of the plugin is currently unaware of this, and relies on a
bunch of manual checks of the form `s:matcher == {}` throughout. That's
a slightly larger refactor, though, and I'll wait until there's interest
in merging this before implementing.
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

Successfully merging this pull request may close these issues.

1 participant