test refactor

This commit is contained in:
DBLA 2025-12-14 10:29:57 -08:00
parent 095c40f8c6
commit 0797eeb51a
16 changed files with 1098 additions and 2254 deletions

2265
init.lua

File diff suppressed because it is too large Load Diff

16
lua/config/autocmds.lua Normal file
View File

@ -0,0 +1,16 @@
-- Autocommands configuration
-- See `:help lua-guide-autocommands`
-- Highlight when yanking (copying) text
vim.api.nvim_create_autocmd('TextYankPost', {
desc = 'Highlight when yanking (copying) text',
group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }),
callback = function()
vim.hl.on_yank()
end,
})
-- Refresh files if changed outside of Neovim
vim.fn.timer_start(2000, function()
vim.cmd 'silent! checktime'
end, { ['repeat'] = -1 })

27
lua/config/keymaps.lua Normal file
View File

@ -0,0 +1,27 @@
-- Keymaps configuration
-- See `:help vim.keymap.set()`
-- Clear highlights on search when pressing <Esc> in normal mode
vim.keymap.set('n', '<Esc>', '<cmd>nohlsearch<CR>')
-- Diagnostic keymaps
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' })
-- Go to first character in line
vim.keymap.set('', '<Leader>h', '^')
-- Go to last character in line
vim.keymap.set('', '<Leader>l', 'g_')
-- Save all files
vim.keymap.set('n', '<leader>w', ':wa <CR>')
-- Exit terminal mode with easier shortcut
vim.keymap.set('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Exit terminal mode' })
-- Keybinds to make split navigation easier
-- Use CTRL+<hjkl> to switch between windows
vim.keymap.set('n', '<C-h>', '<C-w><C-h>', { desc = 'Move focus to the left window' })
vim.keymap.set('n', '<C-l>', '<C-w><C-l>', { desc = 'Move focus to the right window' })
vim.keymap.set('n', '<C-j>', '<C-w><C-j>', { desc = 'Move focus to the lower window' })
vim.keymap.set('n', '<C-k>', '<C-w><C-k>', { desc = 'Move focus to the upper window' })

66
lua/config/options.lua Normal file
View File

@ -0,0 +1,66 @@
-- Options configuration
-- See `:help vim.o` and `:help option-list`
-- Set leader key (must happen before plugins are loaded)
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
-- Set to true if you have a Nerd Font installed
vim.g.have_nerd_font = true
-- Line numbers
vim.o.number = true
vim.o.relativenumber = true
-- Enable mouse mode
vim.o.mouse = 'a'
-- Don't show mode (already in status line)
vim.o.showmode = false
-- Sync clipboard between OS and Neovim
vim.schedule(function()
vim.o.clipboard = 'unnamedplus'
end)
-- Enable break indent
vim.o.breakindent = true
-- Save undo history
vim.o.undofile = true
-- Case-insensitive searching UNLESS \C or capital letters in search term
vim.o.ignorecase = true
vim.o.smartcase = true
-- Keep signcolumn on by default
vim.o.signcolumn = 'yes'
-- Decrease update time
vim.o.updatetime = 250
-- Decrease mapped sequence wait time
vim.o.timeoutlen = 300
-- Configure how new splits should be opened
vim.o.splitright = true
vim.o.splitbelow = true
-- Sets how neovim will display certain whitespace characters
vim.o.list = true
vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '' }
-- Preview substitutions live, as you type
vim.o.inccommand = 'split'
-- Show which line your cursor is on
vim.o.cursorline = true
-- Minimal number of screen lines to keep above and below the cursor
vim.o.scrolloff = 10
-- Raise dialog for unsaved changes instead of failing
vim.o.confirm = true
-- Auto-read files changed outside of Neovim
vim.opt.autoread = true

193
lua/plugins/completion.lua Normal file
View File

