feat: convert to blink, major lsp changes, lualine

This commit is contained in:
Jason Miller 2025-06-12 12:27:51 +09:00
parent 82102e5e29
commit 51ab444c0d
44 changed files with 1387 additions and 700 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ nvim
spell/
lazy-lock.json
.claude/

View File

@ -1,29 +1,45 @@
## ⚙️ My Neovim Configuration
## ⚙️ My Neovim Configuration
This is my personal neovim configuration and it's unlikely to be perfect
for your use case.
What is in it?
* *Kickstart*: based configuration
* *Neovimacs*: modeless editing support, with common Emacs bindings in insert mode
* *Esc*: to toggle between insert (emacs bindings) and normal (neovim mode)
* *Tabs*: Prev (F1), Next (F2), New (F3), and Close (F4) to jump around
* *Tool Tabs*: Terminal (F5)
* *Movement*: Arrows and Tabs (and, yes, I know)
* *Batteries*: Python LSP, completion, treesitter
- _Kickstart_: based configuration
- _Neovimacs_: modeless editing support, with common Emacs bindings in insert mode
- _Esc_: to toggle between insert (emacs bindings) and normal (neovim mode)
- _Tabs_: Prev (F1), Next (F2), New (F3), and Close (F4) to jump around
- _Tool Tabs_: Terminal (F5)
- _Movement_: Arrows and Tabs (and, yes, I know)
- _Batteries_: Python LSP, completion, treesitter
## 📦 Installation
#### Prep
Suggested:
Neovim >= 0.10 or later is required, you may need to get it from the Neovim PPA or similar:
```bash
sudo apt install -y gcc python3-pip python3-venv git make unzip ripgrep gzip wget curl fd-find npm
sudo add-apt-repository ppa:neovim-ppa/unstable
sudo apt-get update
sudo apt-get install neovim
```
Suggested Packages:
```bash
sudo apt install -y cargo gcc python3-pip python3-venv git make unzip ripgrep gzip wget curl fd-find npm xclip
sudo npm install -g tree-sitter-cli
```
If you have an old version of NodeJS, pick up a new one:
```bash
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
```
Optional based on use-case:
```bash
@ -52,9 +68,9 @@ ln -s neovimrc nvim
Checking overall health and options:
```
:CheckHealth
:checkhealth
:Telescope vim_options
:lua print(vim.inspect(vim.opt.XXXX))
:set option?
```
Beyond [which-key](https://github.com/folke/which-key.nvim), you can use the following
@ -65,8 +81,18 @@ nvim commands to help you track down key bindings and resolve conflicts:
:verbose nmap <C-n> -- for normal mode
:nmap <localleader> -- to see leader commands
:WhichKey -- see above
:lua = <expr> -- run lua expression
```
Use `nvim -u NONE -U NONE -N -i NONE` to test with w/o config if things go wrong.
#### Recommended Visits
```
:help <module> -- help for modules
:help telescope.setup() -- help for the setup section
:Telescope help_tags -- search help
```
### References

View File

@ -0,0 +1,16 @@
-- Fix for nvim-cmp feedkeys issue where 'lua require"cmp.utils.feedkeys".run(#)'
-- gets inserted into buffers instead of being executed
-- This overrides the problematic debounce_next_tick_by_keymap function
local ok, async = pcall(require, 'cmp.utils.async')
if not ok then
return
end
-- Override the problematic function with a safer implementation
async.debounce_next_tick_by_keymap = function(callback)
return function()
-- Use vim.schedule instead of feedkeys to avoid inserting command text
vim.schedule(callback)
end
end

10
after/plugin/final.lua Normal file
View File

@ -0,0 +1,10 @@
local builtin = require 'telescope.builtin'
vim.keymap.set('n', '<C-s>', function()
builtin.current_buffer_fuzzy_find()
end)
-- Use vimacs version
--
-- vim.keymap.set('i', '<C-s>', function()
-- builtin.current_buffer_fuzzy_find()
--end)

234
init.lua
View File

