diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua index 1837119..b987df1 100644 --- a/.config/nvim/init.lua +++ b/.config/nvim/init.lua @@ -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', 'b', 'Pick buffers', { desc = "Buffers" }) + vim.keymap.set('n', 'f', 'Pick files', { 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({ + { "g", group = "git" }, + { "h", group = "git_hunk" } + }) + end + }, + "LunarWatcher/auto-pairs", + { "nvim-lua/plenary.nvim" }, + { + "NeogitOrg/neogit", + config = function() + vim.keymap.set('n', 'gs', 'Neogit kind=split_above_all', { desc = "Git Status" }) + vim.keymap.set('n', 'gp', 'Neogit pull', { desc = "Git Pull" }) + vim.keymap.set('n', 'gP', 'Neogit push', { 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', 'hs', gitsigns.stage_hunk, { desc = "(un)stage hunk" }) + map('n', 'hr', gitsigns.reset_hunk, { desc = "reset hunk" }) + + map('v', 'hs', function() + gitsigns.stage_hunk({ vim.fn.line('.'), vim.fn.line('v') }) + end, { desc = "(un)stage hunk" }) + + map('v', 'hr', function() + gitsigns.reset_hunk({ vim.fn.line('.'), vim.fn.line('v') }) + end, { desc = "reset hunk" }) + + --map('n', 'hS', gitsigns.stage_buffer) + --map('n', 'hR', gitsigns.reset_buffer) + map('n', 'hp', gitsigns.preview_hunk, { desc = "preview hunk" }) + map('n', 'hi', gitsigns.preview_hunk_inline, { desc = "preview hunk inline" }) + + map('n', 'hb', function() + gitsigns.blame_line({ full = true }) + end, { desc = "blame line" }) + + map('n', 'hd', gitsigns.diffthis, { desc = "diff buffer" }) + + map('n', 'hD', function() + gitsigns.diffthis('~') + end, { desc = "diff buffer to HEAD" }) + + map('n', 'hQ', function() gitsigns.setqflist('all') end, + { desc = "set qflist with changes in all files and cwd" }) + map('n', 'hq', gitsigns.setqflist, { desc = "set qflist with changes in current buffer" }) + + -- Toggles + map('n', 'tb', gitsigns.toggle_current_line_blame, { desc = "toggle current line blame" }) + map('n', '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", "/", "/\\%V", { desc = 'Search forward within visual selection' }) vim.keymap.set("x", "?", "?\\%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', 'b', 'Pick buffers', { desc = "Buffers" }) -vim.keymap.set('n', 'f', 'Pick files') - vim.keymap.set('n', 's', 'update ~/.config/nvim/init.luasource ~/.config/nvim/init.lua', { desc = "Update Config" }) vim.keymap.set('n', 'w', 'write', { desc = "Write File" }) --- Terminal Mode +-- {{{ Terminal Mode vim.keymap.set('t', '', '') vim.keymap.set('t', 'h', 'h') vim.keymap.set('t', 'j', 'j') @@ -65,26 +218,19 @@ vim.keymap.set('t', '', '') vim.keymap.set('t', '', '') vim.keymap.set('t', '', '') 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', '', 'i', { 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', 'gs', 'Neogit kind=split_above_all', { desc = "Git Status" }) -vim.keymap.set('n', 'gp', 'Neogit pull', { desc = "Git Pull" }) -vim.keymap.set('n', 'gP', 'Neogit push', { 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', 'hs', gitsigns.stage_hunk, { desc = "(un)stage hunk" }) - map('n', 'hr', gitsigns.reset_hunk, { desc = "reset hunk" }) - - map('v', 'hs', function() - gitsigns.stage_hunk({ vim.fn.line('.'), vim.fn.line('v') }) - end, { desc = "(un)stage hunk" }) - - map('v', 'hr', function() - gitsigns.reset_hunk({ vim.fn.line('.'), vim.fn.line('v') }) - end, { desc = "reset hunk" }) - - --map('n', 'hS', gitsigns.stage_buffer) - --map('n', 'hR', gitsigns.reset_buffer) - map('n', 'hp', gitsigns.preview_hunk, { desc = "preview hunk" }) - map('n', 'hi', gitsigns.preview_hunk_inline, { desc = "preview hunk inline" }) - - map('n', 'hb', function() - gitsigns.blame_line({ full = true }) - end, { desc = "blame line" }) - - map('n', 'hd', gitsigns.diffthis, { desc = "diff buffer" }) - - map('n', 'hD', function() - gitsigns.diffthis('~') - end, { desc = "diff buffer to HEAD" }) - - map('n', 'hQ', function() gitsigns.setqflist('all') end, - { desc = "set qflist with changes in all files and cwd" }) - map('n', 'hq', gitsigns.setqflist, { desc = "set qflist with changes in current buffer" }) - - -- Toggles - map('n', 'tb', gitsigns.toggle_current_line_blame, { desc = "toggle current line blame" }) - map('n', '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({ - { "g", group = "git" }, - { "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" }, "tt", toggle_terminal, { desc = "Toggles a floating terminal" }) + +-- }}} diff --git a/.config/nvim/lua/pack.lua b/.config/nvim/lua/pack.lua new file mode 100644 index 0000000..8278e0c --- /dev/null +++ b/.config/nvim/lua/pack.lua @@ -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 diff --git a/.gitignore b/.gitignore index 72e8ffc..4f53e37 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -* +.config/* +!.config/nvim