@ -0,0 +1,193 @@
-- Autocompletion configuration
--
return {
'saghen/blink.cmp',
dependencies = {
'rafamadriz/friendly-snippets',
'echasnovski/mini.icons',
'onsails/lspkind-nvim',
},
event = 'VeryLazy',
version = '*',
opts = {
enabled = function()
return not vim.tbl_contains({ 'oil' }, vim.bo.filetype)
end,
keymap = {
preset = 'enter',
['<C-h>'] = {
function(cmp)
cmp.show_documentation()
end,
},
['<tab>'] = {},
},
signature = { enabled = false },
appearance = {
-- use_nvim_cmp_as_default = true,
nerd_font_variant = 'mono',
},
sources = {
default = { 'lsp', 'path', 'snippets', 'buffer' },
},
cmdline = {
keymap = {
['<Tab>'] = { 'show', 'select_next' },
['<S-Tab>'] = { 'select_prev' },
['<cr>'] = { 'select_and_accept', 'fallback' },
['<space>'] = { 'select_and_accept', 'fallback' },
['<right>'] = { 'select_and_accept', 'fallback' },
['<down>'] = { 'select_next', 'fallback' },
['<up>'] = { 'select_prev', 'fallback' },
['<esc>'] = {
'cancel',
function()
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<C-c>', true, false, true), 'n', true)
end,
},
},
sources = function()
local type = vim.fn.getcmdtype()
-- Search forward and backward
if type == '/' or type == '?' then
return {}
end
-- Commands
if type == ':' or type == '@' then
return { 'cmdline', 'path' }
end
return {}
end,
completion = { ghost_text = { enabled = false } },
},
completion = {
trigger = {
show_on_trigger_character = true,
},
documentation = {
auto_show = true,
auto_show_delay_ms = 200,
window = {
border = 'rounded',
winhighlight = 'Normal:Normal,FloatBorder:FloatBorder,CursorLine:BlinkCmpDocCursorLine,Search:None',
},
},
list = {
selection = {
auto_insert = false,
},
},
menu = {
border = 'rounded',
draw = {
gap = 2,
components = {
kind_icon = {
ellipsis = false,
highlight = function(ctx)
local _, hl, _ = require('mini.icons').get('lsp', ctx.kind)
return hl
end,
text = function(ctx)
local icon = require('lspkind').symbolic(ctx.kind, { mode = 'symbol' })
return icon .. ctx.icon_gap
end,
},
},
},
winhighlight = 'Normal:Normal,FloatBorder:FloatBorder,CursorLine:BlinkCmpMenuSelection,Search:None',
},
},
},
opts_extend = { 'sources.default' },
}
-- return {
-- {
-- 'saghen/blink.cmp',
-- event = 'VimEnter',
-- version = '1.*',
-- dependencies = {
-- {
-- 'L3MON4D3/LuaSnip',
-- version = '2.*',
-- build = (function()
-- if vim.fn.has 'win32' == 1 or vim.fn.executable 'make' == 0 then
-- return
-- end
-- return 'make install_jsregexp'
-- end)(),
-- opts = {},
-- },
-- 'folke/lazydev.nvim',
-- },
-- ---@module 'blink.cmp'
-- ---@type blink.cmp.Config
-- opts = {
-- keymap = {
-- preset = 'enter',
-- },
--
-- appearance = {
-- nerd_font_variant = 'mono',
-- },
--
-- completion = {
-- trigger = {
-- show_on_insert_on_trigger_character = true,
-- },
-- ghost_text = {
-- enabled = true,
-- },
-- menu = {
-- border = 'single',
-- draw = {
-- treesitter = { 'lsp' },
-- padding = { 0, 1 },
-- components = {
-- kind_icon = {
-- text = function(ctx)
-- local kind_icon, _, _ = require('mini.icons').get('lsp', ctx.kind)
-- return kind_icon
-- end,
-- highlight = function(ctx)
-- local _, hl, _ = require('mini.icons').get('lsp', ctx.kind)
-- return hl
-- end,
-- },
-- kind = {
-- highlight = function(ctx)
-- local _, hl, _ = require('mini.icons').get('lsp', ctx.kind)
-- return hl
-- end,
-- },
-- },
-- },
-- },
-- documentation = { auto_show = true, auto_show_delay_ms = 100, window = { border = 'single' } },
-- },
--
-- signature = { enabled = true, window = { border = 'single' } },
--
-- sources = {
-- default = { 'lsp', 'path', 'snippets', 'lazydev' },
-- providers = {
-- lazydev = { module = 'lazydev.integrations.blink', score_offset = 100 },
-- },
-- },
--
-- snippets = { preset = 'luasnip' },
--
-- fuzzy = {
-- sorts = {
-- 'exact',
-- 'score',
-- 'sort_text',
-- },
-- implementation = 'rust',
-- },
-- },
-- },
-- }

3
lua/plugins/debug.lua Normal file
View File

@ -0,0 +1,3 @@
-- Debug Adapter Protocol (DAP) configuration
-- Re-exports the existing kickstart debug config
return require 'kickstart.plugins.debug'

11
lua/plugins/deltaview.lua Normal file
View File

@ -0,0 +1,11 @@
return {
{
'kokusenz/deltaview.nvim',
opts = {
keyconfig = {
dv_toggle_keybind = '<leader>dd',
},
use_nerdfonts = false,
},
},
}

92
lua/plugins/editor.lua Normal file
View File

