refactor: modularize nvim config into lua/config + lua/plugins

- Extract all options, keymaps, and autocommands into lua/config/
- Move every plugin spec into its own file under lua/plugins/
- Add custom inline orng colorscheme (dark + light) synced to macOS appearance
- Consolidate image.nvim into pdf-viewer.lua (fix duplicate spec)
- Fix image.nvim processor: magick -> magick_cli
- Remove conflicting nvim-autopairs (kept mini.pairs)
- Fix broken <C-f> keymap (was calling non-existent builtin cmd)
- Delete lua/kickstart/ and lua/custom/ folders
- Add colors/orng.vim + colors/orng-light.vim stubs for mid-session switching
- init.lua reduced from 1078 lines to 49 lines
This commit is contained in:
2026-02-26 11:54:58 +01:00
parent d36d07d224
commit 6ec8190beb
34 changed files with 1122 additions and 1600 deletions

2
colors/orng-light.vim Normal file
View File

@@ -0,0 +1,2 @@
" orng-light colorscheme stub
lua require("plugins.colorscheme")

2
colors/orng.vim Normal file
View File

@@ -0,0 +1,2 @@
" orng dark colorscheme stub
lua require("plugins.colorscheme")

1085
init.lua

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,11 @@
-- lua/config/autocmds.lua
-- Global autocommands
-- Highlight yanked text briefly
vim.api.nvim_create_autocmd('TextYankPost', {
desc = 'Highlight when yanking (copying) text',
group = vim.api.nvim_create_augroup('user-highlight-yank', { clear = true }),
callback = function()
vim.hl.on_yank()
end,
})

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

@@ -0,0 +1,31 @@
-- lua/config/keymaps.lua
-- Global keymaps (plugin keymaps live in their own plugin files)
local map = vim.keymap.set
-- Clear search highlights
map('n', '<Esc>', '<cmd>nohlsearch<CR>')
-- Diagnostics
map('n', '<leader>q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' })
-- Terminal
map('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Exit terminal mode' })
map('n', '<C-t>', ':split | terminal<CR>', { desc = '[T]erminal (horizontal split)' })
-- Window navigation
map('n', '<C-h>', '<C-w><C-h>', { desc = 'Move focus to the left window' })
map('n', '<C-l>', '<C-w><C-l>', { desc = 'Move focus to the right window' })
map('n', '<C-j>', '<C-w><C-j>', { desc = 'Move focus to the lower window' })
map('n', '<C-k>', '<C-w><C-k>', { desc = 'Move focus to the upper window' })
-- File explorer
map('n', '<C-e>', ':Neotree toggle<CR>', { desc = '[E]xplorer (toggle neotree)' })
-- LazyGit
map('n', '<C-g>', '<cmd>LazyGit<CR>', { desc = 'LazyGit' })
-- Telescope live grep (fixed: was using broken <cmd>builtin... syntax)
map('n', '<C-f>', function()
require('telescope.builtin').live_grep()
end, { desc = '[F]ind (live grep all files)' })

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

@@ -0,0 +1,49 @@
-- lua/config/options.lua
-- All vim options in one place
local opt = vim.opt
local o = vim.o
-- Leader keys are set in init.lua before lazy loads
-- UI
o.number = true
o.relativenumber = false
o.cursorline = true
o.signcolumn = 'yes'
o.showmode = false
o.scrolloff = 10
o.splitright = true
o.splitbelow = true
o.inccommand = 'split'
-- Characters
o.list = true
opt.listchars = { tab = '» ', trail = '·', nbsp = '' }
-- Mouse
o.mouse = 'a'
-- Clipboard (deferred to avoid startup lag)
vim.schedule(function()
o.clipboard = 'unnamedplus'
end)
-- Indentation
o.expandtab = true
o.smartindent = true
o.tabstop = 2
o.shiftwidth = 2
o.breakindent = true
-- Search
o.ignorecase = true
o.smartcase = true
-- Files
o.undofile = true
o.confirm = true
-- Performance
o.updatetime = 250
o.timeoutlen = 300

View File

@@ -1,14 +0,0 @@
-- Set custom maps
local map = vim.keymap.set
-- Horizontal split for terminal
map('n', '<C-t>', ':split | terminal<CR>', { desc = '[T]erminal (Horizontal Split)' })
-- Open neo-filetree
map('n', '<C-e>', ':Neotree toggle<CR>', { desc = '[E]xplorer (toggle neotree)' })
-- Open lazygit in overlay
map('n', '<C-g>', '<cmd>LazyGit<CR>', { desc = 'LazyGit' })
-- Telescope grep all files in working dir
map('n', '<C-f>', '<cmd>builtin.grep_string<CR>', { desc = '[F]ind (Telescope ripgrep all files in work_dir)' })

View File

@@ -1,8 +0,0 @@
-- Add custom comments package
-- https://github.com/numToStr/Comment.nvim
return {
'numToStr/Comment.nvim',
opts = {
-- add any options here
}
}

View File

@@ -1,31 +0,0 @@
-- Open fff.nvim
-- https://github.com/dmtrKovalenko/fff.nvim
return {
'dmtrKovalenko/fff.nvim',
build = function()
-- this will download prebuild binary or try to use existing rustup toolchain to build from source
-- (if you are using lazy you can use gb for rebuilding a plugin if needed)
require('fff.download').download_or_build_binary()
end,
-- if you are using nixos
-- build = "nix run .#release",
opts = { -- (optional)
debug = {
enabled = false, -- we expect your collaboration at least during the beta
show_scores = true, -- to help us optimize the scoring system, feel free to share your scores!
},
},
-- No need to lazy-load with lazy.nvim.
-- This plugin initializes itself lazily.
lazy = false,
keys = {
{
'ff', -- try it if you didn't it is a banger keybinding for a picker
function()
require('fff').find_files()
end,
desc = 'FFFind files',
},
},
}

View File

@@ -1,18 +0,0 @@
-- You can add your own plugins here or in other files in this directory!
-- I promise not to create any merge conflicts in this directory :)
--
-- See the kickstart.nvim README for more information
return {
-- {
-- 'navarasu/onedark.nvim',
-- priority = 1000, -- make sure to load this before all the other start plugins
-- config = function()
-- require('onedark').setup {
-- style = 'warmer',
-- }
-- require('onedark').load()
-- end,
-- },
-- { 'catppuccin/nvim', name = 'catppuccin', priority = 1000 },
--
}

View File

@@ -1,52 +0,0 @@
--[[
--
-- This file is not required for your own configuration,
-- but helps people determine if their system is setup correctly.
--
--]]
local check_version = function()
local verstr = tostring(vim.version())
if not vim.version.ge then
vim.health.error(string.format("Neovim out of date: '%s'. Upgrade to latest stable or nightly", verstr))
return
end
if vim.version.ge(vim.version(), '0.10-dev') then
vim.health.ok(string.format("Neovim version is: '%s'", verstr))
else
vim.health.error(string.format("Neovim out of date: '%s'. Upgrade to latest stable or nightly", verstr))
end
end
local check_external_reqs = function()
-- Basic utils: `git`, `make`, `unzip`
for _, exe in ipairs { 'git', 'make', 'unzip', 'rg' } do
local is_executable = vim.fn.executable(exe) == 1
if is_executable then
vim.health.ok(string.format("Found executable: '%s'", exe))
else
vim.health.warn(string.format("Could not find executable: '%s'", exe))
end
end
return true
end
return {
check = function()
vim.health.start 'kickstart.nvim'
vim.health.info [[NOTE: Not every warning is a 'must-fix' in `:checkhealth`
Fix only warnings for plugins and languages you intend to use.
Mason will give warnings for languages that are not installed.
You do not need to install, unless you want to use those languages!]]
local uv = vim.uv or vim.loop
vim.health.info('System Information: ' .. vim.inspect(uv.os_uname()))
check_version()
check_external_reqs()
end,
}

