Skip to content

Commit

Permalink
refactor: util.cmp
Browse files Browse the repository at this point in the history
  • Loading branch information
rafi committed May 17, 2024
1 parent e6acb61 commit 38a2214
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 110 deletions.
15 changes: 8 additions & 7 deletions lua/rafi/plugins/coding.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ return {
select = false,
}),
['<C-Space>'] = cmp.mapping.complete(),
['<Tab>'] = Util.cmp.supertab({
behavior = require('cmp').SelectBehavior.Select,
}),
['<S-Tab>'] = Util.cmp.supertab_shift({
behavior = require('cmp').SelectBehavior.Select,
}),
['<C-j>'] = Util.cmp.snippet_jump_forward(),
['<C-k>'] = Util.cmp.snippet_jump_backward(),
['<C-n>'] = cmp.mapping.select_next_item({
behavior = cmp.SelectBehavior.Insert,
}),
Expand Down Expand Up @@ -170,13 +178,6 @@ return {
end,
}
table.insert(opts.sources, { name = 'luasnip', keyword_length = 2 })

local Util = require('rafi.util')
local behavior = { behavior = require('cmp').SelectBehavior.Select }
opts.mapping['<Tab>'] = Util.cmp.luasnip_supertab(behavior)
opts.mapping['<S-Tab>'] = Util.cmp.luasnip_shift_supertab(behavior)
opts.mapping['<C-j>'] = Util.cmp.luasnip_jump_forward()
opts.mapping['<C-b>'] = Util.cmp.luasnip_jump_backward()
end,
},
},
Expand Down
80 changes: 18 additions & 62 deletions lua/rafi/plugins/extras/coding/native_snippets.lua
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
if not vim.snippet then
LazyVim.warn('Native snippets are only supported on Neovim >= 0.10.0')
return {}
end

return {
desc = 'Use native snippets instead of LuaSnip. Only works on Neovim >= 0.10!',

{ 'L3MON4D3/LuaSnip', enabled = false },
{ 'saadparwaiz1/cmp_luasnip', enabled = false },

-----------------------------------------------------------------------------
-- Use native snippet client instead of LuaSnip
{
'danymat/neogen',
optional = true,
opts = { snippet_engine = nil },
},

-----------------------------------------------------------------------------
-- Allow vscode style snippets to be used with native neovim snippets
{
'garymjr/nvim-snippets',
'nvim-cmp',
dependencies = {
-- Preconfigured snippets for different languages
'rafamadriz/friendly-snippets',
},
opts = {
friendly_snippets = true,
{ 'rafamadriz/friendly-snippets' },
-- Allow vscode style snippets to be used with native neovim snippets
{ 'garymjr/nvim-snippets', opts = { friendly_snippets = true } },
},
},

-----------------------------------------------------------------------------
{
'nvim-cmp',
dependencies = { 'nvim-snippets' },
opts = function(_, opts)
opts = opts or {}
opts.snippet = {
Expand All @@ -38,49 +29,14 @@ return {

opts.sources = opts.sources or {}
table.insert(opts.sources, { name = 'snippets', keyword_length = 2 })

local cmp = require('cmp')
opts.mapping = opts.mapping or {}
opts.mapping['<Tab>'] = {
i = function(fallback)
local col = vim.fn.col('.') - 1
if cmp.visible() then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
elseif vim.snippet.jumpable(1) then
vim.snippet.jump(1)
elseif col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
cmp.complete()
else
fallback()
end
end,
s = function(fallback)
if vim.snippet.jumpable(1) then
vim.snippet.jump(1)
else
fallback()
end
end
}

opts.mapping['<S-Tab>'] = {
i = function(fallback)
if cmp.visible() then
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Select })
elseif vim.snippet.jumpable(-1) then
vim.snippet.jump(-1)
else
fallback()
end
end,
s = function(fallback)
if vim.snippet.jumpable(-1) then
vim.snippet.jump(-1)
else
fallback()
end
end,
}
end,
},

-----------------------------------------------------------------------------
{
'danymat/neogen',
optional = true,
opts = { snippet_engine = nil },
},

}
127 changes: 86 additions & 41 deletions lua/rafi/util/cmp.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
-- Cmp utilities
-- https://github.com/rafi/vim-config
-- Credits: https://github.com/VonHeikemen/lsp-zero.nvim

-- Several cmp mapping helpers to use to your liking.
-- Based on: https://github.com/VonHeikemen/lsp-zero.nvim

---@class rafi.util.cmp
local M = {}
Expand All @@ -10,11 +12,35 @@ local function get_cmp()
return ok_cmp and cmp or {}
end

local function get_luasnip()
local ok_luasnip, luasnip = pcall(require, 'luasnip')
return ok_luasnip and luasnip or {}
---@class rafi.snippet.client
---@field active fun(filter: vim.snippet.ActiveFilter): boolean
---@field jump fun(direction: number): boolean

-- Return luasnip or native vim.snippet.
---@return rafi.snippet.client
local function get_snippet_client()
local ok, luasnip = pcall(require, 'luasnip')
if not ok then
return vim.snippet or nil
end

-- Add 'active' method to luasnip to match vim.snippet.active behavior.
if not luasnip.active then
luasnip.active = function(filter)
if filter and filter.direction then
return luasnip.locally_jumpable(filter.direction)
end
LazyVim.error('luasnip.active: opts.direction is required')
end
end
return luasnip
end