@ -0,0 +1,92 @@
-- Editor enhancement plugins
return {
-- Detect tabstop and shiftwidth automatically (removed guess-indent.nvim duplicate)
'tpope/vim-sleuth',
-- Highlight TODOs in comments
{
'folke/todo-comments.nvim',
event = 'VimEnter',
dependencies = { 'nvim-lua/plenary.nvim' },
opts = { signs = false },
},
-- Collection of mini plugins
{
'echasnovski/mini.nvim',
config = function()
-- Better Around/Inside textobjects
require('mini.ai').setup { n_lines = 500 }
-- Add/delete/replace surroundings
require('mini.surround').setup()
-- Simple statusline
local statusline = require 'mini.statusline'
statusline.setup { use_icons = vim.g.have_nerd_font }
---@diagnostic disable-next-line: duplicate-set-field
statusline.section_location = function()
return '%2l:%-2v'
end
-- File navigator
require('mini.files').setup()
end,
keys = {
{ '<leader>ff', '<cmd>lua MiniFiles.open()<cr>', desc = 'Toggle file navigator' },
},
},
-- Autopairs
{
'windwp/nvim-autopairs',
event = 'InsertEnter',
dependencies = { 'hrsh7th/nvim-cmp' },
config = function()
require('nvim-autopairs').setup {}
end,
},
-- Diagnostics list
{
'folke/trouble.nvim',
dependencies = { 'nvim-tree/nvim-web-devicons' },
opts = {},
keys = {
{ '<leader>xx', '<cmd>Trouble diagnostics toggle<cr>', desc = 'Diagnostics (Trouble)' },
{ '<leader>xw', '<cmd>Trouble diagnostics toggle filter.buf=0<cr>', desc = 'Buffer Diagnostics (Trouble)' },
{ '<leader>xq', '<cmd>Trouble qflist toggle<cr>', desc = 'Quickfix List (Trouble)' },
{ '<leader>xl', '<cmd>Trouble loclist toggle<cr>', desc = 'Location List (Trouble)' },
{ 'gR', '<cmd>Trouble lsp_references toggle<cr>', desc = 'LSP References (Trouble)' },
},
},
-- Linting
{
'mfussenegger/nvim-lint',
event = { 'BufReadPre', 'BufNewFile' },
config = function()
local lint = require 'lint'
lint.linters_by_ft = {
-- Add linters per filetype as needed
}
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, {
group = vim.api.nvim_create_augroup('lint', { clear = true }),
callback = function()
if vim.opt_local.modifiable:get() then
lint.try_lint()
end
end,
})
end,
},
-- Indentation guides
{
'lukas-reineke/indent-blankline.nvim',
main = 'ibl',
opts = {},
},
}

19
lua/plugins/git.lua Normal file
View File

@ -0,0 +1,19 @@
-- Git integration plugins
return {
-- Git commands in nvim
'tpope/vim-fugitive',
-- Git signs in the gutter
{
'lewis6991/gitsigns.nvim',
opts = {
signs = {
add = { text = '+' },
change = { text = '~' },
delete = { text = '_' },
topdelete = { text = '' },
changedelete = { text = '~' },
},
},
},
}

192
lua/plugins/lsp.lua Normal file
View File

@ -0,0 +1,192 @@
-- LSP Configuration
return {
{
-- Lua LSP for Neovim config
'folke/lazydev.nvim',
ft = 'lua',
opts = {
library = {
{ path = '${3rd}/luv/library', words = { 'vim%.uv' } },
},
},
},
{
-- Main LSP Configuration
'neovim/nvim-lspconfig',
dependencies = {
{ 'mason-org/mason.nvim', opts = {} },
'mason-org/mason-lspconfig.nvim',
'WhoIsSethDaniel/mason-tool-installer.nvim',
{ 'j-hui/fidget.nvim', opts = {} },
'saghen/blink.cmp',
},
config = function()
-- LSP attach autocmd for keymaps and features
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('kickstart-lsp-attach', { clear = true }),
callback = function(event)
local map = function(keys, func, desc, mode)
mode = mode or 'n'
vim.keymap.set(mode, keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
end
-- LSP keymaps
map('grn', vim.lsp.buf.rename, '[R]e[n]ame')
map('gra', vim.lsp.buf.code_action, '[G]oto Code [A]ction', { 'n', 'x' })
map('grr', require('telescope.builtin').lsp_references, '[G]oto [R]eferences')
map('gri', require('telescope.builtin').lsp_implementations, '[G]oto [I]mplementation')
map('grd', require('telescope.builtin').lsp_definitions, '[G]oto [D]efinition')
map('grD', vim.lsp.buf.declaration, '[G]oto [D]eclaration')
map('gO', require('telescope.builtin').lsp_document_symbols, 'Open Document Symbols')
map('gW', require('telescope.builtin').lsp_dynamic_workspace_symbols, 'Open Workspace Symbols')
map('grt', require('telescope.builtin').lsp_type_definitions, '[G]oto [T]ype Definition')
-- Helper function for method support check
local function client_supports_method(client, method, bufnr)
if vim.fn.has 'nvim-0.11' == 1 then
return client:supports_method(method, bufnr)
else
return client.supports_method(method, { bufnr = bufnr })
end
end
-- Document highlighting
local client = vim.lsp.get_client_by_id(event.data.client_id)
if client and client_supports_method(client, vim.lsp.protocol.Methods.textDocument_documentHighlight, event.buf) then
local highlight_augroup = vim.api.nvim_create_augroup('kickstart-lsp-highlight', { clear = false })
vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
buffer = event.buf,
group = highlight_augroup,
callback = vim.lsp.buf.document_highlight,
})
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
buffer = event.buf,
group = highlight_augroup,
callback = vim.lsp.buf.clear_references,
})
vim.api.nvim_create_autocmd('LspDetach', {
group = vim.api.nvim_create_augroup('kickstart-lsp-detach', { clear = true }),
callback = function(event2)
vim.lsp.buf.clear_references()
vim.api.nvim_clear_autocmds { group = 'kickstart-lsp-highlight', buffer = event2.buf }
end,
})
end
-- Inlay hints toggle
if client and client_supports_method(client, vim.lsp.protocol.Methods.textDocument_inlayHint, event.buf) then
map('<leader>th', function()
vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled { bufnr = event.buf })
end, '[T]oggle Inlay [H]ints')
end
end,
})
-- Diagnostic config
vim.diagnostic.config {
severity_sort = true,
float = { border = 'rounded', source = 'if_many' },
underline = { severity = vim.diagnostic.severity.ERROR },
signs = vim.g.have_nerd_font and {
text = {
[vim.diagnostic.severity.ERROR] = '󰅚 ',
[vim.diagnostic.severity.WARN] = '󰀪 ',
[vim.diagnostic.severity.INFO] = '󰋽 ',
[vim.diagnostic.severity.HINT] = '󰌶 ',
},
} or {},
virtual_text = {
source = 'if_many',
spacing = 2,
format = function(diagnostic)
return diagnostic.message
end,
},
}
-- Get capabilities from blink.cmp
local capabilities = require('blink.cmp').get_lsp_capabilities()
-- Sourcekit setup for Swift
local lspconfig = vim.lsp.config
lspconfig('sourcekit', {
capabilities = capabilities,
root_dir = function(_, callback)
callback(require('lspconfig.util').root_pattern 'Package.swift'(vim.fn.getcwd()) or require('lspconfig.util').find_git_ancestor(vim.fn.getcwd()))
end,
cmd = { vim.trim(vim.fn.system 'xcrun -f sourcekit-lsp') },
})
vim.lsp.enable 'sourcekit'
-- Server configurations
local servers = {
clangd = {},
gopls = {},
rust_analyzer = {},
jedi_language_server = {},
ts_ls = {},
lua_ls = {
settings = {
Lua = {
completion = {
callSnippet = 'Replace',
},
},
},
},
}
-- Setup mason tools
local ensure_installed = vim.tbl_keys(servers or {})
vim.list_extend(ensure_installed, { 'stylua' })
require('mason-tool-installer').setup { ensure_installed = ensure_installed }
require('mason-lspconfig').setup {
ensure_installed = {},
automatic_installation = false,
handlers = {
function(server_name)
local server = servers[server_name] or {}
server.capabilities = vim.tbl_deep_extend('force', {}, capabilities, server.capabilities or {})
require('lspconfig')[server_name].setup(server)
end,
},
}
end,
},
{ -- Autoformat
'stevearc/conform.nvim',
event = { 'BufWritePre' },
cmd = { 'ConformInfo' },
keys = {
{
'<leader>f',
function()
require('conform').format { async = true, lsp_format = 'fallback' }
end,
mode = '',
desc = '[F]ormat buffer',
},
},
opts = {
notify_on_error = false,
format_on_save = function(bufnr)
local disable_filetypes = { c = true, cpp = true }
if disable_filetypes[vim.bo[bufnr].filetype] then
return nil
else
return {
timeout_ms = 500,
lsp_format = 'fallback',
}
end
end,
formatters_by_ft = {
lua = { 'stylua' },
html = { 'djlint' },
},
},
},
}

