diff --git a/README.md b/README.md index b9570b5f..8db2916a 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,13 @@ For LazyVim/distro users, you can disable nvim-cmp via: -- 'reversed' will render the label on the left and the kind icon + name on the right -- 'function(blink.cmp.CompletionRenderContext): blink.cmp.Component[]' for custom rendering draw = 'simple', + -- Controls the cycling behavior when reaching the beginning or end of the completion list. + cycle = { + -- When `true`, calling `select_next` at the *bottom* of the completion list will select the *first* completion item. + from_bottom = true, + -- When `true`, calling `select_prev` at the *top* of the completion list will select the *last* completion item. + from_top = true + }, }, documentation = { min_width = 10, diff --git a/lua/blink/cmp/config.lua b/lua/blink/cmp/config.lua index 3d9355d6..318e25cd 100644 --- a/lua/blink/cmp/config.lua +++ b/lua/blink/cmp/config.lua @@ -87,6 +87,11 @@ --- @field winhighlight string --- @field scrolloff number --- @field draw 'simple' | 'reversed' | function(blink.cmp.CompletionRenderContext): blink.cmp.Component[] +--- @field cycle blink.cmp.AutocompleteConfig.CycleConfig + +--- @class blink.cmp.AutocompleteConfig.CycleConfig +--- @field from_bottom boolean When `true`, calling `select_next` at the *bottom* of the completion list will select the *first* completion item. +--- @field from_top boolean When `true`, calling `select_prev` at the *top* of the completion list will select the *last* completion item. --- @class blink.cmp.DocumentationDirectionPriorityConfig --- @field autocomplete_north ("n" | "s" | "e" | "w")[] @@ -233,6 +238,10 @@ local config = { -- 'reversed' will render the label on the left and the kind icon + name on the right -- 'function(blink.cmp.CompletionRenderContext): blink.cmp.Component[]' for custom rendering draw = 'simple', + cycle = { + from_bottom = true, + from_top = true, + }, }, documentation = { min_width = 10, diff --git a/lua/blink/cmp/windows/autocomplete.lua b/lua/blink/cmp/windows/autocomplete.lua index 41008dfa..06d84591 100644 --- a/lua/blink/cmp/windows/autocomplete.lua +++ b/lua/blink/cmp/windows/autocomplete.lua @@ -138,20 +138,41 @@ function autocomplete.select_next() local current_line = vim.api.nvim_win_get_cursor(autocomplete.win:get_win())[1] local line_count = vim.api.nvim_buf_line_count(autocomplete.win:get_buf()) - if current_line == line_count then return end - - vim.api.nvim_win_set_cursor(autocomplete.win:get_win(), { current_line + 1, 0 }) - autocomplete.event_targets.on_select(autocomplete.get_selected_item(), autocomplete.context) + local cycle_from_bottom = config.windows.autocomplete.cycle.from_bottom + local is_last_completion = current_line == line_count + + -- at the end of completion list and the config is not enabled: do nothing + if is_last_completion and not cycle_from_bottom then return end + if is_last_completion then + -- at the end of completion list and the config is enabled: cycle back to first completion + vim.api.nvim_win_set_cursor(autocomplete.win:get_win(), { 1, 0 }) + autocomplete.event_targets.on_select(autocomplete.get_selected_item(), autocomplete.context) + else + -- select next completion + vim.api.nvim_win_set_cursor(autocomplete.win:get_win(), { current_line + 1, 0 }) + autocomplete.event_targets.on_select(autocomplete.get_selected_item(), autocomplete.context) + end end function autocomplete.select_prev() if not autocomplete.win:is_open() then return end local current_line = vim.api.nvim_win_get_cursor(autocomplete.win:get_win())[1] - if current_line == 1 then return end - - vim.api.nvim_win_set_cursor(autocomplete.win:get_win(), { math.max(current_line - 1, 1), 0 }) - autocomplete.event_targets.on_select(autocomplete.get_selected_item(), autocomplete.context) + local line_count = vim.api.nvim_buf_line_count(autocomplete.win:get_buf()) + local cycle_from_top = config.windows.autocomplete.cycle.from_top + local is_first_completion = current_line == 1 + + -- at the beginning of completion list and the config is not enabled: do nothing + if is_first_completion and not cycle_from_top then return end + if is_first_completion then + -- at the beginning of completion list and the config is enabled: cycle back to last completion + vim.api.nvim_win_set_cursor(autocomplete.win:get_win(), { line_count, 0 }) + autocomplete.event_targets.on_select(autocomplete.get_selected_item(), autocomplete.context) + else + -- select previous completion + vim.api.nvim_win_set_cursor(autocomplete.win:get_win(), { math.max(current_line - 1, 1), 0 }) + autocomplete.event_targets.on_select(autocomplete.get_selected_item(), autocomplete.context) + end end function autocomplete.listen_on_select(callback) autocomplete.event_targets.on_select = callback end