@ -1,18 +1,38 @@
-- NOTE: Leader before plugins are loaded (otherwise wrong leader will be used)
-- Check Neovim version requirement
if vim.fn.has 'nvim-0.11' == 0 then
vim.api.nvim_err_writeln 'Error: Neovim 0.11 or higher is required for this configuration.'
vim.api.nvim_err_writeln('Current version: ' .. vim.version().major .. '.' .. vim.version().minor .. '.' .. vim.version().patch)
vim.api.nvim_err_writeln 'Please update Neovim to continue.'
return
end
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
vim.g.have_nerd_font = true
-- User settings
vim.g.tabnine_enable = true -- JCM
vim.g.autocomplete_enable = true
-- vim.o.autochdir = true -- to open from buffer dir
vim.g.format_on_save_enabled = true
-- Add emacs/rl keybindings to this configuration?
vim.g.neovimacs_bindings = true
vim.g.neovimacs_insert = true
-- Margins
vim.opt.title = false -- in status, not great with tmux
vim.opt.number = true -- show line number
vim.opt.relativenumber = false
vim.opt.showmode = false
vim.opt.signcolumn = 'yes'
vim.opt.cursorline = true
vim.opt.scrolloff = 10
vim.opt.colorcolumn = '120'
-- vim.opt.breakindent = true
vim.o.title = false -- in status, not great with tmux
vim.o.number = true -- show line number
vim.o.relativenumber = false
vim.o.showmode = false
vim.o.signcolumn = 'yes'
vim.o.cursorline = true
vim.o.scrolloff = 10
vim.o.colorcolumn = '120'
vim.o.guicursor = 'n-v-i-c:block-Cursor' -- keep block cursor
-- vim.o.breakindent = true
-- TODO: replace with osc52 provider once iTerm2 supports it better
if vim.env.DISPLAY then
@ -21,61 +41,66 @@ if vim.env.DISPLAY then
vim.opt.clipboard:append { 'unnamed', 'unnamedplus' }
end)
end
vim.opt.mouse = 'nvi'
vim.o.mouse = 'nvi'
end
-- File related
vim.opt.autochdir = false
vim.opt.swapfile = false
vim.opt.backup = false
vim.opt.writebackup = false
vim.opt.undofile = true
vim.opt.undodir = os.getenv 'HOME' .. '/.vim/undodir'
vim.o.swapfile = false
vim.o.backup = false
vim.o.writebackup = false
vim.o.undofile = true
vim.o.undodir = os.getenv 'HOME' .. '/.vim/undodir'
if vim.fn.has 'win32' == 1 or vim.fn.has 'win64' == 1 then
vim.opt.fileformats = 'dos,unix,mac'
vim.o.fileformats = 'dos,unix,mac'
elseif vim.fn.has 'mac' == 1 then
vim.opt.fileformats = 'mac,unix,dos'
vim.o.fileformats = 'mac,unix,dos'
else
vim.opt.fileformats = 'unix,dos,mac'
vim.o.fileformats = 'unix,dos,mac'
end
vim.opt.wildmenu = true
vim.opt.wildmode = 'list:longest,list:full' -- list choices, expand singles
vim.o.wildmenu = true
vim.o.wildmode = 'list:longest,list:full' -- expand to longest match, then list choices
vim.keymap.set('n', '<leader>p', '', { desc = '[P] +Explore' })
vim.keymap.set('n', '<leader>pv', vim.cmd.Ex, { desc = 'Open explorer [V]' })
-- Search
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.wrapscan = false
vim.opt.inccommand = 'split' -- preview
vim.o.ignorecase = true
vim.o.smartcase = true
vim.o.wrapscan = false
vim.o.inccommand = 'split' -- preview
-- Performance
vim.opt.updatetime = 250
vim.opt.timeoutlen = 300
vim.o.updatetime = 250
vim.o.timeoutlen = 3000
-- Windows
-- :sp/:vsp to split windows
-- C-w to jump between them
vim.opt.splitright = true
vim.opt.splitbelow = true
vim.o.splitright = true
vim.o.splitbelow = true
-- Whitespace
vim.opt.list = true
vim.o.list = true
vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '' }
-- Spelling: "z=" in normal to suggest replacements
vim.opt.spelllang = 'en_us'
vim.opt.spell = true
vim.o.spelllang = 'en_us'
vim.o.spell = true
-- Diagnostics
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' })
vim.keymap.set('n', '<leader>td', function()
vim.diagnostic.enable(not vim.diagnostic.is_enabled())
end, { silent = true, noremap = true, desc = 'Toggle [D]iagnostics' })
-- Quick diagnostics
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = '[Q]uickfix diagnostics' })
-- Allow 'q' to close simple diagnostic windows
vim.api.nvim_create_autocmd('FileType', {
pattern = { 'qf', 'help', 'checkhealth' },
callback = function()
vim.keymap.set('n', 'q', '<cmd>bd<cr>', { silent = true, buffer = true })
end,
})
-- Highlight when yanking (copying) text - "yap"
vim.api.nvim_create_autocmd('TextYankPost', {
desc = 'Highlight when yanking (copying) text',
group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }),
group = vim.api.nvim_create_augroup('ks-highlight-yank', { clear = true }),
callback = function()
vim.highlight.on_yank()
end,
@ -83,16 +108,17 @@ vim.api.nvim_create_autocmd('TextYankPost', {
-- Install Lazy from Github
local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
local uv = vim.uv or vim.loop
if not uv.fs_stat(lazypath) then
if not vim.uv.fs_stat(lazypath) then
local lazyrepo = 'https://github.com/folke/lazy.nvim.git'
local out = vim.fn.system { 'git', 'clone', '--filter=blob:none', '--branch=stable', lazyrepo, lazypath }
if vim.v.shell_error ~= 0 then
error('Error cloning lazy.nvim:\n' .. out)
end
end ---@diagnostic disable-next-line: undefined-field
vim.opt.rtp:prepend(lazypath)
local rtp = vim.opt.rtp
rtp:prepend(lazypath)
-- :Lazy
require('lazy').setup({
@ -104,26 +130,19 @@ require('lazy').setup({
require 'plugins.gitsigns', -- Add git changes to gutter
require 'plugins.which-key', -- Show keybindings as you go
require 'plugins.telescope', -- Fuzzy finder (file & LSP search)
require 'plugins.lsp', -- Language server (types, errors, signatures)
require 'plugins.lualine', -- Statusbar at bottom
require 'plugins.mason', -- Mason: LSP/DAP/Linter/Formatter installer
require 'plugins.conform', -- Auto-reformat files on save
require 'plugins.claude-code', -- LLM: Claude Code
require 'plugins.venv', -- Virtual environment selection
require 'plugins.autocomplete', -- Auto-completion
require 'plugins.autocomplete-blink', -- Auto-completion (new, incomplete)
require 'plugins.colorscheme', -- Color scheme
require 'plugins.misc', -- Misc small plugins
require 'plugins.mini', -- Misc small plugins
require 'plugins.treesitter', -- Code highlights and reference navigation
require 'plugins.todo', -- Highlight todo, notes in comments
require 'plugins.tabnine', -- Tabnine LLM coding assistant
-- Treesitter navigation
-- :help nvim-treesitter
-- :Telescope help_tags
-- See `:help telescope` and `:help telescope.setup()`
-- To see keymaps do this:
-- - Insert mode: <c-/>
-- - Normal mode: ?
-- Venv selector
-- WIP: https://github.com/linux-cultist/venv-selector.nvim/tree/regexp
-- Wait for a updated release
require 'plugins.avante', -- LLM: Cursor alternative
require 'plugins.tabnine', -- LLM: Tabnine coding assistant
require 'plugins.tiny-inline-diagnostics', -- Better diagnostics
}, {
ui = {
-- If you are using a Nerd Font: set icons to an empty table which will use the
@ -166,12 +185,15 @@ local function recenter_and_refresh()
end
vim.keymap.set('n', '<C-l>', recenter_and_refresh, { noremap = true, silent = true })
vim.keymap.set('i', '<C-l>', recenter_and_refresh, { noremap = true, silent = true })
vim.keymap.set('c', '<C-s>', '<CR>n', { expr = true })
--- (Re)Undefine undesirable behavior
vim.api.nvim_set_keymap('i', '<Esc>', '<Esc>', { noremap = true })
vim.api.nvim_set_keymap('n', '<C-a>', '<Nop>', { noremap = true })
vim.api.nvim_set_keymap('n', '<C-x>', '<Nop>', { noremap = true })
if vim.g.neovimacs_bindings then
vim.api.nvim_set_keymap('i', '<Esc>', '<Esc>', { noremap = true })
vim.api.nvim_set_keymap('n', '<C-a>', '<Nop>', { noremap = true })
vim.api.nvim_set_keymap('n', '<C-x><C-c>', ':confirm qall<CR>', { noremap = true })
vim.api.nvim_set_keymap('n', '<C-x><C-s>', ':update<CR>', { noremap = true })
vim.api.nvim_set_keymap('n', '<C-x><C-f>', ':hide edit ', { noremap = true })
end
-- Terminals/Shell
-- :terminal
@ -181,55 +203,53 @@ vim.keymap.set('c', '<Up>', 'pumvisible() ? "<C-p>" : "<Up>"', { expr = true, no
vim.keymap.set('c', '<Down>', 'pumvisible() ? "<C-n>" : "<Down>"', { expr = true, noremap = true })
vim.keymap.set('c', '<Left>', 'pumvisible() ? "<C-b>" : "<Left>"', { expr = true, noremap = true })
vim.keymap.set('c', '<Right>', 'pumvisible() ? "<C-f>" : "<Right>"', { expr = true, noremap = true })
-- vim.opt.wildcharm = '<Tab>' -- set wildcharm=<C-Z>
-- LSP in insert mode
vim.lsp.handlers['textDocument/publishDiagnostics'] = vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
update_in_insert = false,
})
-- LSP diagnostics configuration
vim.diagnostic.config { virtual_text = false }
-- Tab management keys
-- F1-Prev, F2-Next, F3-New, F4-Close
--
local function safe_tabclose()
local bufnr = vim.api.nvim_get_current_buf()
local buf_windows = vim.call('win_findbuf', bufnr)
local modified = vim.api.nvim_get_option_value('modified', { buf = bufnr })
-- vim.diagnostic.config {
-- virtual_text = {
-- source = 'if_many',
-- },
-- signs = true,
-- underline = true,
-- update_in_insert = true,
-- severity_sort = true,
--}
if vim.fn.tabpagenr '$' == 1 then
-- last tab, no-op
return
elseif modified and #buf_windows == 1 then
vim.ui.input({
prompt = 'Buffer modified, are you sure? ',
}, function(input)
if input == 'y' then
vim.cmd 'tabclose'
end
end)
else
vim.cmd 'tabclose'
-- Register and enable LSP servers
-- See https://github.com/neovim/nvim-lspconfig/tree/master/lua/lspconfig/configs
-- for sample starter configurations.
local lsp_servers = {
'ast_grep',
'clangd',
'lua_ls',
'bashls',
'marksman',
'python',
'taplo',
'yamlls',
'ts_ls',
'dockerls',
}
-- Conditional on executables
if vim.fn.executable 'go' == 1 then
table.insert(lsp_servers, 'gopls')
end
if vim.fn.executable 'nixd' == 1 then
table.insert(lsp_servers, 'nil_ls')
end
require 'lsp/keybindings'
for _, server in ipairs(lsp_servers) do
local ok, config = pcall(require, 'lsp.' .. server)
if ok and config.name then
vim.lsp.config[config.name] = config
vim.lsp.enable(server)
end
end
vim.keymap.set('t', '<F1>', vim.cmd.tabp, { noremap = true, silent = true })
vim.keymap.set('t', '<F2>', vim.cmd.tabn, { noremap = true, silent = true })
vim.keymap.set('t', '<F3>', '<C-\\><C-n>:tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('t', '', '<C-\\><C-n>:tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('t', '<F4>', safe_tabclose, { noremap = true, silent = true })
vim.keymap.set('t', '<F5>', '<C-\\><C-n><Esc>:tab new<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '<F1>', vim.cmd.tabp, { noremap = true, silent = true })
vim.keymap.set('n', '<F2>', vim.cmd.tabn, { noremap = true, silent = true })
vim.keymap.set('n', '<F3>', ':tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '', ':tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '<F4>', safe_tabclose, { noremap = true, silent = true })
vim.keymap.set('n', '<F5>', ':tab term<CR>', { noremap = true, silent = true })
vim.keymap.set('i', '<F1>', vim.cmd.tabp, { noremap = true, silent = true })
vim.keymap.set('i', '<F2>', vim.cmd.tabn, { noremap = true, silent = true })
vim.keymap.set('i', '<F3>', '<Esc>:tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('i', '', '<Esc>:tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('i', '<F4>', safe_tabclose, { noremap = true, silent = true })
vim.keymap.set('i', '<F5>', '<Esc>:tab term<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '<leader>tp', vim.cmd.tabn, { desc = 'Tab [p]revious' })
vim.keymap.set('n', '<leader>tn', vim.cmd.tabp, { desc = 'Tab [n]ext' })
vim.keymap.set('n', '<leader>to', vim.cmd.tabnew, { desc = 'Tab [o]pen' })
vim.keymap.set('n', '<leader>tc', safe_tabclose, { desc = 'Tab [c]lose' })
-- Inlay hints?
vim.lsp.inlay_hint.enable(true)
require 'utils/windows'

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,105 +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
'williamboman/mason.nvim',
'jay-babu/mason-nvim-dap.nvim',
-- Add your own debuggers here
'leoluz/nvim-dap-go',
},
keys = function(_, keys)
local dap = require 'dap'
local dapui = require 'dapui'
return {
-- Basic debugging keymaps, feel free to change to your liking!
{ '<F5>', dap.continue, desc = 'Debug: Start/Continue' },
{ '<F1>', dap.step_into, desc = 'Debug: Step Into' },
{ '<F2>', dap.step_over, desc = 'Debug: Step Over' },
{ '<F3>', dap.step_out, desc = 'Debug: Step Out' },
{ '<leader>b', dap.toggle_breakpoint, desc = 'Debug: Toggle Breakpoint' },
{
'<leader>B',
function()
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>', dapui.toggle, desc = 'Debug: See last session result.' },
unpack(keys),
}
end,
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',
},
}
-- 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 = '',
},
},
}
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,
},
}
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 = 'stage git hunk' })
map('v', '<leader>hr', function()
gitsigns.reset_hunk { vim.fn.line '.', vim.fn.line 'v' }
end, { desc = 'reset git 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.undo_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.toggle_deleted, { 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,55 +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()
lint.try_lint()
end,
})
end,
},
}

