summaryrefslogtreecommitdiff
path: root/lua/blink/cmp/config/completion/menu.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/blink/cmp/config/completion/menu.lua')
-rw-r--r--lua/blink/cmp/config/completion/menu.lua223
1 files changed, 223 insertions, 0 deletions
diff --git a/lua/blink/cmp/config/completion/menu.lua b/lua/blink/cmp/config/completion/menu.lua
new file mode 100644
index 0000000..c807566
--- /dev/null
+++ b/lua/blink/cmp/config/completion/menu.lua
@@ -0,0 +1,223 @@
+local validate = require('blink.cmp.config.utils').validate
+
+--- @class (exact) blink.cmp.CompletionMenuConfig
+--- @field enabled boolean
+--- @field min_width number
+--- @field max_height number
+--- @field border blink.cmp.WindowBorder
+--- @field winblend number
+--- @field winhighlight string
+--- @field scrolloff number Keep the cursor X lines away from the top/bottom of the window
+--- @field scrollbar boolean Note that the gutter will be disabled when border ~= 'none'
+--- @field direction_priority ("n" | "s")[] Which directions to show the window, falling back to the next direction when there's not enough space
+--- @field order blink.cmp.CompletionMenuOrderConfig TODO: implement
+--- @field auto_show boolean | fun(ctx: blink.cmp.Context, items: blink.cmp.CompletionItem[]): boolean Whether to automatically show the window when new completion items are available
+--- @field cmdline_position fun(): number[] Screen coordinates (0-indexed) of the command line
+--- @field draw blink.cmp.Draw Controls how the completion items are rendered on the popup window
+
+--- @class (exact) blink.cmp.CompletionMenuOrderConfig
+--- @field n 'top_down' | 'bottom_up'
+--- @field s 'top_down' | 'bottom_up'
+
+local window = {
+ --- @type blink.cmp.CompletionMenuConfig
+ default = {
+ enabled = true,
+ min_width = 15,
+ max_height = 10,
+ border = 'none',
+ winblend = 0,
+ winhighlight = 'Normal:BlinkCmpMenu,FloatBorder:BlinkCmpMenuBorder,CursorLine:BlinkCmpMenuSelection,Search:None',
+ -- keep the cursor X lines away from the top/bottom of the window
+ scrolloff = 2,
+ -- note that the gutter will be disabled when border ~= 'none'
+ scrollbar = true,
+ -- which directions to show the window,
+ -- falling back to the next direction when there's not enough space
+ direction_priority = { 's', 'n' },
+ -- which direction previous/next items show up
+ -- TODO: implement
+ order = { n = 'bottom_up', s = 'top_down' },
+
+ -- Whether to automatically show the window when new completion items are available
+ auto_show = true,
+
+ -- Screen coordinates of the command line
+ cmdline_position = function()
+ if vim.g.ui_cmdline_pos ~= nil then
+ local pos = vim.g.ui_cmdline_pos -- (1, 0)-indexed
+ return { pos[1] - 1, pos[2] }
+ end
+ local height = (vim.o.cmdheight == 0) and 1 or vim.o.cmdheight
+ return { vim.o.lines - height, 0 }
+ end,
+
+ -- Controls how the completion items are rendered on the popup window
+ draw = {
+ -- Aligns the keyword you've typed to a component in the menu
+ align_to = 'label', -- or 'none' to disable
+ -- Left and right padding, optionally { left, right } for different padding on each side
+ padding = 1,
+ -- Gap between columns
+ gap = 1,
+ treesitter = {}, -- Use treesitter to highlight the label text of completions from these sources
+ -- Components to render, grouped by column
+ columns = { { 'kind_icon' }, { 'label', 'label_description', gap = 1 } },
+ -- Definitions for possible components to render. Each component defines:
+ -- ellipsis: whether to add an ellipsis when truncating the text
+ -- width: control the min, max and fill behavior of the component
+ -- text function: will be called for each item
+ -- highlight function: will be called only when the line appears on screen
+ components = {
+ kind_icon = {
+ ellipsis = false,
+ text = function(ctx) return ctx.kind_icon .. ctx.icon_gap end,
+ highlight = function(ctx)
+ return require('blink.cmp.completion.windows.render.tailwind').get_hl(ctx) or ('BlinkCmpKind' .. ctx.kind)
+ end,
+ },
+
+ kind = {
+ ellipsis = false,
+ width = { fill = true },
+ text = function(ctx) return ctx.kind end,
+ highlight = function(ctx)
+ return require('blink.cmp.completion.windows.render.tailwind').get_hl(ctx) or ('BlinkCmpKind' .. ctx.kind)
+ end,
+ },
+
+ label = {
+ width = { fill = true, max = 60 },
+ text = function(ctx) return ctx.label .. ctx.label_detail end,
+ highlight = function(ctx)
+ -- label and label details
+ local label = ctx.label
+ local highlights = {
+ { 0, #label, group = ctx.deprecated and 'BlinkCmpLabelDeprecated' or 'BlinkCmpLabel' },
+ }
+ if ctx.label_detail then
+ table.insert(highlights, { #label, #label + #ctx.label_detail, group = 'BlinkCmpLabelDetail' })
+ end
+
+ if vim.list_contains(ctx.self.treesitter, ctx.source_id) then
+ -- add treesitter highlights
+ vim.list_extend(highlights, require('blink.cmp.completion.windows.render.treesitter').highlight(ctx))
+ end
+
+ -- characters matched on the label by the fuzzy matcher
+ for _, idx in ipairs(ctx.label_matched_indices) do
+ table.insert(highlights, { idx, idx + 1, group = 'BlinkCmpLabelMatch' })
+ end
+
+ return highlights
+ end,
+ },
+
+ label_description = {
+ width = { max = 30 },
+ text = function(ctx) return ctx.label_description end,
+ highlight = 'BlinkCmpLabelDescription',
+ },
+
+ source_name = {
+ width = { max = 30 },
+ -- source_name or source_id are supported
+ text = function(ctx) return ctx.source_name end,
+ highlight = 'BlinkCmpSource',
+ },
+ },
+ },
+ },
+}
+
+function window.validate(config)
+ validate('completion.menu', {
+ enabled = { config.enabled, 'boolean' },
+ min_width = { config.min_width, 'number' },
+ max_height = { config.max_height, 'number' },
+ border = { config.border, { 'string', 'table' } },
+ winblend = { config.winblend, 'number' },
+ winhighlight = { config.winhighlight, 'string' },
+ scrolloff = { config.scrolloff, 'number' },
+ scrollbar = { config.scrollbar, 'boolean' },
+ direction_priority = {
+ config.direction_priority,
+ function(direction_priority)
+ for _, direction in ipairs(direction_priority) do
+ if not vim.tbl_contains({ 'n', 's' }, direction) then return false end
+ end
+ return true
+ end,
+ 'one of: "n", "s"',
+ },
+ order = { config.order, 'table' },
+ auto_show = { config.auto_show, { 'boolean', 'function' } },
+ cmdline_position = { config.cmdline_position, 'function' },
+ draw = { config.draw, 'table' },
+ }, config)
+ validate('completion.menu.order', {
+ n = { config.order.n, { 'string', 'nil' } },
+ s = { config.order.s, { 'string', 'nil' } },
+ }, config.order)
+
+ validate('completion.menu.draw', {
+ align_to = {
+ config.draw.align_to,
+ function(align)
+ if align == 'none' or align == 'cursor' then return true end
+ for _, column in ipairs(config.draw.columns) do
+ for _, component in ipairs(column) do
+ if component == align then return true end
+ end
+ end
+ return false
+ end,
+ '"none" or one of the components defined in the "columns"',
+ },
+
+ padding = {
+ config.draw.padding,
+ function(padding)
+ if type(padding) == 'number' then return true end
+ if type(padding) ~= 'table' or #padding ~= 2 then return false end
+ if type(padding[1]) == 'number' and type(padding[2]) == 'number' then return true end
+ return false
+ end,
+ 'a number or a tuple of 2 numbers (i.e. [1, 2])',
+ },
+ gap = { config.draw.gap, 'number' },
+
+ treesitter = { config.draw.treesitter, 'table' },
+
+ columns = {
+ config.draw.columns,
+ function(columns)
+ local available_components = vim.tbl_keys(config.draw.components)
+
+ if type(columns) ~= 'table' or #columns == 0 then return false end
+ for _, column in ipairs(columns) do
+ if #column == 0 then return false end
+ for _, component in ipairs(column) do
+ if not vim.tbl_contains(available_components, component) then return false end
+ end
+ if column.gap ~= nil and type(column.gap) ~= 'number' then return false end
+ end
+ return true
+ end,
+ 'a table of tables, where each table contains a list of components and an optional gap. List of available components: '
+ .. table.concat(vim.tbl_keys(config.draw.components), ', '),
+ },
+ components = { config.draw.components, 'table' },
+ }, config.draw)
+
+ for component, definition in pairs(config.draw.components) do
+ validate('completion.menu.draw.components.' .. component, {
+ ellipsis = { definition.ellipsis, 'boolean', true },
+ width = { definition.width, 'table', true },
+ text = { definition.text, 'function' },
+ highlight = { definition.highlight, { 'string', 'function' }, true },
+ }, config.draw.components[component])
+ end
+end
+
+return window