summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyren223 <contact@kyren.codes>2026-06-24 02:40:25 +0300
committerKyren223 <contact@kyren.codes>2026-06-24 02:40:25 +0300
commitda840436921f017269b4bd1cc0719a21886b104f (patch)
tree1a0add5943d6ef6fbf7910b83304ddedba4e7d21
parent43299925b4bf9b2f2ec799212fe77d26908d695d (diff)
Add luasnip and use it to load language and project specific snippets,
also add a bunch of mess to get a nice unwrap(), this is committed on purpose as a reference for future me, will be cleaned up in the next commit. It also touches a bunch of blink.cmp to achieve this.
-rw-r--r--.config/nvim/lua/plugins/blink-cmp.lua84
-rw-r--r--.config/nvim/lua/plugins/luasnip.lua147
2 files changed, 204 insertions, 27 deletions
diff --git a/.config/nvim/lua/plugins/blink-cmp.lua b/.config/nvim/lua/plugins/blink-cmp.lua
index 0c3f6d5..6d2a72e 100644
--- a/.config/nvim/lua/plugins/blink-cmp.lua
+++ b/.config/nvim/lua/plugins/blink-cmp.lua
@@ -5,10 +5,26 @@ return {
version = '1.*',
dependencies = {
{ 'Kaiser-Yang/blink-cmp-git', dependencies = { 'nvim-lua/plenary.nvim' } },
+ { 'L3MON4D3/LuaSnip', version = 'v2.*' },
},
---@module 'blink.cmp'
---@type blink.cmp.Config
opts = {
+ snippets = { preset = 'luasnip' },
+ -- snippets = {
+ -- expand = function(snippet)
+ -- require('luasnip').lsp_expand(snippet)
+ -- end,
+ -- active = function(filter)
+ -- if filter and filter.direction then
+ -- return require('luasnip').jumpable(filter.direction)
+ -- end
+ -- return require('luasnip').in_snippet()
+ -- end,
+ -- jump = function(direction)
+ -- require('luasnip').jump(direction)
+ -- end,
+ -- },
keymap = {
preset = 'enter',
['<C-e>'] = {},
@@ -18,6 +34,36 @@ return {
['<C-f>'] = {},
['<C-u>'] = { 'scroll_documentation_up', 'fallback' },
['<C-d>'] = { 'scroll_documentation_down', 'fallback' },
+ ['<cr>'] = {
+ function(cmp)
+ local item = cmp.get_selected_item()
+
+ if item and item.label == '.unwrap' then
+ vim.schedule(function()
+ local ctx = cmp.get_context()
+ if not ctx or not ctx.bounds then
+ return cmp.accept()
+ end
+ local partial = ctx.get_keyword() or ''
+ local full = cmp.get_selected_item().label
+ local rest = full:sub(#partial + 1 + 1)
+
+ local row = ctx.bounds.line_number - 1
+ local col = ctx.bounds.start_col + ctx.bounds.length
+
+ local line = vim.api.nvim_buf_get_lines(ctx.bufnr, row, row + 1, false)[1]
+ local cursor_at_eol = col > #line
+
+ vim.api.nvim_put({ rest }, 'c', cursor_at_eol, true)
+ require('luasnip').expand()
+ end)
+ return true
+ end
+
+ return cmp.accept()
+ end,
+ 'fallback',
+ },
},
completion = {
@@ -31,7 +77,16 @@ return {
keyword = { range = 'full' },
-- Preselect first one, don't complete until confirmation
- list = { selection = { preselect = true, auto_insert = false } },
+ list = {
+ selection = {
+ preselect = true,
+ -- auto_insert = false,
+ auto_insert = function(ctx)
+ -- vim.notify()
+ return ctx.get_keyword() == 'unwrap'
+ end,
+ },
+ },
menu = {
draw = {
@@ -82,31 +137,7 @@ return {
},
snippets = {
- -- score_offset = 200, -- make snippets highest priority
- transform_items = function(_, items)
- return vim.tbl_filter(function(item)
- -- vim.print(item)
- if item.kind ~= require('blink.cmp.types').CompletionItemKind.Snippet then
- return true
- end
-
- local name = item.description
- -- vim.print(name)
- local parts = vim.split(name, ' ', { trimempty = false })
- local namespace = #parts > 1 and parts[1] or nil
- -- vim.print(namespace)
- if not namespace then
- return true
- end
-
- -- vim.print(vim.fn.getcwd())
- local path = vim.split(vim.fn.getcwd(), '/')
- local dir = path[#path]
- -- vim.print(dir)
-
- return dir == namespace
- end, items)
- end,
+ score_offset = 200, -- make snippets highest priority
},
lsp = {
@@ -181,7 +212,6 @@ return {
opts_extend = { 'sources.default' },
-- TODO: pressing backk (deleting), should re-show completion menu
- -- TODO: ghost text only for LLMs?
}
-- From LazyVim
diff --git a/.config/nvim/lua/plugins/luasnip.lua b/.config/nvim/lua/plugins/luasnip.lua
new file mode 100644
index 0000000..39debb3
--- /dev/null
+++ b/.config/nvim/lua/plugins/luasnip.lua
@@ -0,0 +1,147 @@
+return {
+ 'L3MON4D3/LuaSnip',
+ version = 'v2.*', -- follow latest release.
+ build = 'make install_jsregexp',
+ opts = {},
+ config = function(_, opts)
+ require('luasnip').setup(opts)
+
+ -- Load language specific snippets
+ require('luasnip.loaders.from_vscode').load({ paths = './snippets' })
+
+ -- load project specific snippets
+ local project = vim.fn.fnamemodify(vim.fn.getcwd(), ':t')
+ local project_snippets = {
+ eko = 'eko.json',
+ ['carbonight.nvim'] = 'carbonight.json',
+ }
+ local file = project_snippets[project]
+ if file then
+ require('luasnip.loaders.from_vscode').load_standalone({ path = './snippets/' .. file })
+ end
+
+ -- some shorthands...
+ local ls = require('luasnip')
+ local s = ls.snippet
+ local sn = ls.snippet_node
+ local isn = ls.indent_snippet_node
+ local t = ls.text_node
+ local i = ls.insert_node
+ local f = ls.function_node
+ local c = ls.choice_node
+ local d = ls.dynamic_node
+ local r = ls.restore_node
+ local events = require('luasnip.util.events')
+ local ai = require('luasnip.nodes.absolute_indexer')
+ local opt = require('luasnip.nodes.optional_arg')
+ local extras = require('luasnip.extras')
+ local l = extras.lambda
+ local rep = extras.rep
+ local p = extras.partial
+ local m = extras.match
+ local n = extras.nonempty
+ local dl = extras.dynamic_lambda
+ local fmt = require('luasnip.extras.fmt').fmt
+ local fmta = require('luasnip.extras.fmt').fmta
+ local conds = require('luasnip.extras.expand_conditions')
+ local postfix = require('luasnip.extras.postfix').postfix
+ local ts_postfix = require('luasnip.extras.treesitter_postfix').treesitter_postfix
+ local types = require('luasnip.util.types')
+ local parse = require('luasnip.util.parser').parse_snippet
+ local ms = ls.multi_snippet
+ local k = require('luasnip.nodes.key_indexer').new_key
+ local postfix_builtin = require('luasnip.extras.treesitter_postfix').builtin
+
+ ls.add_snippets('all', {
+ ts_postfix({
+ matchTSNode = postfix_builtin.tsnode_matcher.find_topmost_types({
+ 'identifier',
+ 'method_invocation',
+ 'field_access',
+ 'type_identifier',
+ 'class_literal',
+ 'this',
+ 'scoped_type_identifier',
+ }),
+ trig = '.unwrap',
+ }, {
+ f(function(_, parent)
+ -- Use POSTFIX_MATCH instead of LS_TSMATCH
+ local match = parent.snippet.env.LS_TSMATCH
+ -- vim.notify('postfix: "' .. vim.inspect(match) .. '"')
+
+ if not match then
+ return { 'unwrap()' }
+ end
+
+ -- If LuaSnip provides the match as a table, concat it
+ if type(match) == 'table' then
+ match = table.concat(match, '\n')
+ end
+
+ local wrapped = string.format('unwrap(%s)', match)
+ -- Split it back into a table of strings to safely handle any newlines
+ return vim.split(wrapped, '\n', { plain = true })
+ end),
+ }),
+
+ -- ts_postfix({
+ -- trig = 'unwrap',
+ -- matchTSNode = {
+ -- query = [[
+ -- (field_access
+ -- object: (
+ -- field_access
+ -- method_invocation
+ -- identifier
+ -- ) @my_object
+ -- ) @my_field
+ -- ]],
+ -- query_lang = 'java',
+ -- select = 'longest',
+ -- },
+ -- }, {
+ -- f(function(_, parent)
+ -- -- Use POSTFIX_MATCH instead of LS_TSMATCH
+ -- local match = parent.snippet.env.POSTFIX_MATCH
+ -- vim.notify('postfix: "' .. vim.inspect(match) .. '"')
+ --
+ -- if not match then
+ -- return { 'unwrap()' }
+ -- end
+ --
+ -- -- If LuaSnip provides the match as a table, concat it
+ -- if type(match) == 'table' then
+ -- match = table.concat(match, '\n')
+ -- end
+ --
+ -- -- Format the string
+ -- local wrapped = string.format('unwrap(%s)', match)
+ --
+ -- -- Split it back into a table of strings to safely handle any newlines
+ -- return vim.split(wrapped, '\n', { plain = true })
+ -- end),
+ -- }, { condition = require('luasnip.util.util').yes }),
+ })
+ -- postfix({ trig = 'unwrap', match_pattern = [[[%w%.%_%-%"%']+$]] }, {
+ -- f(function(_, parent)
+ -- local match = parent.snippet.env.POSTFIX_MATCH
+ -- if match == nil then
+ -- return 'bunger'
+ -- end
+ -- -- match = match:sub(1, -2)
+ -- return 'unwrap(' .. match .. ')'
+ -- end, {}),
+ -- }, { condition = require('luasnip.util.util').yes }),
+
+ -- postfix({ trig = '.unwrap', snippetType = 'snippet' }, {
+ -- f(function(_, parent)
+ -- local match = parent.snippet.env.POSTFIX_MATCH
+ -- if match == nil then
+ -- return 'bunger'
+ -- end
+ -- return '[' .. match .. ']'
+ -- end, {}),
+ -- }),
+ end,
+}