108
lua/plugins/misc.lua Normal file
View File

@ -0,0 +1,108 @@
-- Miscellaneous plugins
return {
-- Database client
{
'kristijanhusak/vim-dadbod-ui',
dependencies = {
{ 'tpope/vim-dadbod', lazy = true },
{ 'kristijanhusak/vim-dadbod-completion', ft = { 'sql', 'mysql', 'plsql' }, lazy = true },
},
cmd = { 'DBUI', 'DBUIToggle', 'DBUIAddConnection', 'DBUIFindBuffer' },
init = function()
vim.g.db_ui_use_nerd_fonts = 1
end,
},
-- Wiki/notes
{
'lervag/wiki.vim',
init = function()
vim.g.wiki_root = '~/Documents/Developer/'
end,
},
-- Import picker
{
'piersolenski/import.nvim',
dependencies = { 'nvim-telescope/telescope.nvim' },
opts = { picker = 'telescope' },
keys = {
{ '<leader>i', function() require('import').pick() end, desc = 'Import' },
},
},
-- Type hierarchy viewer
{
'retran/meow.yarn.nvim',
dependencies = { 'MunifTanjim/nui.nvim' },
config = function()
require('meow.yarn').setup {}
vim.keymap.set('n', '<leader>yS', '<Cmd>MeowYarn type super<CR>', { desc = 'Yarn: Super Types' })
vim.keymap.set('n', '<leader>ys', '<Cmd>MeowYarn type sub<CR>', { desc = 'Yarn: Sub Types' })
vim.keymap.set('n', '<leader>yC', '<Cmd>MeowYarn call callers<CR>', { desc = 'Yarn: Callers' })
vim.keymap.set('n', '<leader>yc', '<Cmd>MeowYarn call callees<CR>', { desc = 'Yarn: Callees' })
end,
},
-- Xcode development
{
'wojciech-kulik/xcodebuild.nvim',
dependencies = {
'MunifTanjim/nui.nvim',
'nvim-treesitter/nvim-treesitter',
},
config = function()
require('xcodebuild').setup {}
vim.keymap.set('n', '<leader>X', '<cmd>XcodebuildPicker<cr>', { desc = 'Show Xcodebuild Actions' })
vim.keymap.set('n', '<leader>xf', '<cmd>XcodebuildProjectManager<cr>', { desc = 'Show Project Manager Actions' })
vim.keymap.set('n', '<leader>xb', '<cmd>XcodebuildBuild<cr>', { desc = 'Build Project' })
vim.keymap.set('n', '<leader>xB', '<cmd>XcodebuildBuildForTesting<cr>', { desc = 'Build For Testing' })
vim.keymap.set('n', '<leader>xr', '<cmd>XcodebuildBuildRun<cr>', { desc = 'Build & Run Project' })
vim.keymap.set('n', '<leader>xt', '<cmd>XcodebuildTest<cr>', { desc = 'Run Tests' })
vim.keymap.set('v', '<leader>xt', '<cmd>XcodebuildTestSelected<cr>', { desc = 'Run Selected Tests' })
vim.keymap.set('n', '<leader>xT', '<cmd>XcodebuildTestClass<cr>', { desc = 'Run Current Test Class' })
vim.keymap.set('n', '<leader>x.', '<cmd>XcodebuildTestRepeat<cr>', { desc = 'Repeat Last Test Run' })
vim.keymap.set('n', '<leader>xl', '<cmd>XcodebuildToggleLogs<cr>', { desc = 'Toggle Xcodebuild Logs' })
vim.keymap.set('n', '<leader>xc', '<cmd>XcodebuildToggleCodeCoverage<cr>', { desc = 'Toggle Code Coverage' })
vim.keymap.set('n', '<leader>xC', '<cmd>XcodebuildShowCodeCoverageReport<cr>', { desc = 'Show Code Coverage Report' })
vim.keymap.set('n', '<leader>xe', '<cmd>XcodebuildTestExplorerToggle<cr>', { desc = 'Toggle Test Explorer' })
vim.keymap.set('n', '<leader>xs', '<cmd>XcodebuildFailingSnapshots<cr>', { desc = 'Show Failing Snapshots' })
vim.keymap.set('n', '<leader>xp', '<cmd>XcodebuildPreviewGenerateAndShow<cr>', { desc = 'Generate Preview' })
vim.keymap.set('n', '<leader>x<cr>', '<cmd>XcodebuildPreviewToggle<cr>', { desc = 'Toggle Preview' })
vim.keymap.set('n', '<leader>xd', '<cmd>XcodebuildSelectDevice<cr>', { desc = 'Select Device' })
vim.keymap.set('n', '<leader>xq', '<cmd>Telescope quickfix<cr>', { desc = 'Show QuickFix List' })
vim.keymap.set('n', '<leader>xa', '<cmd>XcodebuildCodeActions<cr>', { desc = 'Show Code Actions' })
end,
},
-- OpenCode AI assistant
{
'NickvanDyke/opencode.nvim',
dependencies = {
{ 'folke/snacks.nvim', opts = { input = { enabled = true } } },
},
---@type opencode.Opts
opts = {},
config = function()
vim.keymap.set({ 'n', 'x' }, '<leader>oa', function()
require('opencode').ask('@this: ', { submit = true })
end, { desc = 'Ask opencode' })
vim.keymap.set({ 'n', 'x' }, '<leader>os', function()
require('opencode').select()
end, { desc = 'Execute opencode action…' })
vim.keymap.set({ 'n', 'x' }, 'ga', function()
require('opencode').prompt '@this'
end, { desc = 'Add to opencode' })
vim.keymap.set({ 'n', 't' }, '<leader>ot', function()
require('opencode').toggle()
end, { desc = 'Toggle opencode' })
vim.keymap.set('n', '<S-C-u>', function()
require('opencode').command 'session.half.page.up'
end, { desc = 'opencode half page up' })
vim.keymap.set('n', '<S-C-d>', function()
require('opencode').command 'session.half.page.down'
end, { desc = 'opencode half page down' })
end,
},
}