View File

@ -1,25 +0,0 @@
-- Neo-tree is a Neovim plugin to browse the file system
-- https://github.com/nvim-neo-tree/neo-tree.nvim
return {
'nvim-neo-tree/neo-tree.nvim',
version = '*',
dependencies = {
'nvim-lua/plenary.nvim',
'nvim-tree/nvim-web-devicons', -- not strictly required, but recommended
'MunifTanjim/nui.nvim',
},
cmd = 'Neotree',
keys = {
{ '\\', ':Neotree reveal<CR>', desc = 'NeoTree reveal' },
},
opts = {
filesystem = {
window = {
mappings = {
['\\'] = 'close_window',
},
},
},
},
}

12
lua/lsp/ast_grep.lua Normal file
View File

@ -0,0 +1,12 @@
return {
cmd = { 'ast-grep', 'lsp' },
filetypes = { 'java', 'javascript', 'html' },
root_dir = function(fname)
return require('lspconfig.util').find_git_ancestor(fname) or vim.fn.getcwd()
end,
settings = {
['ast-grep'] = {
enable = true,
},
},
}

6
lua/lsp/bashls.lua Normal file
View File

@ -0,0 +1,6 @@
return {
name = 'bashls',
cmd = { 'bash-language-server', 'start' },
root_dir = vim.fs.dirname(vim.fs.find({ '.git' }, { upward = true })[1]),
filetypes = { 'sh', 'bash' },
}

37
lua/lsp/clangd.lua Normal file
View File

@ -0,0 +1,37 @@
local tools = require 'utils.tools'
-- Get LSP capabilities with cmp support
local capabilities = tools.get_lsp_capabilities()
-- Setup clangd LSP using autocmd
vim.api.nvim_create_autocmd('FileType', {
pattern = { 'c', 'cpp', 'h', 'hpp' },
callback = function()
local clangd_path = tools.find_executable 'clangd'
if clangd_path then
vim.lsp.start {
name = 'clangd',
cmd = {
clangd_path,
'--background-index',
'--clang-tidy',
'--header-insertion=iwyu',
'--completion-style=detailed',
'--function-arg-placeholders',
'--fallback-style=llvm',
},
root_dir = vim.fs.dirname(vim.fs.find({ 'compile_commands.json', 'compile_flags.txt', '.clangd', '.git' }, { upward = true })[1]),
capabilities = capabilities,
init_options = {
usePlaceholders = true,
completeUnimported = true,
clangdFileStatus = true,
},
}
end
end,
})
-- Return empty config since we handle clangd LSP manually via autocmd above
return {}

6
lua/lsp/dockerls.lua Normal file
View File

@ -0,0 +1,6 @@
return {
name = 'dockerls',
cmd = { 'docker-langserver', '--stdio' },
root_dir = vim.fs.dirname(vim.fs.find({ 'Dockerfile', 'dockerfile', '.dockerignore' }, { upward = true })[1]),
filetypes = { 'dockerfile', 'Dockerfile' },
}

45
lua/lsp/gopls.lua Normal file
View File

@ -0,0 +1,45 @@
local tools = require 'utils.tools'
-- Get LSP capabilities with cmp support
local capabilities = tools.get_lsp_capabilities()
-- Setup gopls LSP using autocmd
vim.api.nvim_create_autocmd('FileType', {
pattern = 'go',
callback = function()
local gopls_path = tools.find_executable 'gopls'
if gopls_path then
vim.lsp.start {
name = 'gopls',
cmd = { gopls_path },
root_dir = vim.fs.dirname(vim.fs.find({ 'go.mod', 'go.work', '.git' }, { upward = true })[1]),
capabilities = capabilities,
settings = {
gopls = {
analyses = {
unusedparams = true,
},
staticcheck = true,
gofumpt = true,
completeUnimported = true,
usePlaceholders = true,
experimentalPostfixCompletions = true,
hints = {
assignVariableTypes = true,
compositeLiteralFields = true,
compositeLiteralTypes = true,
constantValues = true,
functionTypeParameters = true,
parameterNames = true,
rangeVariableTypes = true,
},
},
},
}
end
end,
})
-- Return empty config since we handle gopls LSP manually via autocmd above
return {}

68
lua/lsp/keybindings.lua Normal file
View File

@ -0,0 +1,68 @@
-- LSP keybindings and autocmds
-- Extracted from legacy lsp.lua plugin configuration
-- LSP attach autocmd with keybindings
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)
vim.keymap.set('n', keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
end
-- Navigation keybindings
map('gd', require('telescope.builtin').lsp_definitions, '[G]oto [D]efinition')
map('gI', require('telescope.builtin').lsp_implementations, '[G]oto [I]mplementation')
map('<leader>D', require('telescope.builtin').lsp_type_definitions, 'Type [D]efinition')
map('<leader>ds', require('telescope.builtin').lsp_document_symbols, '[D]ocument [S]ymbols')
map('<leader>ws', require('telescope.builtin').lsp_dynamic_workspace_symbols, '[W]orkspace [S]ymbols')
map('<leader>rn', vim.lsp.buf.rename, '[R]e[n]ame')
map('<leader>ca', vim.lsp.buf.code_action, '[C]ode [A]ction')
-- LSP gr
map('grn', vim.lsp.buf.rename, '[R]e[n]ame') -- rename
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') -- func def
map('grD', vim.lsp.buf.declaration, '[G]oto [D]eclaration') -- header def
map('grt', require('telescope.builtin').lsp_type_definitions, '[G]oto [T]ype Definition')
map('gO', require('telescope.builtin').lsp_document_symbols, 'Open Document Symbols') -- open symbols
map('gW', require('telescope.builtin').lsp_dynamic_workspace_symbols, 'Open Workspace Symbols') -- open ws
-- Document highlighting
local client = vim.lsp.get_client_by_id(event.data.client_id)
if client and client.supports_method(vim.lsp.protocol.Methods.textDocument_documentHighlight) 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(vim.lsp.protocol.Methods.textDocument_inlayHint) 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,
})
vim.keymap.set('n', '<leader>td', function()
vim.diagnostic.enable(not vim.diagnostic.is_enabled())
end, { silent = true, noremap = true, desc = '[d]iagnostics' })

