feat(nvim): phase 3 — replace nvim-cmp with blink.cmp and telescope with snacks.picker

Swap the completion engine to blink.cmp v1.x, dropping nvim-cmp,
its eight cmp-* sources, lspkind, and LuaSnip. Swap the picker
to snacks.picker, removing telescope.nvim and six telescope
extensions. LSP capabilities now sourced from blink.cmp; LSP
navigation keymaps and the colorscheme/keymaps/todo pickers move
to Snacks.picker. friendly-snippets is preserved as blink's
snippet source.
This commit is contained in:
David Ibia
2026-05-02 21:25:57 +01:00
parent 58db15c1c0
commit 81662b99b5
14 changed files with 81 additions and 449 deletions
+14
View File
@@ -0,0 +1,14 @@
require("blink.cmp").setup({
keymap = { preset = "default" },
appearance = {
nerd_font_variant = "mono",
},
sources = {
default = { "lsp", "path", "snippets", "buffer" },
},
cmdline = {
keymap = { preset = "cmdline" },
completion = { menu = { auto_show = true } },
},
signature = { enabled = true },
})
+5 -6
View File
@@ -1,6 +1,5 @@
-- Setup language servers.
local conform = require("conform")
local cmp_nvim_lsp = require("cmp_nvim_lsp")
local opts = { noremap = true, silent = true }
@@ -17,13 +16,13 @@ local on_attach = function(client, bufnr)
vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
opts.desc = "Go to definitions"
vim.keymap.set("n", "gd", "<cmd>Telescope lsp_definitions<CR>", opts)
vim.keymap.set("n", "gd", function() Snacks.picker.lsp_definitions() end, opts)
opts.desc = "Show documentation for what is under cursor"
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
opts.desc = "Show LSP Implementation"
vim.keymap.set("n", "gi", "<cmd>Telescope lsp_implementations<CR>", opts)
vim.keymap.set("n", "gi", function() Snacks.picker.lsp_implementations() end, opts)
opts.desc = "Get Help"
vim.keymap.set("n", "<leader>gh", vim.lsp.buf.signature_help, opts)
@@ -35,7 +34,7 @@ local on_attach = function(client, bufnr)
-- end, opts)
opts.desc = "Show LSP type definitions"
vim.keymap.set("n", "gt", "<cmd>Telescope lsp_type_definitions<CR>", opts)
vim.keymap.set("n", "gt", function() Snacks.picker.lsp_type_definitions() end, opts)
opts.desc = "Smart rename"
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
@@ -44,7 +43,7 @@ local on_attach = function(client, bufnr)
vim.keymap.set({ "n", "v" }, "<leader>ca", vim.lsp.buf.code_action, opts)
opts.desc = "Show LSP references"
vim.keymap.set("n", "<leader>cr", "<cmd>Telescope lsp_references<CR>", opts)
vim.keymap.set("n", "<leader>cr", function() Snacks.picker.lsp_references() end, opts)
opts.desc = "Format File"
vim.keymap.set({ "n", "v" }, "<leader>f", function()
@@ -67,7 +66,7 @@ local on_attach = function(client, bufnr)
end
end
local capabilities = cmp_nvim_lsp.default_capabilities()
local capabilities = require("blink.cmp").get_lsp_capabilities()
-- Configure diagnostic signs (Neovim 0.10+ API; required in 0.12)
vim.diagnostic.config({
-72
View File
@@ -1,72 +0,0 @@
local cmp = require("cmp")
local types = require("cmp.types")
local luasnip = require("luasnip")
local lspkind = require("lspkind")
-- loads vscode style snippets from installed plugins (e.g. friendly-snippets)
require("luasnip.loaders.from_vscode").lazy_load()
cmp.setup({
completion = {
completeopt = "menu,menuone,preview,noinsert,noselect",
autocomplete = { types.cmp.TriggerEvent.TextChanged },
},
window = {
documentation = {
border = { "┌", "─", "┐", "│", "┘", "─", "└", "│" },
winhighlight = "Normal:CmpPmenu,FloatBorder:CmpPmenuBorder,CursorLine:PmenuSel,Search:None",
},
completion = {
side_padding = 1,
border = { "┌", "─", "┐", "│", "┘", "─", "└", "│" },
winhighlight = "Normal:CmpPmenu,FloatBorder:CmpPmenuBorder,CursorLine:PmenuSel,Search:None",
scrollbar = false,
},
},
snippet = { -- configure how nvim-cmp interacts with snippet engine
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
["<C-.>"] = cmp.mapping.complete(), -- show completion suggestions
["C-<BS>"] = cmp.mapping.abort(), -- close completion menu
["<CR>"] = cmp.mapping.confirm({ select = true }), -- accept current selection
}),
-- sources for autocompletion
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "codecompanion" }, -- code companion for AI-assisted coding
{ name = "luasnip" }, -- snippets
{ name = "buffer" }, -- text within current buffer
{ name = "path" }, -- file system paths,
{ name = "nvim_lsp_signature_help" },
}),
-- configure lspkind for vs-code like pictograms in completion menu
formatting = {
format = lspkind.cmp_format({
maxwidth = 50,
ellipsis_char = "...",
}),
},
})
-- Use buffer source for `/` and `?` (if you enabled `native_menu`, this won't work anymore).
cmp.setup.cmdline({ "/", "?" }, {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = "buffer" },
},
})
-- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = "path" },
}, {
{ name = "cmdline" },
}),
matching = { disallow_symbol_nonprefix_matching = false },
})
+33
View File
@@ -0,0 +1,33 @@
local map = vim.keymap.set
local function with_desc(desc)
return { noremap = true, silent = true, desc = desc }
end
map("n", "<C-p>", function()
Snacks.picker.files()
end, with_desc("Find files"))
map("n", "<leader>gf", function()
Snacks.picker.git_files()
end, with_desc("Find git files"))
map("n", "<leader>pr", function()
Snacks.picker.recent()
end, with_desc("Recent files"))
map("n", "<leader>ps", function()
Snacks.picker.grep()
end, with_desc("Live grep"))
map("n", "<leader>sb", function()
Snacks.picker.grep_word()
end, with_desc("Grep word under cursor"))
map("n", "<leader>pf", function()
Snacks.picker.explorer()
end, with_desc("File explorer (cwd)"))
map("n", "<leader>cf", function()
Snacks.picker.explorer({ cwd = vim.fn.expand("%:p:h") })
end, with_desc("File explorer (current file dir)"))
-182
View File
@@ -1,182 +0,0 @@
local telescope = require("telescope")
local builtin = require("telescope.builtin")
local actions = require("telescope.actions")
local trouble = require("trouble.sources.telescope")
local file_browser_actions = require("telescope._extensions.file_browser.actions")
local live_grep_shortcuts = require("telescope-live-grep-args.shortcuts")
local live_grep_actions = require("telescope-live-grep-args.actions")
local image_preview = require("absolute.utils.telescope_image_preview")
-- local function chafa_previewer_mime_hook(filepath, bufnr, opts)
-- local is_image = function(current_filepath)
-- local image_extensions = { "png", "jpg", "jpeg", "gif" } -- Supported image formats
-- local split_path = vim.split(current_filepath:lower(), ".", { plain = true })
-- local extension = split_path[#split_path]
-- return vim.tbl_contains(image_extensions, extension)
-- end
-- if is_image(filepath) then
-- local term = vim.api.nvim_open_term(bufnr, {})
-- local function send_output(_, data, _)
-- for _, d in ipairs(data) do
-- vim.api.nvim_chan_send(term, d .. "\r\n")
-- end
-- end
-- vim.fn.jobstart("chafa --passthrough tmux --exact-size off --format=symbols " .. filepath, {
-- on_stdout = send_output,
-- stdout_buffered = true,
-- })
-- else
-- require("telescope.previewers.utils").set_preview_message(bufnr, opts.winid, "Binary cannot be previewed")
-- end
-- end
telescope.setup({
defaults = {
file_previewer = image_preview.file_previewer,
buffer_previewer_maker = image_preview.buffer_previewer_maker,
vimgrep_arguments = {
"rg",
"--color=never",
"--no-heading",
"--with-filename",
"--line-number",
"--column",
"--smart-case",
"--follow",
"--hidden",
"--glob=!.git/",
},
mappings = {
i = {
["<C-t>"] = trouble.open,
["<C-q>"] = actions.send_to_qflist,
},
n = {
["<C-t>"] = trouble.open,
["<C-q>"] = actions.send_to_qflist,
},
},
},
extensions = {
media_files = {
filetypes = { "png", "webp", "jpg", "jpeg", "svg" },
find_cmd = "rg",
},
live_grep_args = {
auto_quoting = false,
mappings = {
i = {
["C-space"] = live_grep_actions.to_fuzzy_refine,
},
},
},
file_browser = {
hijack_netrw = true,
hide_parent_dir = true,
depth = 1,
respect_gitignore = vim.fn.executable("fd") == 1,
hidden = { file_browser = true, folder_browser = false },
auto_depth = false,
browse_files = require("telescope._extensions.file_browser.finders").browse_files,
display_stat = {
date = true,
size = true,
mode = false,
},
use_fd = true,
git_status = true,
mappings = {
["i"] = {
["<A-c>"] = file_browser_actions.create,
["<S-CR>"] = file_browser_actions.create_from_prompt,
["<A-r>"] = file_browser_actions.rename,
["<A-m>"] = file_browser_actions.move,
["<A-y>"] = file_browser_actions.copy,
["<A-d>"] = file_browser_actions.remove,
["<C-o>"] = file_browser_actions.open,
["<C-g>"] = file_browser_actions.goto_parent_dir,
["<C-e>"] = file_browser_actions.goto_home_dir,
["<C-w>"] = file_browser_actions.goto_cwd,
["<C-t>"] = file_browser_actions.change_cwd,
["<C-f>"] = file_browser_actions.toggle_browser,
["<C-h>"] = file_browser_actions.toggle_hidden,
["<C-s>"] = file_browser_actions.toggle_all,
["<bs>"] = file_browser_actions.backspace,
},
["n"] = {
["c"] = file_browser_actions.create,
["r"] = file_browser_actions.rename,
["m"] = file_browser_actions.move,
["y"] = file_browser_actions.copy,
["d"] = file_browser_actions.remove,
["o"] = file_browser_actions.open,
["g"] = file_browser_actions.goto_parent_dir,
["e"] = file_browser_actions.goto_home_dir,
["w"] = file_browser_actions.goto_cwd,
["t"] = file_browser_actions.change_cwd,
["f"] = file_browser_actions.toggle_browser,
["h"] = file_browser_actions.toggle_hidden,
["s"] = file_browser_actions.toggle_all,
},
},
},
},
})
telescope.load_extension("live_grep_args")
telescope.load_extension("file_browser")
telescope.load_extension("media_files")
telescope.load_extension("themes")
telescope.load_extension("dap")
local find_files = function()
builtin.find_files({
find_command = { "rg", "--files", "--hidden", "--glob", "!.git" },
})
end
local default_grep_files = function()
local input = vim.fn.input("[Global Search]: ")
local trimmed_input = vim.fn.trim(input)
-- Check the length of the input
local live_grep_args = telescope.extensions.live_grep_args
-- If the input is empty open the search prompt
if trimmed_input == "" then
live_grep_args.live_grep_args()
return
end
builtin.grep_string({ search = trimmed_input })
end
-- Function to get the visually selected text
-- Pipe the visual selection into Telescope find_files
local opts = { noremap = true, silent = true }
opts.desc = "Open file browser (cwd)"
vim.keymap.set("n", "<leader>pf", "<cmd>Telescope file_browser<CR>", opts)
opts.desc = "Open file browser (current file)"
vim.keymap.set("n", "<leader>cf", "<cmd>Telescope file_browser path=%:p:h select_buffer=true<CR>", opts)
opts.desc = "Fuzzy find files (cwd)"
vim.keymap.set("n", "<C-p>", find_files, opts)
opts.desc = "Fuzzy find recent files"
vim.keymap.set("n", "<leader>pr", builtin.oldfiles, opts)
opts.desc = "Fuzzy find files in git in cwd"
vim.keymap.set("n", "<leader>gf", builtin.git_files, opts)
opts.desc = "Fuzzy find files in cwd"
vim.keymap.set("n", "<leader>ps", default_grep_files, opts)
opts.desc = "Fuzzy find word under cursor in cwd"
vim.keymap.set("n", "<leader>sb", live_grep_shortcuts.grep_word_under_cursor, opts)
+2 -2
View File
@@ -14,9 +14,9 @@ vim.keymap.set("n", "]t", function()
todo_comments.jump_next()
end, opts)
opts.desc = "Open Comments in Telescope"
opts.desc = "Open todo comments in picker"
vim.keymap.set("n", "<leader>pt", function()
vim.cmd([[TodoTelescope]])
Snacks.picker.todo_comments()
end, opts)
opts.desc = "Open Comments in Trouble"
+3 -4
View File
@@ -147,7 +147,6 @@ SetupAutocommands()
local opts = { noremap = true, silent = true }
opts.desc = "Fuzzy find colorschemes"
vim.api.nvim_set_keymap("n", "<leader>cs", "<cmd>Telescope colorscheme<CR>", opts)
opts.desc = "Fuzzy find themes"
vim.api.nvim_set_keymap("n", "<leader>th", "<cmd>Telescope themes<CR>", opts)
vim.keymap.set("n", "<leader>cs", function()
Snacks.picker.colorschemes()
end, opts)
+9
View File
@@ -0,0 +1,9 @@
return {
"saghen/blink.cmp",
version = "1.*",
event = "InsertEnter",
dependencies = { "rafamadriz/friendly-snippets" },
config = function()
require("absolute.after.blink-cmp")
end,
}
-18
View File
@@ -1,18 +0,0 @@
return {
'hrsh7th/nvim-cmp',
event = "InsertEnter",
dependencies = {
'neovim/nvim-lspconfig',
'hrsh7th/cmp-nvim-lsp',
'hrsh7th/cmp-buffer', --source for text in buffer
'hrsh7th/cmp-path', --source for file system paths
'hrsh7th/cmp-cmdline',
"saadparwaiz1/cmp_luasnip", --source for autocompletion
"onsails/lspkind.nvim",
"L3MON4D3/LuaSnip", --Snippet Engine
"rafamadriz/friendly-snippets", --Useful snippets
},
config = function()
require("absolute.after.nvim-cmp")
end
}
+8 -1
View File
@@ -20,6 +20,13 @@ return {
warn = "",
},
},
-- picker added in Phase 3
picker = {
enabled = true,
ui_select = true,
},
},
config = function(_, opts)
require("snacks").setup(opts)
require("absolute.after.snacks")
end,
}
-21
View File
@@ -1,21 +0,0 @@
return {
"nvim-telescope/telescope.nvim",
branch = "0.1.x",
dependencies = {
"nvim-lua/popup.nvim",
"nvim-lua/plenary.nvim",
"nvim-telescope/telescope-dap.nvim",
"andrew-george/telescope-themes",
"nvim-telescope/telescope-media-files.nvim",
"nvim-telescope/telescope-file-browser.nvim",
{
"nvim-telescope/telescope-live-grep-args.nvim",
-- This will not install any breaking changes.
-- For major updates, this must be adjusted manually.
version = "^1.0.0",
},
},
config = function()
require("absolute.after.telescope")
end,
}
+4 -2
View File
@@ -3,8 +3,10 @@ local opts = {
silent = true,
}
opts.desc = "Show Keymap"
vim.keymap.set("n", "<leader>?", "<cmd>Telescope keymaps<CR>", opts) -- toggle file explorer
opts.desc = "Show keymaps"
vim.keymap.set("n", "<leader>?", function()
Snacks.picker.keymaps()
end, opts)
opts.desc = "Select All"
vim.keymap.set("n", "<C-a>", "gg0vG$", opts) -- select all
@@ -1,127 +0,0 @@
-- Sourced from github
-- https://github.com/3rd/image.nvim/issues/183#issuecomment-2572606511
M = {}
local supported_images = { "svg", "png", "jpg", "jpeg", "gif", "webp", "avif" }
local from_entry = require("telescope.from_entry")
local Path = require("plenary.path")
local conf = require("telescope.config").values
local Previewers = require("telescope.previewers")
local previewers = require("telescope.previewers")
local image_api = require("image")
local is_image_preview = false
local image = nil
local last_file_path = ""
local is_supported_image = function(filepath)
local split_path = vim.split(filepath:lower(), ".", { plain = true })
local extension = split_path[#split_path]
return vim.tbl_contains(supported_images, extension)
end
local delete_image = function()
if not image then
return
end
image:clear()
is_image_preview = false
end
local create_image = function(filepath, winid, bufnr)
image = image_api.hijack_buffer(filepath, winid, bufnr)
if not image then
return
end
vim.schedule(function()
image:render()
end)
is_image_preview = true
end
local function defaulter(f, default_opts)
default_opts = default_opts or {}
return {
new = function(opts)
if conf.preview == false and not opts.preview then
return false
end
opts.preview = type(opts.preview) ~= "table" and {} or opts.preview
if type(conf.preview) == "table" then
for k, v in pairs(conf.preview) do
opts.preview[k] = vim.F.if_nil(opts.preview[k], v)
end
end
return f(opts)
end,
__call = function()
local ok, err = pcall(f(default_opts))
if not ok then
error(debug.traceback(err))
end
end,
}
end
-- NOTE: Add teardown to cat previewer to clear image when close Telescope
local file_previewer = defaulter(function(opts)
opts = opts or {}
local cwd = opts.cwd or vim.fn.getcwd()
return Previewers.new_buffer_previewer({
title = "File Preview",
dyn_title = function(_, entry)
return Path:new(from_entry.path(entry, true)):normalize(cwd)
end,
get_buffer_by_name = function(_, entry)
return from_entry.path(entry, true)
end,
define_preview = function(self, entry, _)
local p = from_entry.path(entry, true)
if p == nil or p == "" then
return
end
conf.buffer_previewer_maker(p, self.state.bufnr, {
bufname = self.state.bufname,
winid = self.state.winid,
preview = opts.preview,
})
end,
teardown = function(_)
if is_image_preview then
delete_image()
end
end,
})
end, {})
local buffer_previewer_maker = function(filepath, bufnr, opts)
-- NOTE: Clear image when preview other file
if is_image_preview and last_file_path ~= filepath then
delete_image()
end
last_file_path = filepath
if is_supported_image(filepath) then
filepath = string.gsub(filepath, " ", "%%20"):gsub("\\", "")
create_image(filepath, opts.winid, bufnr)
else
previewers.buffer_previewer_maker(filepath, bufnr, opts)
end
end
M.buffer_previewer_maker = buffer_previewer_maker
M.file_previewer = file_previewer.new
return M