View File

@ -0,0 +1,57 @@
-- Navigation and file management plugins
return {
-- Tmux integration
{ 'christoomey/vim-tmux-navigator' },
-- File explorer (edit directories like buffers)
{
'stevearc/oil.nvim',
dependencies = { { 'echasnovski/mini.icons', opts = {} } },
lazy = false,
config = function()
require('oil').setup {
view_options = { show_hidden = true },
}
vim.keymap.set('n', '-', '<CMD>Oil<CR>', { desc = 'Open parent directory' })
end,
},
-- File tagging/bookmarks
{
'cbochs/grapple.nvim',
dependencies = { { 'nvim-tree/nvim-web-devicons', lazy = true } },
opts = {
scope = 'git_branch',
icons = false,
},
event = { 'BufReadPost', 'BufNewFile' },
cmd = 'Grapple',
keys = {
{ '<leader>m', '<cmd>Grapple toggle<cr>', desc = 'Grapple toggle tag' },
{ '<leader>k', '<cmd>Grapple toggle_tags<cr>', desc = 'Grapple toggle tags' },
{ '<leader>K', '<cmd>Grapple toggle_scopes<cr>', desc = 'Grapple toggle scopes' },
{ '<leader>j', '<cmd>Grapple cycle forward<cr>', desc = 'Grapple cycle forward' },
{ '<leader>J', '<cmd>Grapple cycle backward<cr>', desc = 'Grapple cycle backward' },
{ '<leader>1', '<cmd>Grapple select index=1<cr>', desc = 'Grapple select 1' },
{ '<leader>2', '<cmd>Grapple select index=2<cr>', desc = 'Grapple select 2' },
{ '<leader>3', '<cmd>Grapple select index=3<cr>', desc = 'Grapple select 3' },
{ '<leader>4', '<cmd>Grapple select index=4<cr>', desc = 'Grapple select 4' },
{ '<leader>5', '<cmd>Grapple select index=5<cr>', desc = 'Grapple select 5' },
},
},
-- Jump motions
{
'folke/flash.nvim',
event = 'VeryLazy',
---@type Flash.Config
opts = {},
keys = {
{ 's', mode = { 'n', 'x', 'o' }, function() require('flash').jump() end, desc = 'Flash' },
{ 'S', mode = { 'n', 'x', 'o' }, function() require('flash').treesitter() end, desc = 'Flash Treesitter' },
{ 'r', mode = 'o', function() require('flash').remote() end, desc = 'Remote Flash' },
{ 'R', mode = { 'o', 'x' }, function() require('flash').treesitter_search() end, desc = 'Treesitter Search' },
{ '<c-s>', mode = { 'c' }, function() require('flash').toggle() end, desc = 'Toggle Flash Search' },
},
},
}