32
lua/lsp/lua_ls.lua Normal file
View File

@ -0,0 +1,32 @@
-- Get LSP capabilities with cmp support
local capabilities = vim.lsp.protocol.make_client_capabilities()
local ok, cmp_nvim_lsp = pcall(require, 'cmp_nvim_lsp')
if ok then
capabilities = vim.tbl_deep_extend('force', capabilities, cmp_nvim_lsp.default_capabilities())
end
-- Lua LSP configuration for vim.lsp.enable()
return {
name = 'lua_ls',
cmd = { 'lua-language-server' },
filetypes = { 'lua' },
root_dir = vim.fs.dirname(vim.fs.find({ '.luarc.json', '.luarc.jsonc', '.stylua.toml', 'stylua.toml', 'selene.toml' }, { upward = true })[1]),
capabilities = capabilities,
settings = {
Lua = {
completion = {
callSnippet = 'Replace',
},
diagnostics = {
globals = { 'vim' },
},
workspace = {
library = vim.api.nvim_get_runtime_file('', true),
checkThirdParty = false,
},
telemetry = {
enable = false,
},
},
},
}

6
lua/lsp/marksman.lua Normal file
View File

@ -0,0 +1,6 @@
return {
name = 'marksman',
cmd = { 'marksman', 'server' },
root_dir = vim.fs.dirname(vim.fs.find({ '.git', '.marksman.toml' }, { upward = true })[1]),
filetypes = { 'markdown', 'md' },
}

6
lua/lsp/nil_ls.lua Normal file
View File

@ -0,0 +1,6 @@
return {
name = 'nixd',
cmd = { 'nixd' },
root_dir = vim.fs.dirname(vim.fs.find({ 'flake.nix', 'default.nix', 'shell.nix' }, { upward = true })[1]),
filetypes = { 'nix' },
}

119
lua/lsp/python.lua Normal file
View File

@ -0,0 +1,119 @@
local tools = require 'utils.tools'
-- Get LSP capabilities with cmp support
local capabilities = tools.get_lsp_capabilities()
-- Setup multiple Python LSP servers using autocmd (not via vim.lsp.enable)
vim.api.nvim_create_autocmd('FileType', {
pattern = 'python',
callback = function()
-- Stop any unwanted Python LSPs that may have been auto-started
local unwanted_lsps = { 'pylsp', 'pyright', 'mypy' }
local clients = vim.lsp.get_clients { bufnr = 0 }
for _, client in ipairs(clients) do
for _, unwanted in ipairs(unwanted_lsps) do
if client.name == unwanted then
vim.lsp.stop_client(client.id, true)
vim.notify('Stopped unwanted LSP: ' .. client.name, vim.log.levels.INFO)
end
end
end
-- Common root directory lookup for all Python LSPs
local current_file = vim.api.nvim_buf_get_name(0)
local file_dir = vim.fs.dirname(current_file)
local root_dir = vim.fs.dirname(
vim.fs.find(
{ 'pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', 'Pipfile', 'pyrightconfig.json', 'ruff.toml', '.ruff.toml' },
{ path = file_dir, upward = true }
)[1]
)
local pyright_path = tools.find_tool 'basedpyright-langserver'
local ruff_path = tools.find_tool 'ruff'
local jedi_path = tools.find_tool 'jedi-language-server'
local python3_path = tools.find_tool 'python3'
-- Setup pyright (hover and type checking)
-- https://docs.basedpyright.com/dev/
if pyright_path then
local pyright_capabilities = vim.tbl_deep_extend('force', capabilities, {})
vim.lsp.start {
name = 'basedpyright',
cmd = { pyright_path, '--stdio' },
root_dir = root_dir,
capabilities = pyright_capabilities,
settings = {
basedpyright = {
analysis = {
autoImportCompletion = true,
autoSearchPaths = true,
diagnosticMode = 'openFilesOnly',
typeCheckingMode = 'off', -- 'basic',
useLibraryCodeForTypes = true,
inlayHints = {
callArgumentNames = true,
},
},
pythonPath = python3_path,
extraPaths = vim.list_extend(
vim.fn.isdirectory(root_dir .. '/python') == 1 and { root_dir .. '/python' } or {},
vim.split(vim.env.PYTHONPATH or '', ':')
),
},
},
handlers = {
['textDocument/publishDiagnostics'] = function() end,
},
}
end
-- Setup ruff (linting and formatting)
if ruff_path then
local ruff_capabilities = vim.tbl_deep_extend('force', capabilities, {})
ruff_capabilities.textDocument.completion = nil
ruff_capabilities.hoverProvider = false
vim.lsp.start {
name = 'ruff',
cmd = { ruff_path, 'server' },
root_dir = root_dir,
capabilities = ruff_capabilities,
handlers = {
['textDocument/hover'] = function() end,
['textDocument/completion'] = function() end,
},
}
end
-- Setup jedi language server (completions)
if jedi_path then
local jedi_capabilities = vim.tbl_deep_extend('force', capabilities, {})
jedi_capabilities.hoverProvider = false
vim.lsp.start {
name = 'jedi_language_server',
cmd = { jedi_path },
root_dir = root_dir,
capabilities = jedi_capabilities,
init_options = {
diagnostics = {
enable = false,
didOpen = false,
didChange = false,
didSave = false,
},
},
handlers = {
['textDocument/publishDiagnostics'] = function() end,
['textDocument/hover'] = function() end,
},
}
end
end,
})
-- Return empty config since we handle Python LSP manually via autocmd above
return {}

6
lua/lsp/taplo.lua Normal file
View File

@ -0,0 +1,6 @@
return {
name = 'taplo',
cmd = { 'taplo', 'lsp', 'stdio' },
root_dir = vim.fs.dirname(vim.fs.find({ '.git', '*.toml' }, { upward = true })[1]),
filetypes = { 'toml' },
}

38
lua/lsp/ts_ls.lua Normal file
View File

@ -0,0 +1,38 @@
-- Get LSP capabilities with cmp support
local capabilities = vim.lsp.protocol.make_client_capabilities()
local ok, cmp_nvim_lsp = pcall(require, 'cmp_nvim_lsp')
if ok then
capabilities = vim.tbl_deep_extend('force', capabilities, cmp_nvim_lsp.default_capabilities())
end
return {
name = 'ts_ls',
cmd = { 'typescript-language-server', '--stdio' },
root_dir = vim.fs.dirname(vim.fs.find({ 'package.json', 'tsconfig.json', 'jsconfig.json', '.git' }, { upward = true })[1]),
filetypes = { 'ts' },
capabilities = capabilities,
settings = {
typescript = {
inlayHints = {
includeInlayParameterNameHints = 'all',
includeInlayParameterNameHintsWhenArgumentMatchesName = false,
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
includeInlayPropertyDeclarationTypeHints = true,
includeInlayFunctionLikeReturnTypeHints = true,
includeInlayEnumMemberValueHints = true,
},
},
javascript = {
inlayHints = {
includeInlayParameterNameHints = 'all',
includeInlayParameterNameHintsWhenArgumentMatchesName = false,
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
includeInlayPropertyDeclarationTypeHints = true,
includeInlayFunctionLikeReturnTypeHints = true,
includeInlayEnumMemberValueHints = true,
},
},
},
}

27
lua/lsp/yamlls.lua Normal file
View File

@ -0,0 +1,27 @@
-- Get LSP capabilities with cmp support
local capabilities = vim.lsp.protocol.make_client_capabilities()
local ok, cmp_nvim_lsp = pcall(require, 'cmp_nvim_lsp')
if ok then
capabilities = vim.tbl_deep_extend('force', capabilities, cmp_nvim_lsp.default_capabilities())
end
return {
name = 'yamlls',
cmd = { 'yaml-language-server', '--stdio' },
filetypes = { 'yaml', 'yml' },
root_dir = vim.fs.dirname(vim.fs.find({ '.git', 'docker-compose.yml', 'docker-compose.yaml' }, { upward = true })[1]),
capabilities = capabilities,
settings = {
telemetry = {
enabled = false,
},
yaml = {
schemas = {
['https://json.schemastore.org/github-workflow.json'] = '/.github/workflows/*',
['https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json'] = '/docker-compose*.{yml,yaml}',
['https://json.schemastore.org/kustomization.json'] = 'kustomization.{yml,yaml}',
['https://json.schemastore.org/chart.json'] = '/Chart.{yml,yaml}',
},
},
},
}