View File

@@ -1,8 +0,0 @@
-- autopairs
-- https://github.com/windwp/nvim-autopairs
return {
'windwp/nvim-autopairs',
event = 'InsertEnter',
opts = {},
}

View File

@@ -1,218 +0,0 @@
-- debug.lua
--
-- Shows how to use the DAP plugin to debug your code.
--
-- Primarily focused on configuring the debugger for Go, but can
-- be extended to other languages as well. That's why it's called
-- kickstart.nvim and not kitchen-sink.nvim ;)
return {
-- NOTE: Yes, you can install new plugins here!
'mfussenegger/nvim-dap',
-- NOTE: And you can specify dependencies as well
dependencies = {
-- Creates a beautiful debugger UI
'rcarriga/nvim-dap-ui',
-- Required dependency for nvim-dap-ui
'nvim-neotest/nvim-nio',
-- Installs the debug adapters for you
'mason-org/mason.nvim',
'jay-babu/mason-nvim-dap.nvim',
-- Add your own debuggers here
'leoluz/nvim-dap-go',
-- Fuzzy find variables with Telescope
'nvim-telescope/telescope-dap.nvim',
},
keys = {
-- Basic debugging keymaps, feel free to change to your liking!
{
'<F5>',
function()
require('dap').continue()
end,
desc = 'Debug: Start/Continue',
},
{
'<F1>',
function()
require('dap').step_into()
end,
desc = 'Debug: Step Into',
},
{
'<F2>',
function()
require('dap').step_over()
end,
desc = 'Debug: Step Over',
},
{
'<F3>',
function()
require('dap').step_out()
end,
desc = 'Debug: Step Out',
},
{
'<leader>b',
function()
require('dap').toggle_breakpoint()
end,
desc = 'Debug: Toggle Breakpoint',
},
{
'<leader>B',
function()
require('dap').set_breakpoint(vim.fn.input 'Breakpoint condition: ')
end,
desc = 'Debug: Set Breakpoint',
},
-- Toggle to see last session result. Without this, you can't see session output in case of unhandled exception.
{
'<F7>',
function()
require('dapui').toggle()
end,
desc = 'Debug: See last session result.',
},
-- Telescope DAP integration for fuzzy finding
{
'<leader>dv',
function()
require('telescope').extensions.dap.variables()
end,
desc = 'Debug: Find [V]ariables',
},
{
'<leader>df',
function()
require('telescope').extensions.dap.frames()
end,
desc = 'Debug: Find [F]rames',
},
{
'<leader>dc',
function()
require('telescope').extensions.dap.commands()
end,
desc = 'Debug: Find [C]ommands',
},
{
'<leader>db',
function()
require('telescope').extensions.dap.list_breakpoints()
end,
desc = 'Debug: List [B]reakpoints',
},
},
config = function()
local dap = require 'dap'
local dapui = require 'dapui'
require('mason-nvim-dap').setup {
-- Makes a best effort to setup the various debuggers with
-- reasonable debug configurations
automatic_installation = true,
-- You can provide additional configuration to the handlers,
-- see mason-nvim-dap README for more information
handlers = {},
-- You'll need to check that you have the required things installed
-- online, please don't ask me how to install them :)
ensure_installed = {
-- Update this to ensure that you have the debuggers for the langs you want
'delve',
'php-debug-adapter',
},
}
-- Dap UI setup
-- For more information, see |:help nvim-dap-ui|
dapui.setup {
-- Set icons to characters that are more likely to work in every terminal.
-- Feel free to remove or use ones that you like more! :)
-- Don't feel like these are good choices.
icons = { expanded = '', collapsed = '', current_frame = '*' },
controls = {
icons = {
pause = '',
play = '',
step_into = '',
step_over = '',
step_out = '',
step_back = 'b',
run_last = '▶▶',
terminate = '',
disconnect = '',
},
},
}
-- Change breakpoint icons
-- vim.api.nvim_set_hl(0, 'DapBreak', { fg = '#e51400' })
-- vim.api.nvim_set_hl(0, 'DapStop', { fg = '#ffcc00' })
-- local breakpoint_icons = vim.g.have_nerd_font
-- and { Breakpoint = '', BreakpointCondition = '', BreakpointRejected = '', LogPoint = '', Stopped = '' }
-- or { Breakpoint = '●', BreakpointCondition = '⊜', BreakpointRejected = '⊘', LogPoint = '◆', Stopped = '⭔' }
-- for type, icon in pairs(breakpoint_icons) do
-- local tp = 'Dap' .. type
-- local hl = (type == 'Stopped') and 'DapStop' or 'DapBreak'
-- vim.fn.sign_define(tp, { text = icon, texthl = hl, numhl = hl })
-- end
dap.listeners.after.event_initialized['dapui_config'] = dapui.open
dap.listeners.before.event_terminated['dapui_config'] = dapui.close
dap.listeners.before.event_exited['dapui_config'] = dapui.close
-- Install golang specific config
require('dap-go').setup {
delve = {
-- On Windows delve must be run attached or it crashes.
-- See https://github.com/leoluz/nvim-dap-go/blob/main/README.md#configuring
detached = vim.fn.has 'win32' == 0,
},
}
-- PHP/Xdebug configuration
dap.adapters.php = {
type = 'executable',
command = 'node',
args = { vim.fn.stdpath('data') .. '/mason/packages/php-debug-adapter/extension/out/phpDebug.js' },
}
dap.configurations.php = {
{
type = 'php',
request = 'launch',
name = 'Listen for Xdebug',
port = 9003,
},
{
type = 'php',
request = 'launch',
name = 'Listen for Xdebug (DDEV)',
port = 9003,
pathMappings = {
['/var/www/html'] = '${workspaceFolder}',
},
},
{
type = 'php',
request = 'launch',
name = 'Run current script',
port = 9003,
cwd = '${fileDirname}',
program = '${file}',
runtimeExecutable = 'php',
},
}
-- Load telescope-dap extension
require('telescope').load_extension('dap')
end,
}

View File