135
lua/plugins/snacks.lua Normal file
View File

@ -0,0 +1,135 @@
-- Snacks.nvim - Multi-purpose utilities
return {
{
'folke/snacks.nvim',
priority = 1000,
lazy = false,
---@type snacks.Config
opts = {
image = { enabled = true },
bigfile = { enabled = true },
dashboard = { enabled = true },
indent = { enabled = true },
input = { enabled = true },
notifier = { enabled = true, timeout = 3000 },
quickfile = { enabled = true },
scroll = { enabled = true },
statuscolumn = { enabled = true },
words = { enabled = true },
terminal = { win = { style = 'float' } },
},
keys = {
-- Pickers & Explorer
{ '<leader><space>', function() Snacks.picker.smart() end, desc = 'Smart Find Files' },
{ '<leader>,', function() Snacks.picker.buffers() end, desc = 'Buffers' },
{ '<leader>:', function() Snacks.picker.command_history() end, desc = 'Command History' },
{ '<leader>n', function() Snacks.picker.notifications() end, desc = 'Notification History' },
{ '<leader>e', function() Snacks.explorer() end, desc = 'File Explorer' },
-- Find
{ '<leader>fb', function() Snacks.picker.buffers() end, desc = 'Buffers' },
{ '<leader>fc', function() Snacks.picker.files { cwd = vim.fn.stdpath 'config' } end, desc = 'Find Config File' },
{ '<leader>fg', function() Snacks.picker.git_files() end, desc = 'Find Git Files' },
{ '<leader>fp', function() Snacks.picker.projects() end, desc = 'Projects' },
{ '<leader>fr', function() Snacks.picker.recent() end, desc = 'Recent' },
-- Git
{ '<leader>gb', function() Snacks.picker.git_branches() end, desc = 'Git Branches' },
{ '<leader>gl', function() Snacks.picker.git_log() end, desc = 'Git Log' },
{ '<leader>gL', function() Snacks.picker.git_log_line() end, desc = 'Git Log Line' },
{ '<leader>gs', function() Snacks.picker.git_status() end, desc = 'Git Status' },
{ '<leader>gS', function() Snacks.picker.git_stash() end, desc = 'Git Stash' },
{ '<leader>gd', function() Snacks.picker.git_diff() end, desc = 'Git Diff (Hunks)' },
{ '<leader>gf', function() Snacks.picker.git_log_file() end, desc = 'Git Log File' },
-- GitHub
{ '<leader>gi', function() Snacks.picker.gh_issue() end, desc = 'GitHub Issues (open)' },
{ '<leader>gI', function() Snacks.picker.gh_issue { state = 'all' } end, desc = 'GitHub Issues (all)' },
{ '<leader>gp', function() Snacks.picker.gh_pr() end, desc = 'GitHub Pull Requests (open)' },
{ '<leader>gP', function() Snacks.picker.gh_pr { state = 'all' } end, desc = 'GitHub Pull Requests (all)' },
-- Search
{ '<leader>sb', function() Snacks.picker.lines() end, desc = 'Buffer Lines' },
{ '<leader>sB', function() Snacks.picker.grep_buffers() end, desc = 'Grep Open Buffers' },
{ '<leader>sw', function() Snacks.picker.grep_word() end, desc = 'Visual selection or word', mode = { 'n', 'x' } },
{ '<leader>s"', function() Snacks.picker.registers() end, desc = 'Registers' },
{ '<leader>s/', function() Snacks.picker.search_history() end, desc = 'Search History' },
{ '<leader>sa', function() Snacks.picker.autocmds() end, desc = 'Autocmds' },
{ '<leader>sc', function() Snacks.picker.command_history() end, desc = 'Command History' },
{ '<leader>sC', function() Snacks.picker.commands() end, desc = 'Commands' },
{ '<leader>sD', function() Snacks.picker.diagnostics_buffer() end, desc = 'Buffer Diagnostics' },
{ '<leader>sH', function() Snacks.picker.highlights() end, desc = 'Highlights' },
{ '<leader>si', function() Snacks.picker.icons() end, desc = 'Icons' },
{ '<leader>sj', function() Snacks.picker.jumps() end, desc = 'Jumps' },
{ '<leader>sl', function() Snacks.picker.loclist() end, desc = 'Location List' },
{ '<leader>sm', function() Snacks.picker.marks() end, desc = 'Marks' },
{ '<leader>sM', function() Snacks.picker.man() end, desc = 'Man Pages' },
{ '<leader>sp', function() Snacks.picker.lazy() end, desc = 'Search for Plugin Spec' },
{ '<leader>sq', function() Snacks.picker.qflist() end, desc = 'Quickfix List' },
{ '<leader>sR', function() Snacks.picker.resume() end, desc = 'Resume' },
{ '<leader>su', function() Snacks.picker.undo() end, desc = 'Undo History' },
{ '<leader>uC', function() Snacks.picker.colorschemes() end, desc = 'Colorschemes' },
-- LSP (via Snacks picker)
{ 'gd', function() Snacks.picker.lsp_definitions() end, desc = 'Goto Definition' },
{ 'gD', function() Snacks.picker.lsp_declarations() end, desc = 'Goto Declaration' },
{ 'gr', function() Snacks.picker.lsp_references() end, nowait = true, desc = 'References' },
{ 'gI', function() Snacks.picker.lsp_implementations() end, desc = 'Goto Implementation' },
{ 'gy', function() Snacks.picker.lsp_type_definitions() end, desc = 'Goto T[y]pe Definition' },
{ 'gai', function() Snacks.picker.lsp_incoming_calls() end, desc = 'C[a]lls Incoming' },
{ 'gao', function() Snacks.picker.lsp_outgoing_calls() end, desc = 'C[a]lls Outgoing' },
{ '<leader>sS', function() Snacks.picker.lsp_workspace_symbols() end, desc = 'LSP Workspace Symbols' },
-- Other utilities
{ '<leader>z', function() Snacks.zen() end, desc = 'Toggle Zen Mode' },
{ '<leader>Z', function() Snacks.zen.zoom() end, desc = 'Toggle Zoom' },
{ '<leader>.', function() Snacks.scratch() end, desc = 'Toggle Scratch Buffer' },
{ '<leader>S', function() Snacks.scratch.select() end, desc = 'Select Scratch Buffer' },
{ '<leader>bd', function() Snacks.bufdelete() end, desc = 'Delete Buffer' },
{ '<leader>cR', function() Snacks.rename.rename_file() end, desc = 'Rename File' },
{ '<leader>gB', function() Snacks.gitbrowse() end, desc = 'Git Browse', mode = { 'n', 'v' } },
{ '<leader>gg', function() Snacks.lazygit() end, desc = 'Lazygit' },
{ '<leader>un', function() Snacks.notifier.hide() end, desc = 'Dismiss All Notifications' },
{ '<c-/>', function() Snacks.terminal() end, desc = 'Toggle Terminal' },
{ '<c-_>', function() Snacks.terminal() end, desc = 'which_key_ignore' },
{ ']]', function() Snacks.words.jump(vim.v.count1) end, desc = 'Next Reference', mode = { 'n', 't' } },
{ '[[', function() Snacks.words.jump(-vim.v.count1) end, desc = 'Prev Reference', mode = { 'n', 't' } },
{
'<leader>N',
desc = 'Neovim News',
function()
Snacks.win {
file = vim.api.nvim_get_runtime_file('doc/news.txt', false)[1],
width = 0.6,
height = 0.6,
wo = { spell = false, wrap = false, signcolumn = 'yes', statuscolumn = ' ', conceallevel = 3 },
}
end,
},
},
init = function()
vim.api.nvim_create_autocmd('User', {
pattern = 'VeryLazy',
callback = function()
-- Setup globals for debugging
_G.dd = function(...) Snacks.debug.inspect(...) end
_G.bt = function() Snacks.debug.backtrace() end
vim.print = _G.dd
-- Toggle mappings
Snacks.toggle.option('spell', { name = 'Spelling' }):map '<leader>us'
Snacks.toggle.option('wrap', { name = 'Wrap' }):map '<leader>uw'
Snacks.toggle.option('relativenumber', { name = 'Relative Number' }):map '<leader>uL'
Snacks.toggle.diagnostics():map '<leader>ud'
Snacks.toggle.line_number():map '<leader>ul'
Snacks.toggle.option('conceallevel', { off = 0, on = vim.o.conceallevel > 0 and vim.o.conceallevel or 2 }):map '<leader>uc'
Snacks.toggle.treesitter():map '<leader>uT'
Snacks.toggle.option('background', { off = 'light', on = 'dark', name = 'Dark Background' }):map '<leader>ub'
Snacks.toggle.inlay_hints():map '<leader>uh'
Snacks.toggle.indent():map '<leader>ug'
Snacks.toggle.dim():map '<leader>uD'
end,
})
end,
},
}