View File

@ -0,0 +1,177 @@
-- TODO: blink is faster than nvim-cmp, but still missing integration
-- with a good number of other plugins. fuzzy is still being improved.
return {
{
'saghen/blink.cmp',
-- branch = 'fuzzy-scoring',
-- commit = 'b9cca35c503f6d220b1162e604e06477db02a23c',
version = 'v1.*',
branch = 'main',
commit = '2c3d276',
event = 'InsertEnter',
enabled = function()
return vim.g.autocomplete_enable
end,
dependencies = {
'saghen/blink.compat',
{
'L3MON4D3/LuaSnip',
build = (function()
if vim.fn.has 'win32' == 1 or vim.fn.executable 'make' == 0 then
return
end
return 'make install_jsregexp'
end)(),
dependencies = {
-- `friendly-snippets` contains a variety of premade snippets.
-- See the README about individual language/framework/plugin snippets:
-- https://github.com/rafamadriz/friendly-snippets
-- {
-- 'rafamadriz/friendly-snippets',
-- config = function()
-- require('luasnip.loaders.from_vscode').lazy_load()
-- end,
-- },
},
},
},
opts = {
--cmdline = { enabled = false },
cmdline = {
keymap = {
-- recommended, as the default keymap will only show and select the next item
['<Tab>'] = { 'show', 'accept' },
['<down>'] = { 'select_next', 'fallback' },
['<up>'] = { 'select_prev', 'fallback' },
['<left>'] = { 'scroll_documentation_up', 'fallback' },
['<right>'] = { 'scroll_documentation_down', 'fallback' },
['<C-g>'] = { 'cancel', 'fallback' },
['<C-h>'] = { 'show', 'fallback' },
},
completion = {
menu = {
auto_show = true,
},
list = {
selection = {
preselect = true,
auto_insert = true,
},
},
},
},
fuzzy = {
implementation = 'lua', -- slower, more flexible, patchable
sorts = { 'exact', 'score', 'sort_text' },
use_frecency = false,
use_proximity = false,
max_typos = function()
return 0
end,
},
completion = {
keyword = { range = 'full' },
-- list = { selection = 'auto_insert' },
trigger = {
show_on_insert_on_trigger_character = false, -- Use C-Space
},
accept = {
auto_brackets = {
enabled = true,
},
},
menu = {
border = 'single',
draw = {
columns = {
{ 'kind_icon' },
-- { 'source_name' },
{ 'label', 'label_description', gap = 1 },
},
},
},
documentation = {
window = {
border = 'single',
},
},
},
keymap = {
preset = 'default',
-- Disable conflicting emacs keys - let neovimacs handle them
['<C-k>'] = {}, -- Remove C-k (emacs: kill to end of line)
['<C-b>'] = {}, -- Remove C-b (emacs: backward char)
['<C-f>'] = {}, -- Remove C-f (emacs: forward char)
['<C-p>'] = {}, -- Remove C-p (emacs: previous line)
['<C-n>'] = {}, -- Remove C-n (emacs: next line)
['<C-e>'] = {}, -- Remove C-e (emacs: end of line)
['<C-y>'] = {}, -- Remove C-y (emacs: yank)
['<C-Space>'] = {}, -- Remove C-Space (emacs: highlight)
-- Alternative completion navigation
['<M-j>'] = { 'select_next', 'fallback' }, -- Alt-j for next
['<M-k>'] = { 'select_prev', 'fallback' }, -- Alt-k for prev
['<M-h>'] = { 'scroll_documentation_up', 'fallback' }, -- Alt-h for doc up
['<M-l>'] = { 'scroll_documentation_down', 'fallback' }, -- Alt-l for doc down
-- Keep arrow keys and other non-conflicting bindings
['<down>'] = { 'select_next', 'fallback' },
['<up>'] = { 'select_prev', 'fallback' },
['<left>'] = { 'scroll_documentation_up', 'fallback' },
['<right>'] = { 'scroll_documentation_down', 'fallback' },
['<Tab>'] = { 'accept', 'fallback' },
['<C-g>'] = { 'cancel', 'fallback' },
['<C-h>'] = { 'show', 'fallback' },
['<C-right>'] = {
function(cmp)
local luasnip = require 'luasnip'
if luasnip.expand_or_locally_jumpable() then
luasnip.expand_or_jump()
else
return cmp.fallback()
end
end,
'fallback',
},
['<C-left>'] = {
function(cmp)
local luasnip = require 'luasnip'
if luasnip.locally_jumpable(-1) then
luasnip.jump(-1)
else
return cmp.fallback()
end
end,
'fallback',
},
},
signature = {
enabled = true,
window = {
border = 'single',
},
},
sources = {
default = { 'lsp', 'path', 'snippets', 'buffer' },
per_filetype = {},
-- TODO: broken
providers = {
tabnine = {
name = 'Tabnine',
module = 'blink.compat.source',
},
},
},
snippets = {
preset = 'luasnip',
},
},
config = function(_, opts)
local luasnip = require 'luasnip'
luasnip.config.setup {}
require('blink.cmp').setup(opts)
end,
},
}

View File

@ -1,83 +0,0 @@
return {
{
'hrsh7th/nvim-cmp',
event = 'InsertEnter',
dependencies = {
{
'L3MON4D3/LuaSnip',
build = (function()
if vim.fn.has 'win32' == 1 or vim.fn.executable 'make' == 0 then
return
end
return 'make install_jsregexp'
end)(),
dependencies = {
-- `friendly-snippets` contains a variety of premade snippets.
-- See the README about individual language/framework/plugin snippets:
-- https://github.com/rafamadriz/friendly-snippets
-- {
-- 'rafamadriz/friendly-snippets',
-- config = function()
-- require('luasnip.loaders.from_vscode').lazy_load()
-- end,
-- },
},
},
'saadparwaiz1/cmp_luasnip',
-- Other completions
'hrsh7th/cmp-nvim-lsp',
'hrsh7th/cmp-path',
},
config = function()
-- See `:help cmp`
local cmp = require 'cmp'
local luasnip = require 'luasnip'
luasnip.config.setup {}
cmp.setup {
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
completion = {
autocomplete = false, -- Use C-Space
completeopt = 'menu,menuone,noinsert',
},
-- `:help ins-completion`
mapping = cmp.mapping.preset.insert {
['<down>'] = cmp.mapping.select_next_item(),
['<up>'] = cmp.mapping.select_prev_item(),
['<left>'] = cmp.mapping.scroll_docs(-4),
['<right>'] = cmp.mapping.scroll_docs(4),
['<Tab>'] = cmp.mapping.confirm { select = true },
['<C-g>'] = cmp.mapping.abort(),
['<C-h>'] = cmp.mapping.complete {},
['<C-right>'] = cmp.mapping(function()
if luasnip.expand_or_locally_jumpable() then
luasnip.expand_or_jump()
end
end, { 'i', 's' }),
['<C-left>'] = cmp.mapping(function()
if luasnip.locally_jumpable(-1) then
luasnip.jump(-1)
end
end, { 'i', 's' }),
['<C-n>'] = cmp.config.disable,
['<C-p>'] = cmp.config.disable,
},
sources = {
{
name = 'lazydev',
-- set group index to 0 to skip loading LuaLS completions as lazydev recommends it
group_index = 0,
},
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'path' },
},
}
end,
},
}

58
lua/plugins/avante.lua Normal file
View File

