Saghen / blink.cmp

Performant, batteries-included completion plugin for Neovim
MIT License
1.37k stars 79 forks source link

New provider attribute: fallbacks #331

Open lopi-py opened 2 weeks ago

lopi-py commented 2 weeks ago

Feature Description

This feature would introduce a fallbacks field within the provider configuration to simplify the setup.

To add an external source like lazydev, you need to also tweak the lsp provider fallback_for property, but it might be more easy and intuitive to set up if we had a fallbacks field:

lazydev = {
  ...,
  fallbacks = { "lsp" },
},

Compared to

lazydev = {
  ...,
},
lsp = {
  fallback_for = { "lazydev" },
},
Saghen commented 1 week ago

Would this be in addition to the fallback_for? If not, I'm curious how we'd handle #328 with this

lopi-py commented 1 week ago

No, this would replace fallback_for.

crates = {
  ...
  -- if the `lsp` "tree" returns 0 items, we reach `othersource`, so order matters here
  fallbacks = { "lsp", "othersource" },
},

The lsp provider has buffer as a fallback, but lsp is a fallback of crates, so the priority is crates -> lsp -> buffer -> othersource. If we added lazydev and lsp as its fallback, lsp would run if crates or lazydev return 0 items, which is the current behavior

crates = {
  ...
  fallbacks = { "lsp" }, -- lsp will run only if crates has 0 items OR if lazydev has 0 items too
},
lazydev = {
  fallbacks = { "lsp" },
},

I have a feeling this is like managing dependencies (actually, this is managing dependencies 😢), but its probably worth it 😅

Saghen commented 1 week ago

actually, this is managing dependencies

:joy: Good point, it's currently defined from the bottom up, and you're proposing top down which I like much more

crates = {
  ...
  fallbacks = { "lsp" }, -- lsp will run only if crates has 0 items OR if lazydev has 0 items too
},
lazydev = {
  fallbacks = { "lsp" },
},

How would we allow the ALL case here, instead of ANY, since I think that would be the desired behavior. Would you do crates.fallbacks = { 'lazydev' } and then lazydev.fallbacks = { 'lsp' }?

I think the hypothetical we would want to support is:

crates/lazydev -- ALL -> lsp --> buffer + ripgrep

so, for that last buffer + ripgrep bit, rather than this:

crates = {
  ...
  -- if the `lsp` "tree" returns 0 items, we reach `othersource`, so order matters here
  fallbacks = { "lsp", "othersource" },
},

We could instead run all of the fallbacks listed. Also I think I like fallback_to since it's a bit clearer

lopi-py commented 1 week ago
crates = {
  fallbacks = { "lsp" },
},
lazydev = {
  fallbacks = { "lsp" },
},
lsp = {
  fallbacks = { "buffer" },
},
buffer = {
  fallbacks = { "ripgrep" },
},

To reduce complexity, we could just make it ALL. lsp would run if crates AND lazydev returns 0 items, and buffer would run if lsp returns 0 items and so on with ripgrep. aditionally we can have another field

crates = {
  fallback_when = "all"/"any",
  fallbacks = { "lsp", "buffer" },
},

but this is getting complex because if lsp doesn't return items, it will fallback to buffer and then, we call buffer too, what would be a use case for ANY?