-- Enables completion when the cursor is inside a word. If the completion
-- menu is visible it will navigate to the next item in the list. If the
-- line is empty it uses the fallback.
---@param select_opts? cmp.SelectOption
---@return cmp.Mapping
function M.tab_complete(select_opts)
local cmp = get_cmp()
return cmp.mapping(function(fallback)
Expand All @@ -30,6 +56,10 @@ function M.tab_complete(select_opts)
end, { 'i', 's' })
end

-- If the completion menu is visible navigate to the previous item
-- in the list. Else, use the fallback.
---@param select_opts? cmp.SelectOption
---@return cmp.Mapping
function M.select_prev_or_fallback(select_opts)
local cmp = get_cmp()
return cmp.mapping(function(fallback)
Expand All @@ -41,6 +71,9 @@ function M.select_prev_or_fallback(select_opts)
end, { 'i', 's' })
end

-- If the completion menu is visible it cancels the
-- popup. Else, it triggers the completion menu.
---@param opts {modes?: string[]}
function M.toggle_completion(opts)
opts = opts or {}
local cmp = get_cmp()
Expand All @@ -54,103 +87,115 @@ function M.toggle_completion(opts)
end, opts.modes)
end

function M.luasnip_supertab(select_opts)
---
-- Snippet related mappings
---

-- If the completion menu is visible it will navigate to the next item in
-- the list. If cursor is on top of the trigger of a snippet it'll expand
-- it. If the cursor can jump to a snippet placeholder, it moves to it.
-- If the cursor is in the middle of a word that doesn't trigger a snippet
-- it displays the completion menu. Else, it uses the fallback.
---@param select_opts? cmp.SelectOption
---@return cmp.Mapping
function M.supertab(select_opts)
local cmp = get_cmp()
local snippet = get_snippet_client()
return {
i = function(fallback)
local col = vim.fn.col('.') - 1
if cmp.visible() then
cmp.select_next_item(select_opts)
elseif require('luasnip').locally_jumpable(1) then
require('luasnip').jump(1)
elseif snippet and snippet.active({ direction = 1 }) then
snippet.jump(1)
elseif col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
fallback()
else
cmp.complete()
end
end,
s = function(fallback)
if require('luasnip').locally_jumpable(1) then
require('luasnip').jump(1)
if snippet and snippet.active({ direction = 1 }) then
snippet.jump(1)
else
fallback()
end
end,
}
end

function M.luasnip_shift_supertab(select_opts)
-- If the completion menu is visible it will navigate to previous item in the
-- list. If the cursor can navigate to a previous snippet placeholder, it
-- moves to it. Else, it uses the fallback.
---@param select_opts? cmp.SelectOption
---@return cmp.Mapping
function M.supertab_shift(select_opts)
local cmp = get_cmp()
local snippet = get_snippet_client()
return {
i = function(fallback)
if cmp.visible() then
cmp.select_prev_item(select_opts)
elseif require('luasnip').locally_jumpable(-1) then
require('luasnip').jump(-1)
elseif snippet and snippet.active({ direction = -1 }) then
snippet.jump(-1)
else
fallback()
end
end,
s = function(fallback)
if require('luasnip').locally_jumpable(-1) then
require('luasnip').jump(-1)
if snippet and snippet.active({ direction = -1 }) then
snippet.jump(-1)
else
fallback()
end
end,
}
end

function M.luasnip_next_or_expand(select_opts)
local cmp = get_cmp()
local luasnip = get_luasnip()

return cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item(select_opts)
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { 'i', 's' })
end

function M.luasnip_next(select_opts)
-- If completion menu is visible it will navigate to the next item in the
-- list. If the cursor can jump to a snippet placeholder, it moves to it.
-- Else, it uses the fallback.
---@param select_opts? cmp.SelectOption
---@return cmp.Mapping
function M.snippet_next(select_opts)
local cmp = get_cmp()
local luasnip = get_luasnip()
local snippet = get_snippet_client()

return cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item(select_opts)
elseif luasnip.jumpable(1) then
luasnip.jump(1)
elseif snippet and snippet.active({ direction = 1 }) then
snippet.jump(1)
else
fallback()
end
end, { 'i', 's' })
end

function M.luasnip_jump_forward()
-- Go to the next snippet placeholder.
---@return cmp.Mapping
function M.snippet_jump_forward()
local cmp = get_cmp()
local luasnip = get_luasnip()
local snippet = get_snippet_client()

return cmp.mapping(function(fallback)
if luasnip.jumpable(1) then
luasnip.jump(1)
if snippet and snippet.active({ direction = 1 }) then
snippet.jump(1)
else
fallback()
end
end, { 'i', 's' })
end

function M.luasnip_jump_backward()
-- Go to the previous snippet placeholder.
---@return cmp.Mapping
function M.snippet_jump_backward()
local cmp = get_cmp()
local luasnip = get_luasnip()
local snippet = get_snippet_client()

return cmp.mapping(function(fallback)
if luasnip.jumpable(-1) then
luasnip.jump(-1)
if snippet and snippet.active({ direction = -1 }) then
snippet.jump(-1)
else
fallback()
end
Expand Down

0 comments on commit 38a2214

Please sign in to comment.