@ -0,0 +1,58 @@
return {
{
'yetone/avante.nvim',
event = 'VeryLazy',
lazy = false,
version = false,
opts = {
-- Use ANTHROPIC_API_KEY=your-api-key
providers = {
claude = {
auto_suggestions_provider = 'claude', -- high-frequency provider (free recommended)
claude = {
endpoint = 'https://api.anthropic.com',
model = 'claude-3-5-sonnet-20241022',
temperature = 0,
max_tokens = 4096,
},
},
},
hints = { enabled = false },
},
build = 'make', -- BUILD_FROM_SOURCE=true
dependencies = {
'stevearc/dressing.nvim',
'nvim-lua/plenary.nvim',
'MunifTanjim/nui.nvim',
--- The below dependencies are optional,
'hrsh7th/nvim-cmp', -- autocompletion for avante commands and mentions
'echasnovski/mini.icons',
{
-- support for image pasting
'HakonHarnes/img-clip.nvim',
event = 'VeryLazy',
opts = {
default = {
embed_image_as_base64 = false,
prompt_for_file_name = false,
drag_and_drop = {
insert_mode = true,
},
use_absolute_path = false,
},
},
},
{
-- Make sure to set this up properly if you have lazy=true
'MeanderingProgrammer/render-markdown.nvim',
opts = {
file_types = { 'markdown', 'Avante' },
},
ft = { 'markdown', 'Avante' },
},
},
keys = {
{ '<leader>la', '<cmd>AvanteChat<cr>', desc = '[A]vante Chat' },
},
},
}

View File

@ -1 +1,13 @@
return { 'akinsho/bufferline.nvim', dependencies = { 'nvim-tree/nvim-web-devicons' }, version = '*', opts = { options = { always_show_bufferline = false, mode = 'tabs', }, }, }
return {
{
'akinsho/bufferline.nvim',
dependencies = { 'nvim-tree/nvim-web-devicons' },
version = '*',
opts = {
options = {
always_show_bufferline = false,
mode = 'tabs',
},
},
},
}

View File

@ -0,0 +1,20 @@
return {
'greggh/claude-code.nvim',
dependencies = {
'nvim-lua/plenary.nvim', -- Required for git operations
},
config = function()
require('claude-code').setup {
window = {
split_ratio = 0.5,
position = 'botright',
enter_insert = true,
hide_numbers = true,
hide_signcolumn = true,
},
}
end,
keys = {
{ '<leader>lc', '<cmd>ClaudeCode<cr>', desc = '[C]laude Code' },
},
}

View File

@ -2,9 +2,22 @@ return {
{
'folke/tokyonight.nvim',
priority = 1000, -- Make sure to load this before all the other start plugins.
init = function()
config = function()
---@diagnostic disable-next-line: missing-fields
require('tokyonight').setup {
styles = {
comments = { italic = false }, -- Disable italics in comments
},
}
vim.cmd.colorscheme 'tokyonight-night'
-- vim.cmd.hi 'Comment gui=none'
end,
opts = {
on_highlights = function(hl, c)
hl.TelescopeNormal = {
fg = c.fg_dark,
}
end,
},
},
}

View File

@ -12,28 +12,63 @@ return {
mode = '',
desc = '[Fo]rmat Buffer',
},
{
'<leader>tf',
function()
vim.g.format_on_save_enabled = not vim.g.format_on_save_enabled
vim.notify('Format on save: ' .. (vim.g.format_on_save_enabled and 'enabled' or 'disabled'))
end,
mode = '',
desc = '[F]ormat on save',
},
},
opts = {
notify_on_error = false,
format_on_save = function(bufnr)
local disable_filetypes = { c = true, cpp = true, sh = true }
return {
timeout_ms = 500,
lsp_fallback = not disable_filetypes[vim.bo[bufnr].filetype],
}
if not vim.g.format_on_save_enabled then
return false
end
local disable_filetypes = { c = true, cpp = true, sh = true, nix = true, md = true }
if disable_filetypes[vim.bo[bufnr].filetype] then
return nil
else
return {
async = false,
timeout_ms = 3000,
lsp_fallback = not disable_filetypes[vim.bo[bufnr].filetype],
}
end
end,
formatters_by_ft = {
c = { 'clangd-format', 'cpplint' },
cs = { 'ast-grep' }, -- c#
css = { 'prettier' },
cmake = { 'cmakelang' },
cpp = { 'clangd-format', 'cpplint' },
flow = { 'prettier' },
go = { 'ast-grep', 'golangci-lint' }, -- gofumpt requires go
html = { 'prettier' },
h = { 'clangd-format', 'cpplint' },
hpp = { 'clangd-format', 'cpplint' },
java = { 'ast-grep' },
javascript = { 'prettier' },
javascriptreact = { 'prettier' },
-- jinja = { 'djlint' },
json = { 'prettier' },
-- latex = { 'tex-fmt' },
lua = { 'stylua' },
python = {
'black',
'isort',
'ruff_format',
'pyright',
},
-- php = { 'php-cs-fixer' },
markdown = { 'prettier' },
md = { 'prettier' },
nix = { 'nixfmt' },
yaml = { 'yamlfmt' },
nix = { 'alejandra' },
python = { 'isort', 'ruff_format' },
-- r = { 'air' },
sh = { 'shfmt' },
-- tf = { 'terraform' },
typescript = { 'prettier' },
typescriptreact = { 'prettier' },
yaml = { 'prettier' },
},
},
},

View File

@ -10,5 +10,21 @@ return {
changedelete = { text = '~' },
},
},
keys = {
{
'<leader>tB',
function()
require('gitsigns').toggle_current_line_blame()
end,
desc = 'Git [B]lame',
},
{
'<leader>tD',
function()
require('gitsigns').toggle_deleted()
end,
desc = '[D]eleted git lines',
},
},
},
}

View File

@ -1,153 +0,0 @@
return {
{ -- properly configures LuaLS
'folke/lazydev.nvim',
ft = 'lua',
opts = {
library = {
-- Load luvit types when the `vim.uv` word is found
{ path = 'luvit-meta/library', words = { 'vim%.uv' } },
},
},
},
{ 'Bilal2453/luvit-meta', lazy = true },
{
-- Main LSP Configuration
'neovim/nvim-lspconfig',
dependencies = {
-- Automatically install LSPs and related tools to stdpath for Neovim
{ 'williamboman/mason.nvim', config = true }, -- NOTE: Must be loaded before dependants
'williamboman/mason-lspconfig.nvim',
'WhoIsSethDaniel/mason-tool-installer.nvim',
-- Useful status updates for LSP.
-- NOTE: `opts = {}` is the same as calling `require('fidget').setup({})`
{ 'j-hui/fidget.nvim', opts = {} },
-- Allows extra capabilities provided by nvim-cmp
'hrsh7th/cmp-nvim-lsp',
},
config = function()
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)
vim.keymap.set('n', keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
end
map('gd', require('telescope.builtin').lsp_definitions, '[G]oto [D]efinition')
map('gr', require('telescope.builtin').lsp_references, '[G]oto [R]eferences')
map('gI', require('telescope.builtin').lsp_implementations, '[G]oto [I]mplementation')
map('<leader>D', require('telescope.builtin').lsp_type_definitions, 'Type [D]efinition')
map('<leader>ds', require('telescope.builtin').lsp_document_symbols, '[D]ocument [S]ymbols')
map('<leader>ws', require('telescope.builtin').lsp_dynamic_workspace_symbols, '[W]orkspace [S]ymbols')
map('<leader>rn', vim.lsp.buf.rename, '[R]e[n]ame')
map('<leader>ca', vim.lsp.buf.code_action, '[C]ode [A]ction')
map('gD', vim.lsp.buf.declaration, '[G]oto [D]eclaration')
-- :help CursorHold
local client = vim.lsp.get_client_by_id(event.data.client_id)
if client and client.supports_method(vim.lsp.protocol.Methods.textDocument_documentHighlight) 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
if client and client.supports_method(vim.lsp.protocol.Methods.textDocument_inlayHint) 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,
})
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = vim.tbl_deep_extend('force', capabilities, require('cmp_nvim_lsp').default_capabilities())
local servers = {
clangd = {},
pyright = {},
black = {},
isort = {},
taplo = {},
-- rust_analyzer = {},
lua_ls = {
-- cmd = {...},
-- filetypes = { ...},
-- capabilities = {},
settings = {
Lua = {
completion = {
callSnippet = 'Replace',
},
},
},
},
}
-- Ensure the servers and tools above are installed
-- :Mason
require('mason').setup()
-- You can add other tools here that you want Mason to install
-- for you, so that they are available from within Neovim.
local ensure_installed = vim.tbl_keys(servers or {})
vim.list_extend(ensure_installed, {
'bashls', -- bash
'black', -- python format
'cmake', -- cmake
'clangd', -- cpp
'debugpy', -- debugger
'dockerls', -- docker
'golangci-lint', -- go
'isort', -- python sorting
'jedi-language-server', -- jedi completion
'jsonls', -- json
'lua_ls', -- lua
'marksman', -- markdown
'mypy', -- python checking
'nixpkgs-fmt', -- Nix
'prettier', -- md
'pyright', -- python lsp
'ruff', -- lint and format for python
'rust-analyzer', -- rust
'shfmt', -- shell
'stylua', -- Used to format Lua code
'taplo', -- LSP for toml files
'tree-sitter-cli', -- treesitter
'ts_ls', -- typescript
'yamllint', -- yaml
'yamlfmt', -- yaml
'yamlls', -- yaml
})
require('mason-tool-installer').setup { ensure_installed = ensure_installed }
require('mason-lspconfig').setup {
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,
},
}