@@ -1,61 +0,0 @@
-- Adds git related signs to the gutter, as well as utilities for managing changes
-- NOTE: gitsigns is already included in init.lua but contains only the base
-- config. This will add also the recommended keymaps.
return {
{
'lewis6991/gitsigns.nvim',
opts = {
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 = 'Jump to next git [c]hange' })
map('n', '[c', function()
if vim.wo.diff then
vim.cmd.normal { '[c', bang = true }
else
gitsigns.nav_hunk 'prev'
end
end, { desc = 'Jump to previous git [c]hange' })
-- Actions
-- visual mode
map('v', '<leader>hs', function()
gitsigns.stage_hunk { vim.fn.line '.', vim.fn.line 'v' }
end, { desc = 'git [s]tage hunk' })
map('v', '<leader>hr', function()
gitsigns.reset_hunk { vim.fn.line '.', vim.fn.line 'v' }
end, { desc = 'git [r]eset hunk' })
-- normal mode
map('n', '<leader>hs', gitsigns.stage_hunk, { desc = 'git [s]tage hunk' })
map('n', '<leader>hr', gitsigns.reset_hunk, { desc = 'git [r]eset hunk' })
map('n', '<leader>hS', gitsigns.stage_buffer, { desc = 'git [S]tage buffer' })
map('n', '<leader>hu', gitsigns.stage_hunk, { desc = 'git [u]ndo stage hunk' })
map('n', '<leader>hR', gitsigns.reset_buffer, { desc = 'git [R]eset buffer' })
map('n', '<leader>hp', gitsigns.preview_hunk, { desc = 'git [p]review hunk' })
map('n', '<leader>hb', gitsigns.blame_line, { desc = 'git [b]lame line' })
map('n', '<leader>hd', gitsigns.diffthis, { desc = 'git [d]iff against index' })
map('n', '<leader>hD', function()
gitsigns.diffthis '@'
end, { desc = 'git [D]iff against last commit' })
-- Toggles
map('n', '<leader>tb', gitsigns.toggle_current_line_blame, { desc = '[T]oggle git show [b]lame line' })
map('n', '<leader>tD', gitsigns.preview_hunk_inline, { desc = '[T]oggle git show [D]eleted' })
end,
},
},
}

View File

@@ -1,9 +0,0 @@
return {
{ -- Add indentation guides even on blank lines
'lukas-reineke/indent-blankline.nvim',
-- Enable `lukas-reineke/indent-blankline.nvim`
-- See `:help ibl`
main = 'ibl',
opts = {},
},
}

View File

@@ -1,60 +0,0 @@
return {
{ -- Linting
'mfussenegger/nvim-lint',
event = { 'BufReadPre', 'BufNewFile' },
config = function()
local lint = require 'lint'
lint.linters_by_ft = {
markdown = { 'markdownlint' },
}
-- To allow other plugins to add linters to require('lint').linters_by_ft,
-- instead set linters_by_ft like this:
-- lint.linters_by_ft = lint.linters_by_ft or {}
-- lint.linters_by_ft['markdown'] = { 'markdownlint' }
--
-- However, note that this will enable a set of default linters,
-- which will cause errors unless these tools are available:
-- {
-- clojure = { "clj-kondo" },
-- dockerfile = { "hadolint" },
-- inko = { "inko" },
-- janet = { "janet" },
-- json = { "jsonlint" },
-- markdown = { "vale" },
-- rst = { "vale" },
-- ruby = { "ruby" },
-- terraform = { "tflint" },
-- text = { "vale" }
-- }
--
-- You can disable the default linters by setting their filetypes to nil:
-- lint.linters_by_ft['clojure'] = nil
-- lint.linters_by_ft['dockerfile'] = nil
-- lint.linters_by_ft['inko'] = nil
-- lint.linters_by_ft['janet'] = nil
-- lint.linters_by_ft['json'] = nil
-- lint.linters_by_ft['markdown'] = nil
-- lint.linters_by_ft['rst'] = nil
-- lint.linters_by_ft['ruby'] = nil
-- lint.linters_by_ft['terraform'] = nil
-- lint.linters_by_ft['text'] = nil
-- Create autocommand which carries out the actual linting
-- on the specified events.
local lint_augroup = vim.api.nvim_create_augroup('lint', { clear = true })
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, {
group = lint_augroup,
callback = function()
-- Only run the linter in buffers that you can modify in order to
-- avoid superfluous noise, notably within the handy LSP pop-ups that
-- describe the hovered symbol using Markdown.
if vim.bo.modifiable then
lint.try_lint()
end
end,
})
end,
},
}

368
lua/plugins/colorscheme.lua Normal file
View File

