Skip to content

Commit

Permalink
feat: support dynamic selection mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Saghen committed Dec 23, 2024
1 parent f7bef25 commit c1017f0
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 8 deletions.
8 changes: 8 additions & 0 deletions docs/configuration/completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ TODO: Find a case where this actually fires : )

Manages the completion list and its behavior when selecting items. The most commonly changed option is `completion.list.selection`, which controls whether the list will automatically select the first item in the list, and whether selection shows a preview:

To control the selection behavior per mode, pass a function to `completion.list.selection` that returns the selection mode:

```lua
completion.list.selection = function(ctx)
return ctx.mode == 'cmdline' and 'auto_insert' or 'preselect'
end
```

:::tabs
== Preselect
Selects the first item automatically
Expand Down
4 changes: 4 additions & 0 deletions docs/configuration/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Blink cmp has *a lot* of configuration options, the following code block highlig

For more common configurations, see the [recipes](../recipes.md).

> [!IMPORTANT] Do not copy this entire configuration! It contains only non-default options
```lua
{
-- Disable for some filetypes
Expand All @@ -25,6 +27,8 @@ For more common configurations, see the [recipes](../recipes.md).

-- Insert completion item on selection, don't select by default
list = { selection = 'auto_insert' },
-- or set per mode
list = { selection = function(ctx) return ctx.mode == 'cmdline' and 'auto_insert' or 'preselect' end },

menu = {
-- Don't automatically show the completion menu
Expand Down
3 changes: 3 additions & 0 deletions docs/configuration/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ completion.trigger = {
completion.list = {
-- Maximum number of items to display
max_items = 200,

-- Controls if completion items will be selected automatically,
-- and whether selection automatically inserts
selection = 'preselect',
-- selection = function(ctx) return ctx.mode == 'cmdline' and 'auto_insert' or 'preselect' end,

-- Controls how the completion items are selected
-- 'preselect' will automatically select the first item in the completion list
-- 'manual' will not select any item by default
Expand Down
13 changes: 12 additions & 1 deletion docs/recipes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Recipes


## Disable per filetype

```lua
Expand All @@ -21,6 +20,18 @@ completion = {
signature = { window = { border = 'single' } },
```

## Change selection mode per mode

```lua
completion = {
list = {
selection = function(ctx)
return ctx.mode == 'cmdline' and 'auto_insert' or 'preselect'
end
}
}
```

## Select Nth item from the list

Here's an example configuration that allows you to select the nth item from the list, based on [#382](https://github.com/Saghen/blink.cmp/issues/382):
Expand Down
19 changes: 14 additions & 5 deletions lua/blink/cmp/completion/list.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
--- @field hide fun()
---
--- @field get_selected_item fun(): blink.cmp.CompletionItem?
--- @field get_selection_mode fun(context: blink.cmp.Context): blink.cmp.CompletionListSelection
--- @field select fun(idx?: number, opts?: { undo_preview?: boolean, is_explicit_selection?: boolean })
--- @field select_next fun()
--- @field select_prev fun()
Expand Down Expand Up @@ -57,7 +58,7 @@ local list = {
config = require('blink.cmp.config').completion.list,
context = nil,
items = {},
selected_item_idx = nil,
selection_mode = nil,
is_explicitly_selected = false,
preview_undo = nil,
}
Expand All @@ -83,6 +84,7 @@ function list.show(context, items_by_source)
-- update the context/list and emit
list.context = context
list.items = list.fuzzy(context, items_by_source)
list.selection_mode = list.get_selection_mode(list.context)

if #list.items == 0 then
list.hide_emitter:emit({ context = context })
Expand All @@ -98,7 +100,7 @@ function list.show(context, items_by_source)
-- otherwise, use the default selection
else
list.select(
list.config.selection == 'preselect' and 1 or nil,
list.selection_mode == 'preselect' and 1 or nil,
{ undo_preview = false, is_explicit_selection = false }
)
end
Expand All @@ -121,14 +123,21 @@ function list.hide() list.hide_emitter:emit({ context = list.context }) end

function list.get_selected_item() return list.items[list.selected_item_idx] end

function list.get_selection_mode(context)
assert(context ~= nil, 'Context must be set before getting selection mode')
if type(list.config.selection) == 'function' then return list.config.selection(context) end
--- @diagnostic disable-next-line: return-type-mismatch
return list.config.selection
end

function list.select(idx, opts)
opts = opts or {}
local item = list.items[idx]

require('blink.cmp.completion.trigger').suppress_events_for_callback(function()
-- default to undoing the preview
if opts.undo_preview ~= false then list.undo_preview() end
if list.config.selection == 'auto_insert' and item then list.apply_preview(item) end
if list.selection_mode == 'auto_insert' and item then list.apply_preview(item) end
end)

--- @diagnostic disable-next-line: assign-type-mismatch
Expand All @@ -149,7 +158,7 @@ function list.select_next()
if not list.config.cycle.from_bottom then return end

-- preselect is not enabled, we go back to no selection
if list.config.selection ~= 'preselect' then return list.select(nil) end
if list.selection_mode ~= 'preselect' then return list.select(nil) end

-- otherwise, we cycle around
return list.select(1)
Expand All @@ -171,7 +180,7 @@ function list.select_prev()
if not list.config.cycle.from_top then return end

-- auto_insert is enabled, we go back to no selection
if list.config.selection == 'auto_insert' then return list.select(nil) end
if list.selection_mode == 'auto_insert' then return list.select(nil) end

-- otherwise, we cycle around
return list.select(#list.items)
Expand Down
7 changes: 5 additions & 2 deletions lua/blink/cmp/config/completion/list.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
--- @class (exact) blink.cmp.CompletionListConfig
--- @field max_items number Maximum number of items to display
--- @field selection blink.cmp.CompletionListSelection Controls if completion items will be selected automatically, and whether selection automatically inserts
--- @field selection blink.cmp.CompletionListSelection | fun(ctx: blink.cmp.Context): blink.cmp.CompletionListSelection Controls if completion items will be selected automatically, and whether selection automatically inserts
--- @field cycle blink.cmp.CompletionListCycleConfig

--- @alias blink.cmp.CompletionListSelection
Expand Down Expand Up @@ -30,7 +30,10 @@ function list.validate(config)
max_items = { config.max_items, 'number' },
selection = {
config.selection,
function() return vim.tbl_contains({ 'preselect', 'manual', 'auto_insert' }, config.selection) end,
function()
return vim.tbl_contains({ 'preselect', 'manual', 'auto_insert' }, config.selection)
or type(config.selection) == 'function'
end,
'one of: preselect, manual, auto_insert',
},
cycle = { config.cycle, 'table' },
Expand Down

0 comments on commit c1017f0

Please sign in to comment.