Compact neovim config with vim.pack abstraction

main
Luis Soto 4 months ago committed by zoomiti
parent 110f7f7f1c
commit f5363fb46e

@ -1,19 +1,184 @@
vim.pack.add({
{ src = "https://github.com/zoomiti/firewatch" },
{ src = "https://github.com/echasnovski/mini.pick" },
{ src = "https://github.com/echasnovski/mini.icons" },
{ src = "https://github.com/neovim/nvim-lspconfig" },
{ src = "https://github.com/mrcjkb/rustaceanvim" },
{ src = 'https://github.com/folke/which-key.nvim' },
{ src = 'https://github.com/LunarWatcher/auto-pairs' },
{ src = 'https://github.com/nvim-lua/plenary.nvim' },
{ src = 'https://github.com/NeogitOrg/neogit' },
{ src = 'https://github.com/lewis6991/gitsigns.nvim' },
{ src = 'https://github.com/nvim-treesitter/nvim-treesitter' },
{ src = 'https://github.com/rayliwell/tree-sitter-rstml' },
{ src = 'https://github.com/Saghen/blink.cmp', version = vim.version.range("v1.*") },
{ src = 'https://github.com/stevearc/oil.nvim' },
{ src = 'https://github.com/catgoose/nvim-colorizer.lua' },
-- vim: foldmethod=marker foldlevel=0
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
require "pack".add({
{
"zoomiti/firewatch",
config = function()
vim.opt.termguicolors = true
vim.g.dark_transp_bg = 1
vim.cmd.colorscheme("fire")
end
},
"nvim-mini/mini.icons",
{
"nvim-mini/mini.pick",
setup = true,
config = function()
vim.keymap.set('n', '<leader>b', '<CMD>Pick buffers<CR>', { desc = "Buffers" })
vim.keymap.set('n', '<leader>f', '<CMD>Pick files<CR>', { desc = "Files" })
end
},
{
"nvim-mini/mini.surround",
setup = {
custom_surroundings = nil,
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
add = 'gs', -- Add surrounding in Normal and Visual modes
delete = 'ds', -- Delete surrounding
find = '', -- Find surrounding (to the right)
find_left = '', -- Find surrounding (to the left)
highlight = '', -- Highlight surrounding
replace = 'cs', -- Replace surrounding
suffix_last = '', -- Suffix to search with "prev" method
suffix_next = '', -- Suffix to search with "next" method
},
respect_selection_type = true,
search_method = 'cover_or_next',
}
},
"neovim/nvim-lspconfig",
"mrcjkb/rustaceanvim",
{
"folke/which-key.nvim",
config = function()
local wk = require("which-key")
wk.add({
{ "<leader>g", group = "git" },
{ "<leader>h", group = "git_hunk" }
})
end
},
"LunarWatcher/auto-pairs",
{ "nvim-lua/plenary.nvim" },
{
"NeogitOrg/neogit",
config = function()
vim.keymap.set('n', '<leader>gs', '<CMD>Neogit kind=split_above_all<CR>', { desc = "Git Status" })
vim.keymap.set('n', '<leader>gp', '<CMD>Neogit pull<CR>', { desc = "Git Pull" })
vim.keymap.set('n', '<leader>gP', '<CMD>Neogit push<CR>', { desc = "Git Push" })
end
},
{
"lewis6991/gitsigns.nvim",
setup = {
-- {{{ gitsigns on_attach
on_attach = function(bufnr)
local gitsigns = require('gitsigns')
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
-- Navigation
map('n', ']c', function()
if vim.wo.diff then
vim.cmd.normal({ ']c', bang = true })
else
gitsigns.nav_hunk('next')
end
end, { desc = "next hunk" })
map('n', '[c', function()
if vim.wo.diff then
vim.cmd.normal({ '[c', bang = true })
else
gitsigns.nav_hunk('prev')
end
end, { desc = "prev hunk" })
-- Actions
map('n', '<leader>hs', gitsigns.stage_hunk, { desc = "(un)stage hunk" })
map('n', '<leader>hr', gitsigns.reset_hunk, { desc = "reset hunk" })
map('v', '<leader>hs', function()
gitsigns.stage_hunk({ vim.fn.line('.'), vim.fn.line('v') })
end, { desc = "(un)stage hunk" })
map('v', '<leader>hr', function()
gitsigns.reset_hunk({ vim.fn.line('.'), vim.fn.line('v') })
end, { desc = "reset hunk" })
--map('n', '<leader>hS', gitsigns.stage_buffer)
--map('n', '<leader>hR', gitsigns.reset_buffer)
map('n', '<leader>hp', gitsigns.preview_hunk, { desc = "preview hunk" })
map('n', '<leader>hi', gitsigns.preview_hunk_inline, { desc = "preview hunk inline" })
map('n', '<leader>hb', function()
gitsigns.blame_line({ full = true })
end, { desc = "blame line" })
map('n', '<leader>hd', gitsigns.diffthis, { desc = "diff buffer" })
map('n', '<leader>hD', function()
gitsigns.diffthis('~')
end, { desc = "diff buffer to HEAD" })
map('n', '<leader>hQ', function() gitsigns.setqflist('all') end,
{ desc = "set qflist with changes in all files and cwd" })
map('n', '<leader>hq', gitsigns.setqflist, { desc = "set qflist with changes in current buffer" })
-- Toggles
map('n', '<leader>tb', gitsigns.toggle_current_line_blame, { desc = "toggle current line blame" })
map('n', '<leader>tw', gitsigns.toggle_word_diff, { desc = "toggle word diff" })
-- Text object
map({ 'o', 'x' }, 'ih', gitsigns.select_hunk, { desc = "inner hunk" })
end
-- }}}
}
},
{
"nvim-treesitter/nvim-treesitter",
module = 'nvim-treesitter.configs',
setup = {
ensure_installed = { "c", "lua" },
sync_install = false,
auto_install = false,
ignore_install = { "javascript" },
highlight = { enable = true },
indent = { enable = true },
}
},
{ "rayliwell/tree-sitter-rstml", setup = true },
{
"Saghen/blink.cmp",
version = vim.version.range("v1.*"),
build = "cargo build --release",
setup = {
signature = { enabled = true }
}
},
{
"stevearc/oil.nvim",
setup = true,
config = function()
vim.keymap.set("n", "-", vim.cmd.Oil, { desc = "Open Parent Directory" })
end
},
{
"catgoose/nvim-colorizer.lua",
setup = {
user_default_options = {
names = true, -- "Name" codes like Blue or red. Added from `vim.api.nvim_get_color_map()`
names_opts = { -- options for mutating/filtering names.
lowercase = false, -- name:lower(), highlight `blue` and `red`
camelcase = true, -- name, highlight `Blue` and `Red`
uppercase = true, -- name:upper(), highlight `BLUE` and `RED`
strip_digits = true, -- ignore names with digits,
-- highlight `blue` and `red`, but not `blue3` and `red4`
},
xterm = true,
}
}
},
"christoomey/vim-tmux-navigator"
})
vim.opt.number = true
@ -34,27 +199,15 @@ vim.g.encoding = 'utf-8'
vim.opt.conceallevel = 2
vim.opt.concealcursor = 'n'
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
vim.keymap.set("x", "/", "<Esc>/\\%V", { desc = 'Search forward within visual selection' })
vim.keymap.set("x", "?", "<Esc>?\\%V", { desc = 'Search forward within visual selection' })
require("mini.icons").setup()
require("colorizer").setup()
require('oil').setup()
vim.keymap.set("n", "-", vim.cmd.Oil, { desc = "Open Parent Directory" })
require "mini.pick".setup()
vim.keymap.set('n', '<leader>b', '<CMD>Pick buffers<CR>', { desc = "Buffers" })
vim.keymap.set('n', '<leader>f', '<CMD>Pick files<CR>')
vim.keymap.set('n', '<leader>s',
'<CMD>update ~/.config/nvim/init.lua<CR><CMD>source ~/.config/nvim/init.lua<CR>', { desc = "Update Config" })
vim.keymap.set('n', '<leader>w', '<CMD>write<CR>', { desc = "Write File" })
-- Terminal Mode
-- {{{ Terminal Mode
vim.keymap.set('t', '<ESC>', '<C-\\><C-n>')
vim.keymap.set('t', '<C-W>h', '<C-\\><C-n><C-W>h')
vim.keymap.set('t', '<C-W>j', '<C-\\><C-n><C-W>j')
@ -65,26 +218,19 @@ vim.keymap.set('t', '<C-W><Down>', '<C-\\><C-n><C-W><Down>')
vim.keymap.set('t', '<C-W><Up>', '<C-\\><C-n><C-W><Up>')
vim.keymap.set('t', '<C-W><Right>', '<C-\\><C-n><C-W><Right>')
vim.api.nvim_create_autocmd('BufEnter', { pattern = 'term://*', command = 'startinsert' })
vim.api.nvim_create_autocmd('BufEnter', {
pattern = 'term://*',
vim.api.nvim_create_autocmd('TermOpen', {
callback = function(args)
vim.keymap.set('n', '<C-c>', 'i<C-c><C-\\><C-n>', { buffer = args.buf })
end
})
-- }}}
-- Colorscheme
vim.opt.termguicolors = true
vim.g.dark_transp_bg = 1
vim.cmd("colorscheme fire")
-- LSP
-- {{{ LSP
vim.keymap.set('n', 'grd', vim.lsp.buf.definition, { desc = "vim.lsp.buf.definition" })
vim.keymap.set({ 'n', 'v', 'x' }, 'grf', vim.lsp.buf.format, { desc = "LSP Format" })
vim.lsp.inlay_hint.enable(true)
vim.g.c_syntax_for_h = 1
require('blink.cmp').setup({
signature = { enabled = true }
})
vim.g.c_syntax_for_h = true
vim.lsp.enable({ 'lua_ls', 'ccls' })
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
@ -121,85 +267,9 @@ vim.diagnostic.config({
current_line = true,
}
})
-- }}}
-- Neogit
vim.keymap.set('n', '<leader>gs', '<CMD>Neogit kind=split_above_all<CR>', { desc = "Git Status" })
vim.keymap.set('n', '<leader>gp', '<CMD>Neogit pull<CR>', { desc = "Git Pull" })
vim.keymap.set('n', '<leader>gP', '<CMD>Neogit push<CR>', { desc = "Git Push" })
require('gitsigns').setup {
on_attach = function(bufnr)
local gitsigns = require('gitsigns')
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
-- Navigation
map('n', ']c', function()
if vim.wo.diff then
vim.cmd.normal({ ']c', bang = true })
else
gitsigns.nav_hunk('next')
end
end, { desc = "next hunk" })
map('n', '[c', function()
if vim.wo.diff then
vim.cmd.normal({ '[c', bang = true })
else
gitsigns.nav_hunk('prev')
end
end, { desc = "prev hunk" })
-- Actions
map('n', '<leader>hs', gitsigns.stage_hunk, { desc = "(un)stage hunk" })
map('n', '<leader>hr', gitsigns.reset_hunk, { desc = "reset hunk" })
map('v', '<leader>hs', function()
gitsigns.stage_hunk({ vim.fn.line('.'), vim.fn.line('v') })
end, { desc = "(un)stage hunk" })
map('v', '<leader>hr', function()
gitsigns.reset_hunk({ vim.fn.line('.'), vim.fn.line('v') })
end, { desc = "reset hunk" })
--map('n', '<leader>hS', gitsigns.stage_buffer)
--map('n', '<leader>hR', gitsigns.reset_buffer)
map('n', '<leader>hp', gitsigns.preview_hunk, { desc = "preview hunk" })
map('n', '<leader>hi', gitsigns.preview_hunk_inline, { desc = "preview hunk inline" })
map('n', '<leader>hb', function()
gitsigns.blame_line({ full = true })
end, { desc = "blame line" })
map('n', '<leader>hd', gitsigns.diffthis, { desc = "diff buffer" })
map('n', '<leader>hD', function()
gitsigns.diffthis('~')
end, { desc = "diff buffer to HEAD" })
map('n', '<leader>hQ', function() gitsigns.setqflist('all') end,
{ desc = "set qflist with changes in all files and cwd" })
map('n', '<leader>hq', gitsigns.setqflist, { desc = "set qflist with changes in current buffer" })
-- Toggles
map('n', '<leader>tb', gitsigns.toggle_current_line_blame, { desc = "toggle current line blame" })
map('n', '<leader>tw', gitsigns.toggle_word_diff, { desc = "toggle word diff" })
-- Text object
map({ 'o', 'x' }, 'ih', gitsigns.select_hunk, { desc = "inner hunk" })
end
}
local wk = require "which-key"
wk.add({
{ "<leader>g", group = "git" },
{ "<leader>h", group = "git_hunk" }
})
-- {{{ Status Line
local statusline = {
'%{%v:lua.StatuslineHighlight("Left")%}',
@ -382,18 +452,9 @@ end
vim.opt.statusline = table.concat(statusline, '')
require 'nvim-treesitter.configs'.setup {
ensure_installed = { "c", "lua" },
--ensure_installed = {},
sync_install = false,
auto_install = false,
ignore_install = { "javascript" },
highlight = { enable = true },
indent = { enable = true },
}
require("tree-sitter-rstml").setup()
-- }}}
-- {{{ Floaterminal
local function create_floating_window(opts)
opts = opts or {}
local width = opts.width or math.floor(vim.o.columns * 0.8)
@ -445,3 +506,5 @@ end
vim.api.nvim_create_user_command("Floaterminal", toggle_terminal, { desc = "Toggles a floating terminal" })
vim.keymap.set({ "n", "t" }, "<leader>tt", toggle_terminal, { desc = "Toggles a floating terminal" })
-- }}}

@ -0,0 +1,292 @@
local M = {}
local function normalize_url(src)
-- Check for nil first
if not src or type(src) ~= "string" or src == "" then
error("Plugin source must be a non-empty string, got: " .. tostring(src))
end
-- Already a full URL (http/https/git)
if src:match("^https?://") or src:match("^git@") then
return src
end
-- SSH format: git@github.com:user/repo.git
if src:match("^git@[%w%.%-]+:") then
return src
end
-- GitHub shorthand: user/repo or user/repo.git
if src:match("^[%w-_%.]+/[%w-_%.]+%.?g?i?t?$") then
local clean_src = src:gsub("%.git$", "")
return "https://github.com/" .. clean_src .. ".git"
end
-- Local path or other format - pass through
return src
end
local function extract_plugin_name(original_src)
if not original_src then return nil end
-- Extract repo name from various formats
local repo = original_src:match("([^/]+)$") -- Get last part after /
if repo then
-- Remove common suffixes
repo = repo:gsub("%.git$", "")
repo = repo:gsub("%.nvim$", "")
repo = repo:gsub("%.lua$", "")
repo = repo:gsub("^nvim%-", "")
return repo
end
return original_src
end
local function is_single_plugin_spec(plugins)
-- Check if it's a single plugin spec vs array of plugin specs
if type(plugins) ~= "table" then
return false
end
-- If plugins[1] is a string, it's a single spec: {"user/repo", setup = {...}}
if type(plugins[1]) == "string" then
return true
end
-- If it has src but plugins[1] is not a table, it's a single spec: {src = "user/repo"}
if plugins.src and type(plugins[1]) ~= "table" then
return true
end
-- Otherwise it's an array: {{src = "..."}, {src = "..."}} or {"user/repo", "user/repo2"}
return false
end
function M._do_setup(spec, src)
local function do_setup()
-- vim.notify("Setting up " .. src)
if spec.setup then
local plugin_name = spec.module or extract_plugin_name(src)
if not plugin_name then
error("Could not determine plugin name for setup")
end
if spec.setup == true then
-- Auto-setup with defaults
require(plugin_name).setup()
elseif type(spec.setup) == "table" then
-- Setup with config
require(plugin_name).setup(spec.setup)
end
end
if spec.config then
if type(spec.config) == "function" then
spec.config()
end
end
end
if spec.config or spec.setup or spec.build then
local setup_ok, setup_err = pcall(do_setup)
if not setup_ok then
vim.notify("Failed to setup " .. (src or "unknown") .. ": " .. setup_err, vim.log.levels.WARN)
end
end
end
-- Main function - handles single plugin or multiple plugins
function M.add(plugins)
-- Single string plugin
if type(plugins) == "string" then
if plugins == "" then
error("Plugin source cannot be empty string")
end
local ok, err = pcall(vim.pack.add, normalize_url(plugins))
if not ok then
vim.notify("Failed to add plugin " .. plugins .. ": " .. err, vim.log.levels.ERROR)
end
return
end
if type(plugins) == "table" then
-- Check if it's a single plugin spec
if is_single_plugin_spec(plugins) then
local plugin = plugins
local src = plugin[1] or plugin.src
if not src or src == "" then
error("Plugin src is required and cannot be empty")
end
-- Normalize and modify in place
local normalized_src = normalize_url(src)
local original_src = src
-- Store build spec for event handling
if plugin.build then
plugin.data = plugin.data or {}
plugin.data.build = plugin.build
end
-- Create clean spec for vim.pack.add
local pack_spec = {}
for k, v in pairs(plugins) do
if k ~= "config" and k ~= "setup" and k ~= "module" and k ~= "build" and k ~= 1 then
pack_spec[k] = v
end
end
pack_spec.src = normalized_src
local ok, err = pcall(vim.pack.add, pack_spec)
if not ok then
vim.notify("Failed to add plugin " .. src .. ": " .. err, vim.log.levels.ERROR)
return
end
-- Do setup if needed
M._do_setup(plugin, original_src)
return
end
-- Multiple plugins - modify in place and collect setup specs
local setup_specs = {}
for i, plugin in ipairs(plugins) do
if type(plugin) == "string" then
if not plugin or plugin == "" then
vim.notify("Plugin " .. i .. " is empty or nil, skipping", vim.log.levels.WARN)
-- Replace with nil to skip
plugins[i] = nil
goto continue
end
-- Convert string to table
plugins[i] = { src = normalize_url(plugin) }
elseif type(plugin) == "table" then
local src = plugin[1] or plugin.src
if not src or type(src) ~= "string" or src == "" then
vim.notify("Plugin " .. i .. " missing or invalid src, skipping", vim.log.levels.WARN)
plugins[i] = nil
goto continue
end
local original_src = src
local normalized_src = normalize_url(src)
-- Store build spec for event handling
if plugin.build then
plugin.data = plugin.data or {}
plugin.data.build = plugin.build
end
-- Cache setup info if needed
if plugin.config or plugin.setup then
setup_specs[#setup_specs + 1] = {
spec = {
config = plugin.config, -- Save the actual function/table
setup = plugin.setup,
module = plugin.module,
},
original_src = original_src
}
end
-- Normalize src and remove our custom keys
plugin.src = normalized_src
plugin.config = nil
plugin.setup = nil
plugin.module = nil
plugin.build = nil
plugin[1] = nil -- Remove positional src
else
vim.notify("Plugin " .. i .. " must be string or table, got " .. type(plugin), vim.log.levels.WARN)
plugins[i] = nil
end
::continue::
end
-- Remove nil entries
local clean_plugins = {}
for _, plugin in ipairs(plugins) do
if plugin then
clean_plugins[#clean_plugins + 1] = plugin
end
end
-- Download all plugins in one call
if #clean_plugins > 0 then
local ok, err = pcall(vim.pack.add, clean_plugins)
if not ok then
vim.notify("Failed to add plugin batch: " .. err, vim.log.levels.ERROR)
return
end
end
-- Now do setup for plugins that need it
for _, setup_info in ipairs(setup_specs) do
M._do_setup(setup_info.spec, setup_info.original_src)
end
else
error("plugins must be a string or table, got: " .. type(plugins))
end
end
-- Alias for convenience
M.use = M.add
function M._run_build_command(build_cmd, plugin_dir, plugin_name, on_success)
if not build_cmd then return end
local cmd_type = type(build_cmd)
if cmd_type == "string" then
vim.notify("Building " .. plugin_name .. "...", vim.log.levels.INFO)
vim.fn.jobstart({
"sh", "-c",
string.format("cd '%s' && %s", plugin_dir, build_cmd)
}, {
on_exit = function(_, exit_code)
if exit_code == 0 then
vim.notify("Build completed for " .. plugin_name, vim.log.levels.INFO)
if on_success then on_success() end
else
vim.notify("Build failed for " .. plugin_name, vim.log.levels.ERROR)
end
end
})
elseif cmd_type == "function" then
local ok, err = pcall(build_cmd, plugin_dir)
if ok and on_success then
on_success()
elseif not ok then
vim.notify("Build function failed for " .. plugin_name .. ": " .. err, vim.log.levels.ERROR)
end
end
end
--buf = 1,
--data = {
-- kind = "delete",
-- path = "/Users/zoomiti/.local/share/nvim/site/pack/core/opt/blink.cmp",
-- spec = {
-- name = "blink.cmp",
-- src = "https://github.com/Saghen/blink.cmp.git"
-- }
--},
--event = "PackChanged",
--file = "/Users/zoomiti/.local/share/nvim/site/pack/core/opt/blink.cmp",
--id = 14,
--match = "/Users/zoomiti/.local/share/nvim/site/pack/core/opt/blink.cmp"
local augroup = vim.api.nvim_create_augroup('most_basic_build_system', { clear = false })
vim.api.nvim_create_autocmd("PackChanged", {
group = augroup,
pattern = "*",
callback = function(ev)
if ev.data.kind ~= 'delete' and ev.data.spec.data and ev.data.spec.data.build and type(ev.data.spec.data.build) == "string" then
vim.system({ "sh", "-c" }, { stdin = 'cd' .. ev.data.path .. ' && ' .. ev.data.spec.data.build })
end
end
})
return M

3
.gitignore vendored

@ -1 +1,2 @@
*
.config/*
!.config/nvim

Loading…