94
lua/plugins/lualine.lua Normal file
View File

@ -0,0 +1,94 @@
return {
{
'nvim-lualine/lualine.nvim',
dependencies = { 'nvim-tree/nvim-web-devicons' },
config = function()
local mode_map = {
['NORMAL'] = 'N',
['O-PENDING'] = 'N?',
['INSERT'] = 'I',
['VISUAL'] = 'V',
['V-BLOCK'] = 'VB',
['V-LINE'] = 'VL',
['V-REPLACE'] = 'VR',
['REPLACE'] = 'R',
['COMMAND'] = '!',
['SHELL'] = 'SH',
['TERMINAL'] = 'T',
['EX'] = 'X',
['S-BLOCK'] = 'SB',
['S-LINE'] = 'SL',
['SELECT'] = 'S',
['CONFIRM'] = 'Y?',
['MORE'] = 'M',
}
require('lualine').setup {
options = {
icons_enabled = true,
theme = 'auto',
component_separators = { left = '', right = '' },
section_separators = { left = '', right = '' },
disabled_filetypes = {
statusline = {},
winbar = {},
},
ignore_focus = {},
always_divide_middle = true,
always_show_tabline = true,
globalstatus = false,
refresh = {
statusline = 100,
tabline = 100,
winbar = 100,
},
},
sections = {
lualine_a = { {
'mode',
fmt = function(s)
return mode_map[s] or s
end,
} },
lualine_b = { 'branch', 'diff', 'diagnostics' },
lualine_c = { 'filename' },
lualine_x = {
'encoding',
'fileformat',
'filetype',
{
'tabnine',
fmt = function(s)
if string.match(s, 'disabled') or s == '' then
return '-'
else
return ''
end
end,
},
{
function()
return vim.g.format_on_save_enabled and '󰸱' or ''
end,
color = { fg = '#98c379' },
},
},
lualine_y = { 'progress' },
lualine_z = { 'location' },
},
inactive_sections = {
lualine_a = {},
lualine_b = {},
lualine_c = { 'filename' },
lualine_x = { 'location' },
lualine_y = {},
lualine_z = {},
},
tabline = {},
winbar = {},
inactive_winbar = {},
extensions = {},
-- '' or ''
}
end,
},
}

128
lua/plugins/mason.lua Normal file
View File

@ -0,0 +1,128 @@
return {
-- Mason: LSP/DAP/Linter/Formatter installer
{
'mason-org/mason.nvim',
config = function()
require('mason').setup()
-- Add Mason bin directory to PATH
local mason_bin = vim.fn.stdpath 'data' .. '/mason/bin'
local current_path = vim.env.PATH or ''
if not string.find(current_path, mason_bin, 1, true) then
vim.env.PATH = mason_bin .. ':' .. current_path
end
-- Auto-cleanup unused packages (add when deprecating packages)
--
vim.defer_fn(function()
local registry = require 'mason-registry'
local unused_packages = {
'black',
'mypy',
'pyright',
'nixpkgs-fmt',
'python-lsp-server',
'pyflakes',
'pylint',
'pep8',
}
for _, package_name in ipairs(unused_packages) do
if registry.is_installed(package_name) then
local package = registry.get_package(package_name)
package:uninstall():once('closed', function()
vim.notify('Removed unused package: ' .. package_name, vim.log.levels.INFO)
end)
end
end
end, 1000)
end,
},
-- Mason tool installer for formatters/linters
-- Note: mason-lspconfig.nvim is not used for LSP beyond installs, we use vim.lsp fot that
{
'WhoIsSethDaniel/mason-tool-installer.nvim',
dependencies = { 'williamboman/mason.nvim', 'williamboman/mason-lspconfig.nvim' },
config = function()
-- Function to translate LSP server names to Mason package names using mason-lspconfig
local function translate_lsp_names(lsp_servers)
local mason_packages = {}
local ok, mason_lspconfig = pcall(require, 'mason-lspconfig')
if ok then
for _, server in ipairs(lsp_servers) do
local success, package_name = pcall(mason_lspconfig.get_mason_package, server)
if success and package_name then
table.insert(mason_packages, package_name.name)
else
-- Fallback to original name if no mapping found
table.insert(mason_packages, server)
end
end
else
-- If mason-lspconfig not available, use original names
mason_packages = lsp_servers
end
return mason_packages
end
-- LSP servers to install (using mason-lspconfig names)
local lsp_servers = {
'clangd',
'basedpyright',
'bashls', -- bash-language-server
'dockerls', -- dockerfile-language-server
-- 'gopls', -- go
'jedi_language_server',
'lua_ls', -- lua-language-server
'marksman',
'rust_analyzer',
'taplo',
'ts_ls', -- typescript-language-server
'yamlls', -- yaml-language-server
}
-- Other tools
local other_tools = {
-- Formatters
'alejandra', -- nix
'ast-grep',
'clang-format',
'cmakelang',
'isort', -- python
'prettier',
'ruff', -- python
'shfmt',
'stylua',
-- Linters
'ast-grep',
'cmakelint',
'cpplint',
'golangci-lint',
'ruff', -- python
'yamllint',
'golangci-lint',
-- Debuggers
'debugpy',
-- Additional tools
'tree-sitter-cli',
}
-- Combine translated LSP servers with other tools
local all_tools = {}
vim.list_extend(all_tools, translate_lsp_names(lsp_servers))
vim.list_extend(all_tools, other_tools)
require('mason-tool-installer').setup {
ensure_installed = all_tools,
auto_update = false,
run_on_start = true,
}
end,
},
}

View File

@ -16,13 +16,6 @@ return {
-- - sd' - [S]urround [D]elete [']quotes
-- - sr)' - [S]urround [R]eplace [)] [']
require('mini.surround').setup()
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
end,
},
}

View File

@ -1,6 +1,11 @@
return {
{ -- Emacs-style keybindings in insert mode
{
'millerjason/neovimacs.nvim',
opts = {},
opts = {
VM_Enabled = vim.g.neovimacs_bindings,
VM_StartInsert = vim.g.neovimacs_insert,
VM_UnixConsoleMetaSendsEsc = false,
TabIndentStyle = 'none', -- 'emacs', 'never', 'whitespace', 'startofline'
},
},
}

View File

@ -13,12 +13,17 @@ return {
log_file_path = nil,
ignore_certificate_errors = false,
}
require('tabnine.status').disable_tabnine()
if vim.g.tabnine_enable then
require('tabnine.status').enable_tabnine()
else
require('tabnine.status').disable_tabnine()
end
end,
lazy = false,
keys = {
{ '<leader>lc', '<cmd>TabnineChat<cr>', desc = '[L]LM [C]hat' },
{ '<leader>lt', '<cmd>TabnineToggle<cr>', desc = '[L]LM [T]oggle' },
{ '<leader>ls', '<cmd>TabnineStatus<cr>', desc = '[L]LM [S]tatus' },
{ '<leader>t9', '<cmd>TabnineToggle<cr>', desc = '[T]oggle' },
{ '<leader>l9e', '<cmd>TabnineEnable<cr>', desc = 'T9 [E]nable' },
{ '<leader>l9c', '<cmd>TabnineChat<cr>', desc = 'T9 [C]hat' },
{ '<leader>l9s', '<cmd>TabnineStatus<cr>', desc = 'T9 [S]tatus' },
},
}

View File

