Skip to content

feat: matched character highlighting #245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 59 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,11 +404,65 @@ MiniDeps.add({
-- 'auto_insert' will not select any item by default, and insert the completion items automatically when selecting them
selection = 'preselect',
-- Controls how the completion items are rendered on the popup window
-- 'simple' will render the item's kind icon the left alongside the label
-- 'reversed' will render the label on the left and the kind icon + name on the right
-- 'minimal' will render the label on the left and the kind name on the right
-- 'function(blink.cmp.CompletionRenderContext): blink.cmp.Component[]' for custom rendering
draw = 'simple',
draw = {
align_to_component = '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,
-- 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 .. ' ' end,
highlight = function(ctx) return 'BlinkCmpKind' .. ctx.kind end,
},

kind = {
ellipsis = false,
text = function(ctx) return ctx.kind .. ' ' end,
highlight = function(ctx) return 'BlinkCmpKind' .. ctx.kind end,
},

label = {
width = { fill = true, max = 60 },
text = function(ctx) return ctx.label .. (ctx.label_detail or '') end,
highlight = function(ctx)
-- label and label details
local highlights = {
{ 0, #ctx.label, group = ctx.deprecated and 'BlinkCmpLabelDeprecated' or 'BlinkCmpLabel' },
}
if ctx.label_detail then
table.insert(
highlights,
{ #ctx.label + 1, #ctx.label + #ctx.label_detail, group = 'BlinkCmpLabelDetail' }
)
end

-- characters matched on the label by the fuzzy matcher
if ctx.label_matched_indices ~= nil then
for _, idx in ipairs(ctx.label_matched_indices) do
table.insert(highlights, { idx, idx + 1, group = 'BlinkCmpLabelMatch' })
end
end

return highlights
end,
},

label_description = {
width = { max = 30 },
text = function(ctx) return ctx.label_description or '' end,
highlight = 'BlinkCmpLabelDescription',
},
},
},
-- 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.
Expand Down
77 changes: 69 additions & 8 deletions lua/blink/cmp/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
--- @field winblend? number
--- @field winhighlight? string
--- @field scrolloff? number
--- @field draw? 'simple' | 'reversed' | 'minimal' | blink.cmp.CompletionDrawFn
--- @field draw? blink.cmp.Draw
--- @field cycle? blink.cmp.AutocompleteConfig.CycleConfig

--- @class blink.cmp.AutocompleteConfig.CycleConfig
Expand Down Expand Up @@ -389,11 +389,65 @@ local config = {
-- 'auto_insert' will not select any item by default, and insert the completion items automatically when selecting them
selection = 'preselect',
-- Controls how the completion items are rendered on the popup window
-- 'simple' will render the item's kind icon the left alongside the label
-- 'reversed' will render the label on the left and the kind icon + name on the right
-- 'minimal' will render the label on the left and the kind name on the right
-- 'function(blink.cmp.CompletionRenderContext): blink.cmp.Component[]' for custom rendering
draw = 'simple',
draw = {
align_to_component = '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,
-- 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 .. ' ' end,
highlight = function(ctx) return 'BlinkCmpKind' .. ctx.kind end,
},

kind = {
ellipsis = false,
text = function(ctx) return ctx.kind .. ' ' end,
highlight = function(ctx) return 'BlinkCmpKind' .. ctx.kind end,
},

label = {
width = { fill = true, max = 60 },
text = function(ctx) return ctx.label .. (ctx.label_detail or '') end,
highlight = function(ctx)
-- label and label details
local highlights = {
{ 0, #ctx.label, group = ctx.deprecated and 'BlinkCmpLabelDeprecated' or 'BlinkCmpLabel' },
}
if ctx.label_detail then
table.insert(
highlights,
{ #ctx.label + 1, #ctx.label + #ctx.label_detail, group = 'BlinkCmpLabelDetail' }
)
end

-- characters matched on the label by the fuzzy matcher
if ctx.label_matched_indices ~= nil then
for _, idx in ipairs(ctx.label_matched_indices) do
table.insert(highlights, { idx, idx + 1, group = 'BlinkCmpLabelMatch' })
end
end

return highlights
end,
},

label_description = {
width = { max = 30 },
text = function(ctx) return ctx.label_description or '' end,
highlight = 'BlinkCmpLabelDescription',
},
},
},
-- 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.
Expand All @@ -405,7 +459,7 @@ local config = {
documentation = {
max_width = 80,
max_height = 20,
desired_min_width = 40,
desired_min_width = 50,
desired_min_height = 10,
border = 'padded',
winblend = 0,
Expand Down Expand Up @@ -496,6 +550,13 @@ local config = {
local M = {}

--- @param opts blink.cmp.Config
function M.merge_with(opts) config = vim.tbl_deep_extend('force', config, opts or {}) end
function M.merge_with(opts)
config = vim.tbl_deep_extend('force', config, opts or {})

-- TODO: remove on 1.0
if type(config.windows.autocomplete.draw) == 'string' or type(config.windows.autocomplete.draw) == 'function' then
error('The blink.cmp autocomplete draw has been rewritten, please see the README for the new configuration')
end
end

return setmetatable(M, { __index = function(_, k) return config[k] end })
4 changes: 3 additions & 1 deletion lua/blink/cmp/fuzzy/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ function fuzzy.access(item) fuzzy.rust.access(item) end
---@param lines string
function fuzzy.get_words(lines) return fuzzy.rust.get_words(lines) end

function fuzzy.fuzzy_matched_indices(needle, haystack) return fuzzy.rust.fuzzy_matched_indices(needle, haystack) end

---@param needle string
---@param haystack blink.cmp.CompletionItem[]?
---@return blink.cmp.CompletionItem[]
function fuzzy.filter_items(needle, haystack)
function fuzzy.fuzzy(needle, haystack)
haystack = haystack or {}

-- get the nearby words
Expand Down
14 changes: 14 additions & 0 deletions lua/blink/cmp/fuzzy/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ pub fn fuzzy(
.collect())
}

pub fn fuzzy_matched_indices(
_lua: &Lua,
(needle, haystack): (String, Vec<String>),
) -> LuaResult<Vec<Vec<usize>>> {
Ok(frizbee::match_list_for_matched_indices(
&needle,
&haystack.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
))
}

pub fn get_words(_: &Lua, text: String) -> LuaResult<Vec<String>> {
Ok(REGEX
.find_iter(&text)
Expand All @@ -84,6 +94,10 @@ pub fn get_words(_: &Lua, text: String) -> LuaResult<Vec<String>> {
fn blink_cmp_fuzzy(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("fuzzy", lua.create_function(fuzzy)?)?;
exports.set(
"fuzzy_matched_indices",
lua.create_function(fuzzy_matched_indices)?,
)?;
exports.set("get_words", lua.create_function(get_words)?)?;
exports.set("init_db", lua.create_function(init_db)?)?;
exports.set("destroy_db", lua.create_function(destroy_db)?)?;
Expand Down
2 changes: 1 addition & 1 deletion lua/blink/cmp/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ cmp.setup = function(opts)
vim.schedule(function()
if cmp.trigger.context == nil or cmp.trigger.context.id ~= context.id then return end

local filtered_items = cmp.fuzzy.filter_items(cmp.fuzzy.get_query(), items)
local filtered_items = cmp.fuzzy.fuzzy(cmp.fuzzy.get_query(), items)
filtered_items = cmp.sources.apply_max_items_for_completions(context, filtered_items)
if #filtered_items > 0 then
cmp.windows.autocomplete.open_with_items(context, filtered_items)
Expand Down
Loading