blink.cmp icon indicating copy to clipboard operation
blink.cmp copied to clipboard

Ability to scroll list by count?

Open ibhagwan opened this issue 11 months ago • 6 comments

Feature Description

Ty @Saghen for this wonderful plugin!

I might be missing something but I also looked at the code and couldn't find an equivalent for documentation scroll up|down for the item list.

The use case is using <C-b>|<C-f> to scroll the item list back/forward, perhaps something like #382 but for select_{next|prev}?

keymap = {
  ['<C-b>'] = { function(cmp) cmp.select_prev({ count = 10 }) end },
  ['<C-f>'] = { function(cmp) cmp.select_next({ count = 10 }) end },

ibhagwan avatar Dec 15 '24 09:12 ibhagwan

quick workaround

local function select_next_idx(idx, dir)
  dir = dir or 1

  local list = require "blink.cmp.completion.list"
  if #list.items == 0 then
    return
  end

  local target_idx
  -- haven't selected anything yet
  if list.selected_item_idx == nil then
    if dir == 1 then
      target_idx = idx
    else
      target_idx = #list.items - idx
    end
  elseif list.selected_item_idx == #list.items then
    if dir == 1 then
      target_idx = 1
    else
      target_idx = #list.items - idx
    end
  elseif list.selected_item_idx == 1 and dir == -1 then
    target_idx = #list.items - idx
  else
    target_idx = list.selected_item_idx + (idx * dir)
  end

  -- clamp
  if target_idx < 1 then
    target_idx = 1
  elseif target_idx > #list.items then
    target_idx = #list.items
  end

  list.select(target_idx)
end
keymap = {
  ["<PageDown>"] = {
    function(cmp)
      if not cmp.is_visible() then
        return
      end
      vim.schedule(function()
        select_next_idx(3)
      end)
      return true
    end,
    "fallback",
  },
  ["<PageUp>"] = {
    function(cmp)
      if not cmp.is_visible() then
        return
      end
      vim.schedule(function()
        select_next_idx(3, -1)
      end)
      return true
    end,
    "fallback",
  },
}

iurimateus avatar Dec 15 '24 13:12 iurimateus

Would you expect this to respect completion.list.cycle?

saghen avatar Dec 17 '24 18:12 saghen

Would you expect this to respect completion.list.cycle?

I'm not sure, either way is fine, your call :-)

ibhagwan avatar Dec 17 '24 19:12 ibhagwan

Would you expect this to respect completion.list.cycle?

I believe the more sane default would be to respect it, but maybe it could be a setup parameter?

brunobmello25 avatar Dec 19 '24 18:12 brunobmello25

Would you expect this to respect completion.list.cycle?

It should cycle if it's already at the end, but not to cycle if there are fewer than count but more than zero items away from the end.

ouuan avatar Apr 01 '25 03:04 ouuan

Here's an implementation I came up with. Could this be integrated into the plugin?

---@param cb fun(cmp: blink.cmp.API)
---@return fun(cmp: blink.cmp.API): boolean
local function wrap_scroll_page(cb)
  return function(cmp)
    if cmp.is_menu_visible() then
      vim.schedule(cb)
      return true
    else
      return false
    end
  end
end

local function scroll_page_up()
  local list = require('blink.cmp.completion.list')
  if #list.items == 0 or list.context == nil then
    return
  elseif list.selected_item_idx == nil then
    return list.select(#list.items)
  else
    local page_size = require('blink.cmp.completion.windows.menu').win:get_height()
    return list.select(math.max(list.selected_item_idx - page_size, 1))
  end
end

local function scroll_page_down()
  local list = require('blink.cmp.completion.list')
  if #list.items == 0 or list.context == nil then
    return
  elseif list.selected_item_idx == nil then
    return list.select(#list.items)
  else
    local page_size = require('blink.cmp.completion.windows.menu').win:get_height()
    return list.select(math.min(list.selected_item_idx + page_size, #list.items))
  end
end

require('blink.cmp').setup({
  keymap = {
    ['<C-u>'] = { wrap_scroll_page(scroll_page_up), 'fallback' },
    ['<C-d>'] = { wrap_scroll_page(scroll_page_down), 'fallback' },
  },
})

dmitmel avatar Apr 27 '25 09:04 dmitmel