@ -49,7 +49,6 @@ return {
vim.keymap.set('n', '<leader>fg', builtin.live_grep, { desc = 'Find [G]rep' })
vim.keymap.set('n', '<leader>fb', builtin.buffers, { desc = 'Find [B]uffers' })
vim.keymap.set('n', '<leader>fh', builtin.help_tags, { desc = 'Find [H]elp tags' })
vim.keymap.set('n', '<leader><leader>', builtin.buffers, { desc = '[ ] Find existing buffers' })
-- Slightly advanced example of overriding default behavior and theme
vim.keymap.set('n', '<leader>/', function()

View File

@ -0,0 +1,33 @@
return {
{
'rachartier/tiny-inline-diagnostic.nvim',
event = 'VeryLazy',
priority = 1000, -- needs to be loaded in first
config = function(_, opts)
vim.opt.updatetime = 100
vim.api.nvim_set_hl(0, 'DiagnosticError', { fg = '#f76464' })
vim.api.nvim_set_hl(0, 'DiagnosticWarn', { fg = '#f7bf64' })
vim.api.nvim_set_hl(0, 'DiagnosticInfo', { fg = '#64bcf7' })
vim.api.nvim_set_hl(0, 'DiagnosticHint', { fg = '#64f79d' })
require('tiny-inline-diagnostic').setup(opts)
end,
opts = {
enable_on_insert = true,
multiple_diag_under_cursor = true,
show_all_diags_on_cursorline = true,
multilines = {
enabled = false,
always_show = true,
},
signs = {
left = '',
right = '',
diag = '',
arrow = '',
up_arrow = '',
vertical = '',
vertical_end = '',
},
},
},
}

View File

@ -1,5 +1,5 @@
return {
'linux-cultist/venv-selector.nvim',
'linux-cultist/venv-selector.nvim',
branch = 'regexp',
dependencies = {
'neovim/nvim-lspconfig',

View File

@ -2,18 +2,26 @@ return {
{ -- Shows keybindings as you go
'folke/which-key.nvim',
event = 'VimEnter',
config = function()
require('which-key').setup()
-- Document existing key chains
require('which-key').add {
opts = {
delay = 0,
icons = {
mappings = vim.g.have_nerd_font,
},
spec = {
{ '<leader>a', group = '[A]vante' },
{ '<leader>c', group = '[C]ode' },
{ '<leader>d', group = '[D]ocument' },
{ '<leader>r', group = '[R]ename' },
{ '<leader>s', group = '[S]earch' },
{ '<leader>w', group = '[W]orkspace' },
{ '<leader>w', group = '[W]indow' },
{ '<leader>t', group = '[T]oggle' },
{ '<leader>l', group = '[L]LM Assist' },
{ '<leader>l9', group = 'Tab[9]' },
{ '<leader>h', group = 'Git [H]unk', mode = { 'n', 'v' } },
}
end,
{ '<leader>p', group = '[P] Explore', mode = { 'n' } },
{ '<leader>f', group = '[F]ind/Grep' },
{ '<leader>i', group = '[I]ndent' },
},
},
},
}

111
lua/utils/tools.lua Normal file
View File

@ -0,0 +1,111 @@
local M = {}
-- Helper function to find the executable in path, prioritizing non-mason paths
function M.find_executable(executable)
-- First try with PATH environment variable
local paths = vim.split(vim.env.PATH, ':', { plain = true })
for _, dir in ipairs(paths) do
if dir ~= '' and not string.find(dir, 'mason') then
local path = dir .. '/' .. executable
if vim.fn.filereadable(path) == 1 and vim.fn.executable(path) == 1 then
return path
end
end
end
-- Fallback to mason path if needed
local path = vim.fn.exepath(executable)
return path ~= '' and path or nil
end
-- Function to detect python venv path
function M.get_python_venv_path()
-- Get the directory of the current file
local current_file = vim.api.nvim_buf_get_name(0)
local current_dir = vim.fn.fnamemodify(current_file, ':h')
-- Function to find git root
local function find_git_root(path)
local git_dir = vim.fn.finddir('.git', path .. ';')
if git_dir ~= '' then
return vim.fn.fnamemodify(git_dir, ':h')
end
return nil
end
-- Search paths in order: current directory, git root, user path
local search_paths = {
current_dir,
find_git_root(current_dir),
vim.fn.expand '~',
}
-- Check each search path for .venv
for _, path in ipairs(search_paths) do
if path then
local venv_path = path .. '/.venv'
if vim.fn.isdirectory(venv_path) == 1 then
return venv_path .. '/bin'
end
end
end
-- Check for VIRTUAL_ENV environment variable (set by direnv or manually)
local virtual_env = vim.env.VIRTUAL_ENV
if virtual_env and virtual_env ~= '' then
return virtual_env .. '/bin'
end
-- Default to system path
return nil
end
-- Helper function to find executable in both venv and system PATH
function M.find_tool(tool_name)
-- First check virtual env path
local venv_path = M.get_python_venv_path()
if venv_path then
local tool_path = venv_path .. '/' .. tool_name
if vim.fn.filereadable(tool_path) == 1 then
return tool_path
end
end
-- Fall back to executable in PATH
return M.find_executable(tool_name)
end
-- Get LSP capabilities with cmp support
function M.get_lsp_capabilities()
local capabilities = vim.lsp.protocol.make_client_capabilities()
local ok, cmp_nvim_lsp = pcall(require, 'cmp_nvim_lsp')
if ok then
capabilities = vim.tbl_deep_extend('force', capabilities, cmp_nvim_lsp.default_capabilities())
end
return capabilities
end
-- Get LSP root directory for current buffer
function M.get_lsp_root()
local buf = vim.api.nvim_get_current_buf()
local clients = vim.lsp.get_clients { bufnr = buf }
for _, client in ipairs(clients) do
if client.config.root_dir then
return client.config.root_dir
end
end
-- Fallback to git root or current working directory
local current_file = vim.api.nvim_buf_get_name(buf)
local current_dir = vim.fn.fnamemodify(current_file, ':h')
local git_root = vim.fs.dirname(vim.fs.find({ '.git' }, { path = current_dir, upward = true })[1])
if git_root then
return git_root
end
return vim.fn.getcwd()
end
return M

42
lua/utils/windows.lua Normal file
View File

@ -0,0 +1,42 @@
-- Tab management keys
-- F1-Prev, F2-Next, F3-New, F4-Close
--
local function safe_tabclose()
local bufnr = vim.api.nvim_get_current_buf()
local buf_windows = vim.call('win_findbuf', bufnr)
local modified = vim.bo[bufnr].modified
if vim.fn.tabpagenr '$' == 1 then
-- last tab, no-op
return
elseif modified and #buf_windows == 1 then
vim.ui.input({
prompt = 'Buffer modified, are you sure? ',
}, function(input)
if input == 'y' then
vim.cmd 'tabclose'
end
end)
else
vim.cmd 'tabclose'
end
end
vim.keymap.set('t', '<F1>', vim.cmd.tabp, { noremap = true, silent = true })
vim.keymap.set('t', '<F2>', vim.cmd.tabn, { noremap = true, silent = true })
vim.keymap.set('t', '<F3>', '<C-\\><C-n>:tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('t', '<F4>', safe_tabclose, { noremap = true, silent = true })
vim.keymap.set('t', '<F5>', '<C-\\><C-n><Esc>:tab new<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '<F1>', vim.cmd.tabp, { noremap = true, silent = true })
vim.keymap.set('n', '<F2>', vim.cmd.tabn, { noremap = true, silent = true })
vim.keymap.set('n', '<F3>', ':tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '<F4>', safe_tabclose, { noremap = true, silent = true })
vim.keymap.set('n', '<F5>', ':tab term<CR>', { noremap = true, silent = true })
vim.keymap.set('i', '<F1>', vim.cmd.tabp, { noremap = true, silent = true })
vim.keymap.set('i', '<F2>', vim.cmd.tabn, { noremap = true, silent = true })
vim.keymap.set('i', '<F3>', '<Esc>:tabnew<CR>', { noremap = true, silent = true })
vim.keymap.set('i', '<F4>', safe_tabclose, { noremap = true, silent = true })
vim.keymap.set('i', '<F5>', '<Esc>:tab term<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '<leader>wp', vim.cmd.tabn, { desc = '[p]revious' })
vim.keymap.set('n', '<leader>wn', vim.cmd.tabp, { desc = '[n]ext' })
vim.keymap.set('n', '<leader>wo', vim.cmd.tabnew, { desc = '[o]pen' })
vim.keymap.set('n', '<leader>wc', safe_tabclose, { desc = '[c]lose' })