61
lua/plugins/telescope.lua Normal file
View File

@ -0,0 +1,61 @@
-- Telescope fuzzy finder configuration
return {
{
'nvim-telescope/telescope.nvim',
event = 'VimEnter',
dependencies = {
'nvim-lua/plenary.nvim',
{
'nvim-telescope/telescope-fzf-native.nvim',
build = 'make',
cond = function()
return vim.fn.executable 'make' == 1
end,
},
{ 'nvim-telescope/telescope-ui-select.nvim' },
{ 'nvim-tree/nvim-web-devicons', enabled = vim.g.have_nerd_font },
},
config = function()
require('telescope').setup {
extensions = {
['ui-select'] = {
require('telescope.themes').get_dropdown(),
},
},
}
pcall(require('telescope').load_extension, 'fzf')
pcall(require('telescope').load_extension, 'ui-select')
local builtin = require 'telescope.builtin'
vim.keymap.set('n', '<leader>sh', builtin.help_tags, { desc = '[S]earch [H]elp' })
vim.keymap.set('n', '<leader>sk', builtin.keymaps, { desc = '[S]earch [K]eymaps' })
vim.keymap.set('n', '<leader>sf', builtin.find_files, { desc = '[S]earch [F]iles' })
vim.keymap.set('n', '<leader>ss', builtin.builtin, { desc = '[S]earch [S]elect Telescope' })
vim.keymap.set('n', '<leader>sw', builtin.grep_string, { desc = '[S]earch current [W]ord' })
vim.keymap.set('n', '<leader>sg', builtin.live_grep, { desc = '[S]earch by [G]rep' })
vim.keymap.set('n', '<leader>sd', builtin.diagnostics, { desc = '[S]earch [D]iagnostics' })
vim.keymap.set('n', '<leader>sr', builtin.resume, { desc = '[S]earch [R]esume' })
vim.keymap.set('n', '<leader>s.', builtin.oldfiles, { desc = '[S]earch Recent Files ("." for repeat)' })
vim.keymap.set('n', '<leader><leader>', builtin.buffers, { desc = '[ ] Find existing buffers' })
vim.keymap.set('n', '<leader>/', function()
builtin.current_buffer_fuzzy_find(require('telescope.themes').get_dropdown {
winblend = 10,
previewer = false,
})
end, { desc = '[/] Fuzzily search in current buffer' })
vim.keymap.set('n', '<leader>s/', function()
builtin.live_grep {
grep_open_files = true,
prompt_title = 'Live Grep in Open Files',
}
end, { desc = '[S]earch [/] in Open Files' })
vim.keymap.set('n', '<leader>sn', function()
builtin.find_files { cwd = vim.fn.stdpath 'config' }
end, { desc = '[S]earch [N]eovim files' })
end,
},
}