@@ -0,0 +1,368 @@
-- lua/plugins/colorscheme.lua
-- Custom "orng" colorscheme — dark and light variants.
-- Palette source: https://gist.github.com/axeldotdev/9b441e22147d2d838624d82357ac4302
--
-- Usage:
-- :colorscheme orng → dark (bg #0a0a0a)
-- :colorscheme orng-light → light (bg #ffffff)
-- ── Shared accent colors ───────────────────────────────────────────────────
local accent = {
orange = '#EC5B2B',
orange_br = '#EE7948',
cyan = '#56b6c2',
red = '#e06c75',
}
-- ── Dark palette ───────────────────────────────────────────────────────────
local dark = {
bg = '#0a0a0a',
fg = '#eeeeee',
orange = accent.orange,
orange_br = accent.orange_br,
yellow = '#e5c07b',
yellow_br = '#FFF7F1',
blue = '#6ba1e6',
cyan = accent.cyan,
red = accent.red,
gray1 = '#1a1a1a', -- panel / float bg
gray2 = '#2a2a2a', -- prompt bg
gray3 = '#3a3a3a', -- borders / separators
gray4 = '#606060', -- comments / inactive
gray5 = '#888888', -- delimiters
sel_bg = accent.orange,
sel_fg = '#0a0a0a',
diff_add = '#0d1f2d',
diff_chg = '#1f1a0d',
diff_del = '#1f0d0d',
diff_txt = '#2a1a0a',
vt_err = '#1f0d0d',
vt_warn = '#1f1a0d',
vt_info = '#0d1020',
vt_hint = '#0d1a1a',
}
-- ── Light palette ──────────────────────────────────────────────────────────
local light = {
bg = '#ffffff',
fg = '#1a1a1a',
orange = '#D44A1A', -- slightly darker than accent for readability on white
orange_br = accent.orange,
yellow = '#b07800',
yellow_br = '#8a5c00',
blue = '#2d6fc4',
cyan = '#1a7a87',
red = '#c0334a',
gray1 = '#f5f0eb', -- panel / float bg (warm off-white)
gray2 = '#ede8e2', -- prompt bg
gray3 = '#d4cfc8', -- borders / separators
gray4 = '#999999', -- comments / inactive
gray5 = '#777777', -- delimiters
sel_bg = accent.orange,
sel_fg = '#ffffff',
diff_add = '#ddeeff',
diff_chg = '#fff3cc',
diff_del = '#ffd6d6',
diff_txt = '#ffe0b2',
vt_err = '#fde8e8',
vt_warn = '#fef9e0',
vt_info = '#e8f0fd',
vt_hint = '#e0f5f5',
}
-- ── Highlight helper ───────────────────────────────────────────────────────
local function hi(group, opts)
vim.api.nvim_set_hl(0, group, opts)
end
-- ── Apply function — called with either `dark` or `light` palette ──────────
local function apply(c, variant)
local bg_mode = variant == 'light' and 'light' or 'dark'
vim.o.background = bg_mode
vim.cmd.highlight 'clear'
if vim.fn.exists 'syntax_on' == 1 then vim.cmd.syntax 'reset' end
vim.g.colors_name = 'orng' .. (variant == 'light' and '-light' or '')
-- Editor chrome
hi('Normal', { fg = c.fg, bg = c.bg })
hi('NormalFloat', { fg = c.fg, bg = c.gray1 })
hi('NormalNC', { fg = c.fg, bg = c.bg })
hi('FloatBorder', { fg = c.orange, bg = c.gray1 })
hi('FloatTitle', { fg = c.orange_br, bg = c.gray1, bold = true })
hi('ColorColumn', { bg = c.gray1 })
hi('CursorLine', { bg = c.gray1 })
hi('CursorLineNr', { fg = c.orange, bold = true })
hi('LineNr', { fg = c.gray4 })
hi('SignColumn', { fg = c.gray4, bg = c.bg })
hi('Folded', { fg = c.gray4, bg = c.gray1 })
hi('FoldColumn', { fg = c.gray4, bg = c.bg })
hi('VertSplit', { fg = c.gray3 })
hi('WinSeparator', { fg = c.gray3 })
hi('EndOfBuffer', { fg = c.gray3 })
-- Cursor & selection
hi('Cursor', { fg = c.bg, bg = c.fg })
hi('CursorIM', { fg = c.bg, bg = c.fg })
hi('Visual', { fg = c.sel_fg, bg = c.sel_bg })
hi('VisualNOS', { fg = c.sel_fg, bg = c.sel_bg })
-- Status / tab line
hi('StatusLine', { fg = c.fg, bg = c.gray2 })
hi('StatusLineNC', { fg = c.gray4, bg = c.gray1 })
hi('TabLine', { fg = c.gray4, bg = c.gray1 })
hi('TabLineFill', { bg = c.gray1 })
hi('TabLineSel', { fg = c.fg, bg = c.gray2, bold = true })
-- Pmenu
hi('Pmenu', { fg = c.fg, bg = c.gray1 })
hi('PmenuSel', { fg = c.sel_fg, bg = c.orange })
hi('PmenuSbar', { bg = c.gray2 })
hi('PmenuThumb', { bg = c.orange })
hi('PmenuBorder', { fg = c.orange })
-- Search
hi('Search', { fg = c.bg, bg = c.yellow })
hi('IncSearch', { fg = c.bg, bg = c.orange })
hi('CurSearch', { fg = c.bg, bg = c.orange_br })
hi('Substitute', { fg = c.bg, bg = c.orange })
-- Messages
hi('ErrorMsg', { fg = c.red, bold = true })
hi('WarningMsg', { fg = c.yellow })
hi('ModeMsg', { fg = c.orange, bold = true })
hi('MoreMsg', { fg = c.orange })
hi('Question', { fg = c.blue })
-- Diff
hi('DiffAdd', { fg = c.blue, bg = c.diff_add })
hi('DiffChange', { fg = c.yellow, bg = c.diff_chg })
hi('DiffDelete', { fg = c.red, bg = c.diff_del })
hi('DiffText', { fg = c.orange_br, bg = c.diff_txt, bold = true })
hi('Added', { fg = c.blue })
hi('Changed', { fg = c.yellow })
hi('Removed', { fg = c.red })
-- Spelling
hi('SpellBad', { undercurl = true, sp = c.red })
hi('SpellCap', { undercurl = true, sp = c.yellow })
hi('SpellLocal', { undercurl = true, sp = c.blue })
hi('SpellRare', { undercurl = true, sp = c.cyan })
-- Syntax
hi('Comment', { fg = c.gray4, italic = true })
hi('Constant', { fg = c.orange_br })
hi('String', { fg = c.yellow_br })
hi('Character', { fg = c.yellow_br })
hi('Number', { fg = c.orange_br })
hi('Boolean', { fg = c.orange, bold = true })
hi('Float', { fg = c.orange_br })
hi('Identifier', { fg = c.fg })
hi('Function', { fg = c.orange, bold = true })
hi('Statement', { fg = c.orange })
hi('Conditional', { fg = c.orange })
hi('Repeat', { fg = c.orange })
hi('Label', { fg = c.orange })
hi('Operator', { fg = c.yellow })
hi('Keyword', { fg = c.orange, bold = true })
hi('Exception', { fg = c.red })
hi('PreProc', { fg = c.blue })
hi('Include', { fg = c.blue })
hi('Define', { fg = c.blue })
hi('Macro', { fg = c.blue })
hi('PreCondit', { fg = c.blue })
hi('Type', { fg = c.yellow })
hi('StorageClass', { fg = c.yellow })
hi('Structure', { fg = c.yellow })
hi('Typedef', { fg = c.yellow })
hi('Special', { fg = c.cyan })
hi('SpecialChar', { fg = c.cyan })
hi('Tag', { fg = c.orange })
hi('Delimiter', { fg = c.gray5 })
hi('SpecialComment', { fg = c.gray4, italic = true })
hi('Debug', { fg = c.red })
hi('Underlined', { underline = true })
hi('Ignore', { fg = c.gray4 })
hi('Error', { fg = c.red, bold = true })
hi('Todo', { fg = c.bg, bg = c.orange, bold = true })
-- Treesitter
hi('@variable', { fg = c.fg })
hi('@variable.builtin', { fg = c.orange_br })
hi('@variable.parameter', { fg = c.fg })
hi('@variable.member', { fg = c.fg })
hi('@constant', { fg = c.orange_br })
hi('@constant.builtin', { fg = c.orange, bold = true })
hi('@constant.macro', { fg = c.blue })
hi('@string', { fg = c.yellow_br })
hi('@string.escape', { fg = c.cyan })
hi('@string.special', { fg = c.cyan })
hi('@character', { fg = c.yellow_br })
hi('@number', { fg = c.orange_br })
hi('@boolean', { fg = c.orange, bold = true })
hi('@float', { fg = c.orange_br })
hi('@function', { fg = c.orange, bold = true })
hi('@function.builtin', { fg = c.orange_br })
hi('@function.call', { fg = c.orange })
hi('@function.macro', { fg = c.blue })
hi('@function.method', { fg = c.orange })
hi('@function.method.call', { fg = c.orange })
hi('@constructor', { fg = c.yellow })
hi('@operator', { fg = c.yellow })
hi('@keyword', { fg = c.orange, bold = true })
hi('@keyword.function', { fg = c.orange, bold = true })
hi('@keyword.operator', { fg = c.yellow })
hi('@keyword.return', { fg = c.orange, bold = true })
hi('@keyword.import', { fg = c.blue })
hi('@keyword.conditional', { fg = c.orange })
hi('@keyword.repeat', { fg = c.orange })
hi('@keyword.exception', { fg = c.red })
hi('@type', { fg = c.yellow })
hi('@type.builtin', { fg = c.yellow, bold = true })
hi('@type.definition', { fg = c.yellow })
hi('@attribute', { fg = c.blue })
hi('@property', { fg = c.fg })
hi('@punctuation.delimiter', { fg = c.gray5 })
hi('@punctuation.bracket', { fg = c.gray5 })
hi('@punctuation.special', { fg = c.cyan })
hi('@comment', { fg = c.gray4, italic = true })
hi('@comment.documentation', { fg = c.gray5, italic = true })
hi('@tag', { fg = c.orange })
hi('@tag.attribute', { fg = c.yellow })
hi('@tag.delimiter', { fg = c.gray5 })
hi('@markup.heading', { fg = c.orange, bold = true })
hi('@markup.bold', { fg = c.yellow_br, bold = true })
hi('@markup.italic', { fg = c.fg, italic = true })
hi('@markup.link', { fg = c.blue, underline = true })
hi('@markup.link.url', { fg = c.blue, underline = true })
hi('@markup.raw', { fg = c.cyan })
hi('@markup.list', { fg = c.orange })
-- LSP
hi('LspReferenceText', { bg = c.gray2 })
hi('LspReferenceRead', { bg = c.gray2 })
hi('LspReferenceWrite', { bg = c.gray3 })
hi('LspInlayHint', { fg = c.gray4, italic = true })
hi('LspCodeLens', { fg = c.gray4, italic = true })
-- Diagnostics
hi('DiagnosticError', { fg = c.red })
hi('DiagnosticWarn', { fg = c.yellow })
hi('DiagnosticInfo', { fg = c.blue })
hi('DiagnosticHint', { fg = c.cyan })
hi('DiagnosticOk', { fg = c.blue })
hi('DiagnosticVirtualTextError', { fg = c.red, bg = c.vt_err, italic = true })
hi('DiagnosticVirtualTextWarn', { fg = c.yellow, bg = c.vt_warn, italic = true })
hi('DiagnosticVirtualTextInfo', { fg = c.blue, bg = c.vt_info, italic = true })
hi('DiagnosticVirtualTextHint', { fg = c.cyan, bg = c.vt_hint, italic = true })
hi('DiagnosticUnderlineError', { undercurl = true, sp = c.red })
hi('DiagnosticUnderlineWarn', { undercurl = true, sp = c.yellow })
hi('DiagnosticUnderlineInfo', { undercurl = true, sp = c.blue })
hi('DiagnosticUnderlineHint', { undercurl = true, sp = c.cyan })
-- Gitsigns
hi('GitSignsAdd', { fg = c.blue })
hi('GitSignsChange', { fg = c.yellow })
hi('GitSignsDelete', { fg = c.red })
-- Telescope
hi('TelescopeBorder', { fg = c.orange, bg = c.gray1 })
hi('TelescopeNormal', { fg = c.fg, bg = c.gray1 })
hi('TelescopePromptBorder', { fg = c.orange_br, bg = c.gray2 })
hi('TelescopePromptNormal', { fg = c.fg, bg = c.gray2 })
hi('TelescopePromptPrefix', { fg = c.orange, bg = c.gray2 })
hi('TelescopeResultsBorder', { fg = c.orange, bg = c.gray1 })
hi('TelescopePreviewBorder', { fg = c.gray3, bg = c.gray1 })
hi('TelescopeSelection', { fg = c.sel_fg, bg = c.orange })
hi('TelescopeSelectionCaret', { fg = c.sel_fg, bg = c.orange })
hi('TelescopeMatching', { fg = c.orange_br, bold = true })
hi('TelescopeTitle', { fg = c.orange_br, bold = true })
-- NeoTree
hi('NeoTreeNormal', { fg = c.fg, bg = c.gray1 })
hi('NeoTreeNormalNC', { fg = c.fg, bg = c.gray1 })
hi('NeoTreeVertSplit', { fg = c.gray3, bg = c.gray1 })
hi('NeoTreeWinSeparator', { fg = c.gray3, bg = c.gray1 })
hi('NeoTreeDirectoryName',{ fg = c.orange })
hi('NeoTreeDirectoryIcon',{ fg = c.orange })
hi('NeoTreeFileName', { fg = c.fg })
hi('NeoTreeGitAdded', { fg = c.blue })
hi('NeoTreeGitModified', { fg = c.yellow })
hi('NeoTreeGitDeleted', { fg = c.red })
hi('NeoTreeIndentMarker', { fg = c.gray3 })
hi('NeoTreeExpander', { fg = c.orange })
hi('NeoTreeTitleBar', { fg = c.bg, bg = c.orange, bold = true })
-- Which-key
hi('WhichKey', { fg = c.orange })
hi('WhichKeyGroup', { fg = c.yellow })
hi('WhichKeyDesc', { fg = c.fg })
hi('WhichKeySeparator', { fg = c.gray4 })
hi('WhichKeyFloat', { bg = c.gray1 })
hi('WhichKeyBorder', { fg = c.orange, bg = c.gray1 })
-- Blink.cmp
hi('BlinkCmpMenu', { fg = c.fg, bg = c.gray1 })
hi('BlinkCmpMenuBorder', { fg = c.orange, bg = c.gray1 })
hi('BlinkCmpMenuSelection', { fg = c.sel_fg, bg = c.orange })
hi('BlinkCmpScrollBarThumb', { bg = c.orange })
hi('BlinkCmpScrollBarGutter', { bg = c.gray2 })
hi('BlinkCmpLabel', { fg = c.fg })
hi('BlinkCmpLabelMatch', { fg = c.orange_br, bold = true })
hi('BlinkCmpKind', { fg = c.yellow })
hi('BlinkCmpDoc', { fg = c.fg, bg = c.gray1 })
hi('BlinkCmpDocBorder', { fg = c.gray3, bg = c.gray1 })
-- Mini.statusline
hi('MiniStatuslineModeNormal', { fg = c.bg, bg = c.orange, bold = true })
hi('MiniStatuslineModeInsert', { fg = c.bg, bg = c.blue, bold = true })
hi('MiniStatuslineModeVisual', { fg = c.bg, bg = c.yellow, bold = true })
hi('MiniStatuslineModeReplace', { fg = c.bg, bg = c.red, bold = true })
hi('MiniStatuslineModeCommand', { fg = c.bg, bg = c.orange_br, bold = true })
hi('MiniStatuslineModeOther', { fg = c.bg, bg = c.gray4, bold = true })
hi('MiniStatuslineDevinfo', { fg = c.fg, bg = c.gray2 })
hi('MiniStatuslineFilename', { fg = c.fg, bg = c.gray2 })
hi('MiniStatuslineFileinfo', { fg = c.fg, bg = c.gray2 })
hi('MiniStatuslineInactive', { fg = c.gray4, bg = c.gray1 })
-- Todo-comments
hi('TodoBgTODO', { fg = c.bg, bg = c.orange, bold = true })
hi('TodoBgNOTE', { fg = c.bg, bg = c.blue, bold = true })
hi('TodoBgFIX', { fg = c.bg, bg = c.red, bold = true })
hi('TodoBgWARN', { fg = c.bg, bg = c.yellow, bold = true })
hi('TodoBgHACK', { fg = c.bg, bg = c.yellow, bold = true })
hi('TodoBgPERF', { fg = c.bg, bg = c.cyan, bold = true })
hi('TodoFgTODO', { fg = c.orange })
hi('TodoFgNOTE', { fg = c.blue })
hi('TodoFgFIX', { fg = c.red })
hi('TodoFgWARN', { fg = c.yellow })
hi('TodoFgHACK', { fg = c.yellow })
hi('TodoFgPERF', { fg = c.cyan })
end
-- ── Helper: pick palette from macOS system appearance ─────────────────────
local function is_dark_mode()
local out = vim.fn.system 'defaults read -g AppleInterfaceStyle 2>/dev/null'
return out:match 'Dark' ~= nil
end
-- ── Apply on startup based on system appearance ────────────────────────────
if is_dark_mode() then
apply(dark, 'dark')
else
apply(light, 'light')
end
-- ── Re-apply on explicit :colorscheme calls ────────────────────────────────
-- These autocmds let :colorscheme orng / orng-light work mid-session.
-- Note: they fire only if a colors/orng*.vim stub exists; we create one below.
vim.api.nvim_create_autocmd('ColorScheme', {
pattern = 'orng',
callback = function() apply(dark, 'dark') end,
})
vim.api.nvim_create_autocmd('ColorScheme', {
pattern = 'orng-light',
callback = function() apply(light, 'light') end,
})
return {}

View File

@@ -0,0 +1,55 @@
-- lua/plugins/completion.lua
-- Autocompletion via blink.cmp + LuaSnip snippets
return {
'saghen/blink.cmp',
event = 'VimEnter',
version = '1.*',
dependencies = {
{
'L3MON4D3/LuaSnip',
version = '2.*',
build = (function()
-- Regex support in snippets requires a build step.
-- Skip on Windows or when make is unavailable.
if vim.fn.has 'win32' == 1 or vim.fn.executable 'make' == 0 then
return
end
return 'make install_jsregexp'
end)(),
},
'folke/lazydev.nvim',
},
--- @module 'blink.cmp'
--- @type blink.cmp.Config
opts = {
keymap = {
-- super-tab: Tab to accept, C-n/C-p to navigate
preset = 'super-tab',
},
appearance = {
nerd_font_variant = 'mono',
},
completion = {
documentation = { auto_show = true, auto_show_delay_ms = 300 },
menu = {
border = 'rounded',
},
},
sources = {
default = { 'lsp', 'path', 'snippets', 'lazydev' },
providers = {
lazydev = { module = 'lazydev.integrations.blink', score_offset = 100 },
},
},
snippets = { preset = 'luasnip' },
fuzzy = { implementation = 'lua' },
signature = { enabled = true, window = { border = 'rounded' } },
},
}

50
lua/plugins/conform.lua Normal file
View File

@@ -0,0 +1,50 @@
-- lua/plugins/conform.lua
-- Code formatting via conform.nvim
return {
'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)
-- Disable lsp_fallback for languages without a standardized style.
local disable_filetypes = { c = true, cpp = true }
if disable_filetypes[vim.bo[bufnr].filetype] then
return nil
end
return { timeout_ms = 500, lsp_format = 'fallback' }
end,
formatters = {
yamlfmt = {
prepend_args = { '-conf', vim.fn.expand '~/.config/yamlfmt/.yamlfmt' },
},
},
formatters_by_ft = {
lua = { 'stylua' },
python = { 'black' },
javascript = { 'biome', 'prettier', stop_after_first = true },
typescript = { 'biome', 'prettier', stop_after_first = true },
javascriptreact = { 'biome', 'prettier', stop_after_first = true },
typescriptreact = { 'biome', 'prettier', stop_after_first = true },
json = { 'biome', 'prettier', stop_after_first = true },
html = { 'prettier' },
css = { 'prettier' },
graphql = { 'prettier' },
yaml = { 'yamlfmt' },
},
},
}

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

@@ -0,0 +1,75 @@
-- lua/plugins/debug.lua
-- DAP debugging: Go + PHP/Xdebug support
return {
'mfussenegger/nvim-dap',
dependencies = {
'rcarriga/nvim-dap-ui',
'nvim-neotest/nvim-nio',
'mason-org/mason.nvim',
'jay-babu/mason-nvim-dap.nvim',
'leoluz/nvim-dap-go',
'nvim-telescope/telescope-dap.nvim',
},
keys = {
{ '<F5>', function() require('dap').continue() end, desc = 'Debug: Start/Continue' },
{ '<F1>', function() require('dap').step_into() end, desc = 'Debug: Step Into' },
{ '<F2>', function() require('dap').step_over() end, desc = 'Debug: Step Over' },
{ '<F3>', function() require('dap').step_out() end, desc = 'Debug: Step Out' },
{ '<F7>', function() require('dapui').toggle() end, desc = 'Debug: Toggle UI' },
{ '<leader>b', function() require('dap').toggle_breakpoint() end, desc = 'Debug: Toggle Breakpoint' },
{ '<leader>B', function() require('dap').set_breakpoint(vim.fn.input 'Condition: ') end, desc = 'Debug: Set Conditional Breakpoint' },
{ '<leader>dv', function() require('telescope').extensions.dap.variables() end, desc = 'Debug: Find [V]ariables' },
{ '<leader>df', function() require('telescope').extensions.dap.frames() end, desc = 'Debug: Find [F]rames' },
{ '<leader>dc', function() require('telescope').extensions.dap.commands() end, desc = 'Debug: Find [C]ommands' },
{ '<leader>db', function() require('telescope').extensions.dap.list_breakpoints() end, desc = 'Debug: List [B]reakpoints' },
},
config = function()
local dap = require 'dap'
local dapui = require 'dapui'
require('mason-nvim-dap').setup {
automatic_installation = true,
handlers = {},
ensure_installed = { 'delve', 'php-debug-adapter' },
}
dapui.setup {
icons = { expanded = '', collapsed = '', current_frame = '*' },
controls = {
icons = {
pause = '', play = '', step_into = '', step_over = '',
step_out = '', step_back = 'b', run_last = '▶▶',
terminate = '', disconnect = '',
},
},
}
-- Auto-open/close dapui with debug sessions
dap.listeners.after.event_initialized['dapui_config'] = dapui.open
dap.listeners.before.event_terminated['dapui_config'] = dapui.close
dap.listeners.before.event_exited['dapui_config'] = dapui.close
-- Go
require('dap-go').setup {
delve = { detached = vim.fn.has 'win32' == 0 },
}
-- PHP / Xdebug
dap.adapters.php = {
type = 'executable',
command = 'node',
args = { vim.fn.stdpath 'data' .. '/mason/packages/php-debug-adapter/extension/out/phpDebug.js' },
}
dap.configurations.php = {
{ type = 'php', request = 'launch', name = 'Listen for Xdebug', port = 9003 },
{ type = 'php', request = 'launch', name = 'Listen for Xdebug (DDEV)', port = 9003,
pathMappings = { ['/var/www/html'] = '${workspaceFolder}' } },
{ type = 'php', request = 'launch', name = 'Run current script', port = 9003,
cwd = '${fileDirname}', program = '${file}', runtimeExecutable = 'php' },
}
-- Telescope DAP extension
require('telescope').load_extension 'dap'
end,
}

View File

@@ -0,0 +1,7 @@
-- lua/plugins/extras/comment.lua
-- Smart commenting via gcc / gc<motion>
return {
'numToStr/Comment.nvim',
opts = {},
}

View File

@@ -0,0 +1,17 @@
-- lua/plugins/extras/fff.lua
-- Fast fuzzy file finder with a native Rust binary
-- https://github.com/dmtrKovalenko/fff.nvim
return {
'dmtrKovalenko/fff.nvim',
lazy = false,
build = function()
require('fff.download').download_or_build_binary()
end,
opts = {
debug = { enabled = false, show_scores = false },
},
keys = {
{ 'ff', function() require('fff').find_files() end, desc = 'FFFind files' },
},
}

View File

@@ -1,4 +1,5 @@
-- Open lazygit
-- lua/plugins/extras/lazygit.lua
-- LazyGit floating window integration
-- https://github.com/kdheepak/lazygit.nvim
return {
@@ -11,13 +12,9 @@ return {
'LazyGitFilter',
'LazyGitFilterCurrentFile',
},
-- optional for floating window border decoration
dependencies = {
'nvim-lua/plenary.nvim',
},
-- setting the keybinding for LazyGit with 'keys' is recommended in
-- order to load the plugin when the command is run for the first time
dependencies = { 'nvim-lua/plenary.nvim' },
keys = {
{ '<leader>lg', '<cmd>LazyGit<cr>', desc = 'LazyGit' },
-- <C-g> is also mapped in config/keymaps.lua
},
}

51
lua/plugins/gitsigns.lua Normal file
View File

@@ -0,0 +1,51 @@
-- lua/plugins/gitsigns.lua
-- Git signs in the gutter + hunk actions and navigation keymaps
return {
'lewis6991/gitsigns.nvim',
opts = {
signs = {
add = { text = '+' },
change = { text = '~' },
delete = { text = '_' },
topdelete = { text = '' },
changedelete = { text = '~' },
},
on_attach = function(bufnr)
local gs = require 'gitsigns'
local function map(mode, lhs, rhs, opts)
opts = vim.tbl_extend('force', { buffer = bufnr }, opts or {})
vim.keymap.set(mode, lhs, rhs, opts)
end
-- Navigation
map('n', ']c', function()
if vim.wo.diff then vim.cmd.normal { ']c', bang = true }
else gs.nav_hunk 'next' end
end, { desc = 'Jump to next git [c]hange' })
map('n', '[c', function()
if vim.wo.diff then vim.cmd.normal { '[c', bang = true }
else gs.nav_hunk 'prev' end
end, { desc = 'Jump to previous git [c]hange' })
-- Hunk actions (visual + normal)
map('v', '<leader>hs', function() gs.stage_hunk { vim.fn.line '.', vim.fn.line 'v' } end, { desc = 'git [s]tage hunk' })
map('v', '<leader>hr', function() gs.reset_hunk { vim.fn.line '.', vim.fn.line 'v' } end, { desc = 'git [r]eset hunk' })
map('n', '<leader>hs', gs.stage_hunk, { desc = 'git [s]tage hunk' })
map('n', '<leader>hr', gs.reset_hunk, { desc = 'git [r]eset hunk' })
map('n', '<leader>hS', gs.stage_buffer, { desc = 'git [S]tage buffer' })
map('n', '<leader>hu', gs.undo_stage_hunk, { desc = 'git [u]ndo stage hunk' })
map('n', '<leader>hR', gs.reset_buffer, { desc = 'git [R]eset buffer' })
map('n', '<leader>hp', gs.preview_hunk, { desc = 'git [p]review hunk' })
map('n', '<leader>hb', gs.blame_line, { desc = 'git [b]lame line' })
map('n', '<leader>hd', gs.diffthis, { desc = 'git [d]iff against index' })
map('n', '<leader>hD', function() gs.diffthis '@' end, { desc = 'git [D]iff against last commit' })
-- Toggles
map('n', '<leader>tb', gs.toggle_current_line_blame, { desc = '[T]oggle git [b]lame line' })
map('n', '<leader>tD', gs.preview_hunk_inline, { desc = '[T]oggle git show [D]eleted' })
end,
},
}

View File

@@ -0,0 +1,7 @@
-- lua/plugins/guess-indent.lua
-- Automatically detect tabstop and shiftwidth from the file
return {
'NMAC427/guess-indent.nvim',
opts = {},
}

25
lua/plugins/lint.lua Normal file
View File

@@ -0,0 +1,25 @@
-- lua/plugins/lint.lua
-- Async linting via nvim-lint
return {
'mfussenegger/nvim-lint',
event = { 'BufReadPre', 'BufNewFile' },
config = function()
local lint = require 'lint'
lint.linters_by_ft = {
markdown = { 'markdownlint' },
}
local augroup = vim.api.nvim_create_augroup('user-lint', { clear = true })
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, {
group = augroup,
callback = function()
-- Only lint modifiable buffers to avoid noise in readonly pop-ups
if vim.bo.modifiable then
lint.try_lint()
end
end,
})
end,
}

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