View File

@ -0,0 +1,29 @@
-- Treesitter configuration for syntax highlighting and parsing
return {
{
'nvim-treesitter/nvim-treesitter',
build = ':TSUpdate',
main = 'nvim-treesitter.configs',
opts = {
ensure_installed = {
'bash',
'c',
'diff',
'html',
'lua',
'luadoc',
'markdown',
'markdown_inline',
'query',
'vim',
'vimdoc',
},
auto_install = true,
highlight = {
enable = true,
additional_vim_regex_highlighting = { 'ruby' },
},
indent = { enable = true, disable = { 'ruby' } },
},
},
}

78
lua/plugins/ui.lua Normal file
View File

@ -0,0 +1,78 @@
-- UI and appearance plugins
return {
-- Colorscheme
{
'rebelot/kanagawa.nvim',
priority = 1000,
config = function()
vim.cmd 'colorscheme kanagawa'
vim.cmd.hi 'Comment gui=none'
end,
},
-- Keybinding hints
{
'folke/which-key.nvim',
event = 'VimEnter',
opts = {
delay = 0,
icons = {
mappings = vim.g.have_nerd_font,
keys = vim.g.have_nerd_font and {} or {
Up = '<Up> ',
Down = '<Down> ',
Left = '<Left> ',
Right = '<Right> ',
C = '<C-…> ',
M = '<M-…> ',
D = '<D-…> ',
S = '<S-…> ',
CR = '<CR> ',
Esc = '<Esc> ',
ScrollWheelDown = '<ScrollWheelDown> ',
ScrollWheelUp = '<ScrollWheelUp> ',
NL = '<NL> ',
BS = '<BS> ',
Space = '<Space> ',
Tab = '<Tab> ',
F1 = '<F1>',
F2 = '<F2>',
F3 = '<F3>',
F4 = '<F4>',
F5 = '<F5>',
F6 = '<F6>',
F7 = '<F7>',
F8 = '<F8>',
F9 = '<F9>',
F10 = '<F10>',
F11 = '<F11>',
F12 = '<F12>',
},
},
spec = {
{ '<leader>c', group = '[C]ode' },
{ '<leader>d', group = '[D]ocument' },
{ '<leader>r', group = '[R]ename' },
{ '<leader>s', group = '[S]earch' },
{ '<leader>t', group = '[T]oggle' },
{ '<leader>h', group = 'Git [H]unk', mode = { 'n', 'v' } },
{ '<leader>g', group = '[G]it' },
{ '<leader>x', group = 'Trouble/Xcode' },
{ '<leader>f', group = '[F]ind/Format' },
{ '<leader>u', group = '[U]I Toggles' },
{ '<leader>o', group = '[O]pencode' },
{ '<leader>y', group = '[Y]arn (Type Hierarchy)' },
},
},
},
-- Icons
{ 'nvim-tree/nvim-web-devicons', enabled = vim.g.have_nerd_font },
{ 'echasnovski/mini.icons', opts = {} },
-- Gutter marks
{
'dimtion/guttermarks.nvim',
event = 'VeryLazy',
},
}