@@ -0,0 +1,135 @@
-- lua/plugins/lsp.lua
-- Language Server Protocol: mason + nvim-lspconfig + lazydev + fidget
return {
{
-- Lua LSP annotations for Neovim API
'folke/lazydev.nvim',
ft = 'lua',
opts = {
library = {
{ path = '${3rd}/luv/library', words = { 'vim%.uv' } },
},
},
},
{
'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()
-- Keymaps attached per-buffer when an LSP connects
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('user-lsp-attach', { clear = true }),
callback = function(event)
local map = function(keys, func, desc, mode)
vim.keymap.set(mode or 'n', keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
end
local tb = require 'telescope.builtin'
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', tb.lsp_references, '[G]oto [R]eferences')
map('gri', tb.lsp_implementations, '[G]oto [I]mplementation')
map('grd', tb.lsp_definitions, '[G]oto [D]efinition')
map('grD', vim.lsp.buf.declaration, '[G]oto [D]eclaration')
map('gO', tb.lsp_document_symbols, 'Open Document Symbols')
map('gW', tb.lsp_dynamic_workspace_symbols, 'Open Workspace Symbols')
map('grt', tb.lsp_type_definitions, '[G]oto [T]ype Definition')
-- Compat helper for 0.10 vs 0.11
local function client_supports(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
local client = vim.lsp.get_client_by_id(event.data.client_id)
-- Document highlight on CursorHold
if client and client_supports(client, vim.lsp.protocol.Methods.textDocument_documentHighlight, event.buf) then
local hl_group = vim.api.nvim_create_augroup('user-lsp-highlight', { clear = false })
vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
buffer = event.buf, group = hl_group, callback = vim.lsp.buf.document_highlight,
})
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
buffer = event.buf, group = hl_group, callback = vim.lsp.buf.clear_references,
})
vim.api.nvim_create_autocmd('LspDetach', {
group = vim.api.nvim_create_augroup('user-lsp-detach', { clear = true }),
callback = function(ev)
vim.lsp.buf.clear_references()
vim.api.nvim_clear_autocmds { group = 'user-lsp-highlight', buffer = ev.buf }
end,
})
end
-- Inlay hints toggle
if client and client_supports(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,
})
-- Diagnostics appearance
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(d) return d.message end,
},
}
-- Capabilities enhanced by blink.cmp
local capabilities = require('blink.cmp').get_lsp_capabilities()
-- Language servers to install and configure
-- Add more servers here as needed; see `:help lspconfig-all`
local servers = {
lua_ls = {
settings = {
Lua = {
completion = { callSnippet = 'Replace' },
},
},
},
}
local ensure_installed = vim.tbl_keys(servers)
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,
},
}

32
lua/plugins/mini.lua Normal file
View File

@@ -0,0 +1,32 @@
-- lua/plugins/mini.lua
-- mini.nvim collection: ai, pairs, animate, starter, surround, statusline
return {
'echasnovski/mini.nvim',
config = function()
-- Better text objects: va) yi' ci{ etc.
require('mini.ai').setup { n_lines = 500 }
-- Auto-pairs for brackets, quotes, etc.
require('mini.pairs').setup()
-- Smooth scroll / cursor animations
require('mini.animate').setup()
-- Start screen
require('mini.starter').setup()
-- Surround: saiw) sd' sr)'
require('mini.surround').setup()
-- Statusline
local statusline = require 'mini.statusline'
statusline.setup { use_icons = vim.g.have_nerd_font }
-- Cursor position as LINE:COL
---@diagnostic disable-next-line: duplicate-set-field
statusline.section_location = function()
return '%2l:%-2v'
end
end,
}

View File

@@ -1,22 +1,22 @@
-- Neo-tree is a Neovim plugin to browse the file system
-- https://github.com/nvim-neo-tree/neo-tree.nvim
-- lua/plugins/neo-tree.lua
-- File system explorer
return {
'nvim-neo-tree/neo-tree.nvim',
version = '*',
lazy = false,
dependencies = {
'nvim-lua/plenary.nvim',
'nvim-tree/nvim-web-devicons', -- not strictly required, but recommended
'nvim-tree/nvim-web-devicons',
'MunifTanjim/nui.nvim',
},
lazy = false,
keys = {
{ '\\', ':Neotree reveal<CR>', desc = 'NeoTree reveal', silent = true },
{ '\\', ':Neotree reveal<CR>', desc = 'NeoTree: reveal current file', silent = true },
},
opts = {
filesystem = {
filtered_items = {
visible = true, -- Also show '.' files.
visible = true, -- show dotfiles
},
window = {
mappings = {

View File

@@ -1,29 +1,34 @@
-- Simple PDF viewer using pdftoppm + image.nvim
-- Requires: poppler (brew install poppler)
-- lua/plugins/pdf-viewer.lua
-- PDF viewer using pdftoppm + image.nvim (kitty/magick backend)
-- Requires: poppler (brew install poppler), kitty terminal
return {
'3rd/image.nvim',
config = function()
local image = require('image')
image.setup({})
opts = {
backend = 'kitty',
processor = 'magick_cli',
integrations = {
markdown = { enabled = true },
},
},
config = function(_, opts)
local image = require 'image'
image.setup(opts)
local pdf_state = {}
-- Open PDF: convert all pages to PNG via pdftoppm, then display page 1
vim.api.nvim_create_autocmd('BufReadPost', {
pattern = '*.pdf',
callback = function()
local pdf_path = vim.fn.expand('%:p')
local pdf_path = vim.fn.expand '%:p'
local temp_dir = vim.fn.tempname()
vim.fn.mkdir(temp_dir, 'p')
local bufnr = vim.api.nvim_get_current_buf()
-- Convert all pages upfront
vim.notify('Converting PDF...', vim.log.levels.INFO)
vim.notify('Converting PDF…', vim.log.levels.INFO)
vim.fn.jobstart({
'pdftoppm', '-png',
pdf_path, temp_dir .. '/page'
}, {
vim.fn.jobstart({ 'pdftoppm', '-png', pdf_path, temp_dir .. '/page' }, {
on_exit = function(_, code)
vim.schedule(function()
if code ~= 0 then
@@ -31,38 +36,28 @@ return {
return
end
-- Count generated files
-- Count pages
local handle = vim.loop.fs_scandir(temp_dir)
local page_count = 0
if handle then
while true do
local name = vim.loop.fs_scandir_next(handle)
if not name then break end
if name:match('^page%-%d+%.png$') then
page_count = page_count + 1
end
if name:match '^page%-%d+%.png$' then page_count = page_count + 1 end
end
end
pdf_state[bufnr] = {
temp_dir = temp_dir,
current_page = 1,
total_pages = page_count,
}
pdf_state[bufnr] = { temp_dir = temp_dir, current_page = 1, total_pages = page_count }
local function show_page(page_num)
local state = pdf_state[bufnr]
if not state then return end
image.clear()
vim.bo[bufnr].modifiable = true
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {''})
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { '' })
vim.bo[bufnr].modifiable = false
local page_file = string.format('%s/page-%02d.png', state.temp_dir, page_num)
local img = image.from_file(page_file, { buffer = bufnr })
if img then
img:render()
vim.notify(string.format('Page %d / %d', page_num, state.total_pages), vim.log.levels.INFO)
@@ -74,32 +69,30 @@ return {
local function change_page(delta)
local state = pdf_state[bufnr]
if not state then return end
local new_page = state.current_page + delta
if new_page < 1 or new_page > state.total_pages then
return
end
if new_page < 1 or new_page > state.total_pages then return end
state.current_page = new_page
show_page(new_page)
end
-- Keymaps
vim.keymap.set('n', 'j', function() change_page(1) end, { buffer = bufnr, desc = 'Next page' })
vim.keymap.set('n', 'k', function() change_page(-1) end, { buffer = bufnr, desc = 'Previous page' })
vim.keymap.set('n', '<Right>', function() change_page(1) end, { buffer = bufnr, desc = 'Next page' })
vim.keymap.set('n', '<Left>', function() change_page(-1) end, { buffer = bufnr, desc = 'Previous page' })
vim.keymap.set('n', 'q', '<cmd>bd!<cr>', { buffer = bufnr, silent = true })
-- Buffer-local navigation keys
local map = function(lhs, fn, desc)
vim.keymap.set('n', lhs, fn, { buffer = bufnr, desc = desc })
end
map('j', function() change_page(1) end, 'PDF: next page')
map('k', function() change_page(-1) end, 'PDF: previous page')
map('<Right>', function() change_page(1) end, 'PDF: next page')
map('<Left>', function() change_page(-1) end, 'PDF: previous page')
map('q', '<cmd>bd!<cr>', 'PDF: close')
-- Show first page
show_page(1)
end)
end
end,
})
end,
})
-- Cleanup
-- Cleanup temp files on buffer close
vim.api.nvim_create_autocmd('BufDelete', {
pattern = '*.pdf',
callback = function(args)

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

@@ -0,0 +1,61 @@
-- lua/plugins/telescope.lua
-- Fuzzy finder for files, LSP, grep, help, and more
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'
-- Search keymaps
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' })
vim.keymap.set('n', '<leader><leader>', builtin.buffers, { desc = '[ ] Find existing buffers' })
-- Fuzzy search inside current buffer
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' })
-- Grep only open files
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' })
-- Search Neovim config 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,9 @@
-- lua/plugins/todo-comments.lua
-- Highlight and search TODO, NOTE, FIXME, HACK, WARN, PERF comments
return {
'folke/todo-comments.nvim',
event = 'VimEnter',
dependencies = { 'nvim-lua/plenary.nvim' },
opts = { signs = false },
}

View File

@@ -0,0 +1,24 @@
-- lua/plugins/treesitter.lua
-- Syntax highlighting, indentation, and more via Tree-sitter
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',
-- web
'javascript', 'typescript', 'tsx', 'json', 'css',
-- go / php
'go', 'php',
},
auto_install = true,
highlight = {
enable = true,
additional_vim_regex_highlighting = { 'ruby' },
},
indent = { enable = true, disable = { 'ruby' } },
},
}

29
lua/plugins/which-key.lua Normal file
View File

@@ -0,0 +1,29 @@
-- lua/plugins/which-key.lua
-- Shows pending keybind completions as you type
return {
'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>s', group = '[S]earch' },
{ '<leader>t', group = '[T]oggle' },
{ '<leader>h', group = 'Git [H]unk', mode = { 'n', 'v' } },
{ '<leader>d', group = '[D]ebug' },
},
},
}