neovim conf based on kickstart.nvim
1-- Set <space> as the leader key
2-- See `:help mapleader`
3-- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used)
4vim.g.mapleader = ' '
5vim.g.maplocalleader = ' '
6
7-- Set to true if you have a Nerd Font installed and selected in the terminal
8vim.g.have_nerd_font = true
9
10-- [[ Setting options ]]
11-- See `:help vim.o`
12-- NOTE: You can change these options as you wish!
13-- For more options, you can see `:help option-list`
14
15-- Make line numbers default
16vim.o.number = true
17-- You can also add relative line numbers, to help with jumping.
18-- Experiment for yourself to see if you like it!
19vim.o.relativenumber = true
20
21-- Enable mouse mode, can be useful for resizing splits for example!
22vim.o.mouse = 'a'
23
24-- Don't show the mode, since it's already in the status line
25vim.o.showmode = false
26
27-- Sync clipboard between OS and Neovim.
28-- Schedule the setting after `UiEnter` because it can increase startup-time.
29-- Remove this option if you want your OS clipboard to remain independent.
30-- See `:help 'clipboard'`
31vim.schedule(function() vim.o.clipboard = 'unnamedplus' end)
32
33-- Enable break indent
34vim.o.breakindent = true
35
36-- Enable undo/redo changes even after closing and reopening a file
37vim.o.undofile = true
38
39-- Case-insensitive searching UNLESS \C or one or more capital letters in the search term
40vim.o.ignorecase = true
41vim.o.smartcase = true
42
43-- Keep signcolumn on by default
44vim.o.signcolumn = 'yes'
45
46-- Decrease update time
47vim.o.updatetime = 250
48
49-- Decrease mapped sequence wait time
50vim.o.timeoutlen = 300
51
52-- Configure how new splits should be opened
53vim.o.splitright = true
54vim.o.splitbelow = true
55
56-- Sets how neovim will display certain whitespace characters in the editor.
57-- See `:help 'list'`
58-- and `:help 'listchars'`
59--
60-- Notice listchars is set using `vim.opt` instead of `vim.o`.
61-- It is very similar to `vim.o` but offers an interface for conveniently interacting with tables.
62-- See `:help lua-options`
63-- and `:help lua-guide-options`
64vim.o.list = true
65vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '␣' }
66
67-- Preview substitutions live, as you type!
68vim.o.inccommand = 'split'
69
70-- Show which line your cursor is on
71vim.o.cursorline = true
72
73-- Minimal number of screen lines to keep above and below the cursor.
74vim.o.scrolloff = 10
75
76-- if performing an operation that would fail due to unsaved changes in the buffer (like `:q`),
77-- instead raise a dialog asking if you wish to save the current file(s)
78-- See `:help 'confirm'`
79vim.o.confirm = true
80
81-- [[ Basic Keymaps ]]
82-- See `:help vim.keymap.set()`
83
84-- Clear highlights on search when pressing <Esc> in normal mode
85-- See `:help hlsearch`
86vim.keymap.set('n', '<Esc>', '<cmd>nohlsearch<CR>')
87
88-- Diagnostic Config & Keymaps
89-- -- See :help vim.diagnostic.Opts
90-- vim.diagnostic.config {
91-- update_in_insert = false,
92-- severity_sort = true,
93-- float = { border = 'rounded', max_width = 120, max_height = 20, focusable = true },
94-- underline = { severity = { min = vim.diagnostic.severity.WARN } },
95--
96-- -- Can switch between these as you prefer
97-- virtual_text = true, -- Text shows up at the end of the line
98-- virtual_lines = { only_current_line = true }, -- Text shows up underneath the line, with virtual lines
99--
100-- -- Auto open the float, so you can easily read the errors when jumping with `[d` and `]d`
101-- jump = { float = true },
102-- }
103--
104-- vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' })
105
106-- Exit terminal mode in the builtin terminal with a shortcut that is a bit easier
107-- for people to discover. Otherwise, you normally need to press <C-\><C-n>, which
108-- is not what someone will guess without a bit more experience.
109--
110-- NOTE: This won't work in all terminal emulators/tmux/etc. Try your own mapping
111-- or just use <C-\><C-n> to exit terminal mode
112vim.keymap.set('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Exit terminal mode' })
113
114-- TIP: Disable arrow keys in normal mode
115-- vim.keymap.set('n', '<left>', '<cmd>echo "Use h to move!!"<CR>')
116-- vim.keymap.set('n', '<right>', '<cmd>echo "Use l to move!!"<CR>')
117-- vim.keymap.set('n', '<up>', '<cmd>echo "Use k to move!!"<CR>')
118-- vim.keymap.set('n', '<down>', '<cmd>echo "Use j to move!!"<CR>')
119
120-- Keybinds to make split navigation easier.
121-- Use CTRL+<hjkl> to switch between windows
122--
123-- See `:help wincmd` for a list of all window commands
124vim.keymap.set('n', '<C-h>', '<C-w><C-h>', { desc = 'Move focus to the left window' })
125vim.keymap.set('n', '<C-l>', '<C-w><C-l>', { desc = 'Move focus to the right window' })
126vim.keymap.set('n', '<C-j>', '<C-w><C-j>', { desc = 'Move focus to the lower window' })
127vim.keymap.set('n', '<C-k>', '<C-w><C-k>', { desc = 'Move focus to the upper window' })
128
129-- NOTE: Some terminals have colliding keymaps or are not able to send distinct keycodes
130-- vim.keymap.set("n", "<C-S-h>", "<C-w>H", { desc = "Move window to the left" })
131-- vim.keymap.set("n", "<C-S-l>", "<C-w>L", { desc = "Move window to the right" })
132-- vim.keymap.set("n", "<C-S-j>", "<C-w>J", { desc = "Move window to the lower" })
133-- vim.keymap.set("n", "<C-S-k>", "<C-w>K", { desc = "Move window to the upper" })
134
135-- [[ Quality of life ]]
136vim.o.winborder = 'rounded'
137
138-- [[ Basic Autocommands ]]
139-- See `:help lua-guide-autocommands`
140
141-- Highlight when yanking (copying) text
142-- Try it with `yap` in normal mode
143-- See `:help vim.hl.on_yank()`
144vim.api.nvim_create_autocmd('TextYankPost', {
145 desc = 'Highlight when yanking (copying) text',
146 group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }),
147 callback = function() vim.hl.on_yank() end,
148})
149
150-- [[ Install `lazy.nvim` plugin manager ]]
151-- See `:help lazy.nvim.txt` or https://github.com/folke/lazy.nvim for more info
152local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
153if not (vim.uv or vim.loop).fs_stat(lazypath) then
154 local lazyrepo = 'https://github.com/folke/lazy.nvim.git'
155 local out = vim.fn.system { 'git', 'clone', '--filter=blob:none', '--branch=stable', lazyrepo, lazypath }
156 if vim.v.shell_error ~= 0 then error('Error cloning lazy.nvim:\n' .. out) end
157end
158
159---@type vim.Option
160local rtp = vim.opt.rtp
161rtp:prepend(lazypath)
162
163-- [[ Configure and install plugins ]]
164--
165-- To check the current status of your plugins, run
166-- :Lazy
167--
168-- You can press `?` in this menu for help. Use `:q` to close the window
169--
170-- To update plugins you can run
171-- :Lazy update
172--
173-- NOTE: Here is where you install your plugins.
174require('lazy').setup({
175 -- NOTE: Plugins can be added via a link or github org/name. To run setup automatically, use `opts = {}`
176 { 'NMAC427/guess-indent.nvim', opts = {} },
177
178 -- Alternatively, use `config = function() ... end` for full control over the configuration.
179 -- If you prefer to call `setup` explicitly, use:
180 -- {
181 -- 'lewis6991/gitsigns.nvim',
182 -- config = function()
183 -- require('gitsigns').setup({
184 -- -- Your gitsigns configuration here
185 -- })
186 -- end,
187 -- }
188 --
189 -- Here is a more advanced example where we pass configuration
190 -- options to `gitsigns.nvim`.
191 --
192 -- See `:help gitsigns` to understand what the configuration keys do
193 { -- Adds git related signs to the gutter, as well as utilities for managing changes
194 'lewis6991/gitsigns.nvim',
195 ---@module 'gitsigns'
196 ---@type Gitsigns.Config
197 ---@diagnostic disable-next-line: missing-fields
198 opts = {
199 signs = {
200 add = { text = '+' }, ---@diagnostic disable-line: missing-fields
201 change = { text = '~' }, ---@diagnostic disable-line: missing-fields
202 delete = { text = '_' }, ---@diagnostic disable-line: missing-fields
203 topdelete = { text = '‾' }, ---@diagnostic disable-line: missing-fields
204 changedelete = { text = '~' }, ---@diagnostic disable-line: missing-fields
205 },
206 },
207 },
208
209 -- NOTE: Plugins can also be configured to run Lua code when they are loaded.
210 --
211 -- This is often very useful to both group configuration, as well as handle
212 -- lazy loading plugins that don't need to be loaded immediately at startup.
213 --
214 -- For example, in the following configuration, we use:
215 -- event = 'VimEnter'
216 --
217 -- which loads which-key before all the UI elements are loaded. Events can be
218 -- normal autocommands events (`:help autocmd-events`).
219 --
220 -- Then, because we use the `opts` key (recommended), the configuration runs
221 -- after the plugin has been loaded as `require(MODULE).setup(opts)`.
222
223 { -- Useful plugin to show you pending keybinds.
224 'folke/which-key.nvim',
225 event = 'VimEnter',
226 ---@module 'which-key'
227 ---@type wk.Opts
228 ---@diagnostic disable-next-line: missing-fields
229 opts = {
230 -- delay between pressing a key and opening which-key (milliseconds)
231 delay = 0,
232 icons = { mappings = vim.g.have_nerd_font },
233
234 preset = 'helix',
235
236 -- Document existing key chains
237 spec = {
238 { '<leader>s', group = '[S]earch', mode = { 'n', 'v' } },
239 { '<leader>t', group = '[T]oggle' },
240 { '<leader>h', group = 'Git [H]unk', mode = { 'n', 'v' } }, -- Enable gitsigns recommended keymaps first
241 { 'gr', group = 'LSP Actions', mode = { 'n' } },
242 },
243 },
244 },
245
246 -- NOTE: Plugins can specify dependencies.
247 --
248 -- The dependencies are proper plugin specifications as well - anything
249 -- you do for a plugin at the top level, you can do for a dependency.
250 --
251 -- Use the `dependencies` key to specify the dependencies of a particular plugin
252
253 {
254 'folke/snacks.nvim',
255 priority = 1000,
256 lazy = false,
257 ---@type snacks.Config
258 opts = {
259 bufdelete = { enabled = true },
260 debug = { enabled = true },
261 zen = { enabled = true },
262 bigfile = { enabled = true },
263 dashboard = { enabled = true },
264 explorer = { enabled = true },
265 image = { enabled = true },
266 indent = { enabled = true },
267 input = { enabled = true },
268 keyboard = { enabled = true },
269 lazygit = { enabled = true },
270 notifier = { enabled = true },
271 picker = { enabled = true },
272 quickfile = { enabled = true },
273 scope = { enabled = true },
274 scroll = { enabled = true },
275 statuscolumn = { enabled = true },
276 terminal = { enabled = true },
277 words = { enabled = true },
278 },
279 keys = {
280 { '\\', function() Snacks.explorer() end, desc = 'snacks explorer reveal', silent = true },
281 { '<leader><space>', function() Snacks.picker.smart() end, desc = 'Smart Find Files' },
282 { '<leader>,', function() Snacks.picker.buffers() end, desc = 'Buffers' },
283 { '<leader>/', function() Snacks.picker.grep() end, desc = 'Grep' },
284 { '<leader>:', function() Snacks.picker.command_history() end, desc = 'Command History' },
285 { '<leader>n', function() Snacks.picker.notifications() end, desc = 'Notification History' },
286 { '<leader>e', function() Snacks.explorer() end, desc = 'File Explorer' },
287 -- find
288 { '<leader>fb', function() Snacks.picker.buffers() end, desc = 'Buffers' },
289 { '<leader>fc', function() Snacks.picker.files { cwd = vim.fn.stdpath 'config' } end, desc = 'Find Config File' },
290 { '<leader>ff', function() Snacks.picker.files() end, desc = 'Find Files' },
291 { '<leader>fg', function() Snacks.picker.git_files() end, desc = 'Find Git Files' },
292 { '<leader>fp', function() Snacks.picker.projects() end, desc = 'Projects' },
293 { '<leader>fr', function() Snacks.picker.recent() end, desc = 'Recent' },
294 -- git
295 { '<leader>gb', function() Snacks.picker.git_branches() end, desc = 'Git Branches' },
296 { '<leader>gl', function() Snacks.picker.git_log() end, desc = 'Git Log' },
297 { '<leader>gL', function() Snacks.picker.git_log_line() end, desc = 'Git Log Line' },
298 { '<leader>gs', function() Snacks.picker.git_status() end, desc = 'Git Status' },
299 { '<leader>gS', function() Snacks.picker.git_stash() end, desc = 'Git Stash' },
300 { '<leader>gd', function() Snacks.picker.git_diff() end, desc = 'Git Diff (Hunks)' },
301 { '<leader>gf', function() Snacks.picker.git_log_file() end, desc = 'Git Log File' },
302 -- gh
303 { '<leader>gi', function() Snacks.picker.gh_issue() end, desc = 'GitHub Issues (open)' },
304 { '<leader>gI', function() Snacks.picker.gh_issue { state = 'all' } end, desc = 'GitHub Issues (all)' },
305 { '<leader>gp', function() Snacks.picker.gh_pr() end, desc = 'GitHub Pull Requests (open)' },
306 { '<leader>gP', function() Snacks.picker.gh_pr { state = 'all' } end, desc = 'GitHub Pull Requests (all)' },
307 -- Grep
308 { '<leader>sb', function() Snacks.picker.lines() end, desc = 'Buffer Lines' },
309 { '<leader>sB', function() Snacks.picker.grep_buffers() end, desc = 'Grep Open Buffers' },
310 { '<leader>sg', function() Snacks.picker.grep() end, desc = 'Grep' },
311 { '<leader>sw', function() Snacks.picker.grep_word() end, desc = 'Visual selection or word', mode = { 'n', 'x' } },
312 -- search
313 { '<leader>s"', function() Snacks.picker.registers() end, desc = 'Registers' },
314 { '<leader>s/', function() Snacks.picker.search_history() end, desc = 'Search History' },
315 { '<leader>sa', function() Snacks.picker.autocmds() end, desc = 'Autocmds' },
316 { '<leader>sb', function() Snacks.picker.lines() end, desc = 'Buffer Lines' },
317 { '<leader>sc', function() Snacks.picker.command_history() end, desc = 'Command History' },
318 { '<leader>sC', function() Snacks.picker.commands() end, desc = 'Commands' },
319 { '<leader>sd', function() Snacks.picker.diagnostics() end, desc = 'Diagnostics' },
320 { '<leader>sD', function() Snacks.picker.diagnostics_buffer() end, desc = 'Buffer Diagnostics' },
321 { '<leader>sh', function() Snacks.picker.help() end, desc = 'Help Pages' },
322 { '<leader>sH', function() Snacks.picker.highlights() end, desc = 'Highlights' },
323 { '<leader>si', function() Snacks.picker.icons() end, desc = 'Icons' },
324 { '<leader>sj', function() Snacks.picker.jumps() end, desc = 'Jumps' },
325 { '<leader>sk', function() Snacks.picker.keymaps() end, desc = 'Keymaps' },
326 { '<leader>sl', function() Snacks.picker.loclist() end, desc = 'Location List' },
327 { '<leader>sm', function() Snacks.picker.marks() end, desc = 'Marks' },
328 { '<leader>sM', function() Snacks.picker.man() end, desc = 'Man Pages' },
329 { '<leader>sp', function() Snacks.picker.lazy() end, desc = 'Search for Plugin Spec' },
330 { '<leader>sq', function() Snacks.picker.qflist() end, desc = 'Quickfix List' },
331 { '<leader>sR', function() Snacks.picker.resume() end, desc = 'Resume' },
332 { '<leader>su', function() Snacks.picker.undo() end, desc = 'Undo History' },
333 { '<leader>uC', function() Snacks.picker.colorschemes() end, desc = 'Colorschemes' },
334 -- LSP
335 { 'gd', function() Snacks.picker.lsp_definitions() end, desc = 'Goto Definition' },
336 { 'gD', function() Snacks.picker.lsp_declarations() end, desc = 'Goto Declaration' },
337 { 'gr', function() Snacks.picker.lsp_references() end, nowait = true, desc = 'References' },
338 { 'gI', function() Snacks.picker.lsp_implementations() end, desc = 'Goto Implementation' },
339 { 'gy', function() Snacks.picker.lsp_type_definitions() end, desc = 'Goto T[y]pe Definition' },
340 { 'gai', function() Snacks.picker.lsp_incoming_calls() end, desc = 'C[a]lls Incoming' },
341 { 'gao', function() Snacks.picker.lsp_outgoing_calls() end, desc = 'C[a]lls Outgoing' },
342 { '<leader>ss', function() Snacks.picker.lsp_symbols() end, desc = 'LSP Symbols' },
343 { '<leader>sS', function() Snacks.picker.lsp_workspace_symbols() end, desc = 'LSP Workspace Symbols' },
344 -- Other
345 { '<leader>z', function() Snacks.zen() end, desc = 'Toggle Zen Mode' },
346 { '<leader>Z', function() Snacks.zen.zoom() end, desc = 'Toggle Zoom' },
347 { '<leader>n', function() Snacks.notifier.show_history() end, desc = 'Notification History' },
348 { '<leader>bd', function() Snacks.bufdelete() end, desc = 'Delete Buffer' },
349 { '<leader>cR', function() Snacks.rename.rename_file() end, desc = 'Rename File' },
350 { '<leader>gB', function() Snacks.gitbrowse() end, desc = 'Git Browse', mode = { 'n', 'v' } },
351 { '<leader>gg', function() Snacks.lazygit() end, desc = 'Lazygit' },
352 { '<leader>un', function() Snacks.notifier.hide() end, desc = 'Dismiss All Notifications' },
353 { '<c-/>', function() Snacks.terminal() end, desc = 'Toggle Terminal' },
354 { '<c-_>', function() Snacks.terminal() end, desc = 'which_key_ignore' },
355 { ']]', function() Snacks.words.jump(vim.v.count1) end, desc = 'Next Reference', mode = { 'n', 't' } },
356 { '[[', function() Snacks.words.jump(-vim.v.count1) end, desc = 'Prev Reference', mode = { 'n', 't' } },
357 {
358 '<leader>N',
359 desc = 'Neovim News',
360 function()
361 Snacks.win {
362 file = vim.api.nvim_get_runtime_file('doc/news.txt', false)[1],
363 width = 0.6,
364 height = 0.6,
365 wo = {
366 spell = false,
367 wrap = false,
368 signcolumn = 'yes',
369 statuscolumn = ' ',
370 conceallevel = 3,
371 },
372 }
373 end,
374 },
375 },
376 },
377
378 -- LSP Plugins
379 {
380 -- Main LSP Configuration
381 'neovim/nvim-lspconfig',
382 dependencies = {
383 -- Automatically install LSPs and related tools to stdpath for Neovim
384 -- Mason must be loaded before its dependents so we need to set it up here.
385 -- NOTE: `opts = {}` is the same as calling `require('mason').setup({})`
386 {
387 'mason-org/mason.nvim',
388 ---@module 'mason.settings'
389 ---@type MasonSettings
390 ---@diagnostic disable-next-line: missing-fields
391 opts = {},
392 },
393 'smjonas/inc-rename.nvim',
394
395 -- Maps LSP server names between nvim-lspconfig and Mason package names.
396 'mason-org/mason-lspconfig.nvim',
397 'WhoIsSethDaniel/mason-tool-installer.nvim',
398
399 -- Useful status updates for LSP.
400 { 'j-hui/fidget.nvim', opts = {} },
401 },
402 config = function()
403 -- Brief aside: **What is LSP?**
404 --
405 -- LSP is an initialism you've probably heard, but might not understand what it is.
406 --
407 -- LSP stands for Language Server Protocol. It's a protocol that helps editors
408 -- and language tooling communicate in a standardized fashion.
409 --
410 -- In general, you have a "server" which is some tool built to understand a particular
411 -- language (such as `gopls`, `lua_ls`, `rust_analyzer`, etc.). These Language Servers
412 -- (sometimes called LSP servers, but that's kind of like ATM Machine) are standalone
413 -- processes that communicate with some "client" - in this case, Neovim!
414 --
415 -- LSP provides Neovim with features like:
416 -- - Go to definition
417 -- - Find references
418 -- - Autocompletion
419 -- - Symbol Search
420 -- - and more!
421 --
422 -- Thus, Language Servers are external tools that must be installed separately from
423 -- Neovim. This is where `mason` and related plugins come into play.
424 --
425 -- If you're wondering about lsp vs treesitter, you can check out the wonderfully
426 -- and elegantly composed help section, `:help lsp-vs-treesitter`
427
428 -- This function gets run when an LSP attaches to a particular buffer.
429 -- That is to say, every time a new file is opened that is associated with
430 -- an lsp (for example, opening `main.rs` is associated with `rust_analyzer`) this
431 -- function will be executed to configure the current buffer
432 vim.api.nvim_create_autocmd('LspAttach', {
433 group = vim.api.nvim_create_augroup('kickstart-lsp-attach', { clear = true }),
434 callback = function(event)
435 -- NOTE: Remember that Lua is a real programming language, and as such it is possible
436 -- to define small helper and utility functions so you don't have to repeat yourself.
437 --
438 -- In this case, we create a function that lets us more easily define mappings specific
439 -- for LSP related items. It sets the mode, buffer and description for us each time.
440 local map = function(keys, func, desc, mode)
441 mode = mode or 'n'
442 vim.keymap.set(mode, keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
443 end
444
445 -- Rename the variable under your cursor.
446 -- Most Language Servers support renaming across files, etc.
447 map('grn', vim.lsp.buf.rename, '[R]e[n]ame')
448
449 -- Execute a code action, usually your cursor needs to be on top of an error
450 -- or a suggestion from your LSP for this to activate.
451 map('gra', vim.lsp.buf.code_action, '[G]oto Code [A]ction', { 'n', 'x' })
452
453 -- WARN: This is not Goto Definition, this is Goto Declaration.
454 -- For example, in C this would take you to the header.
455 map('grD', vim.lsp.buf.declaration, '[G]oto [D]eclaration')
456
457 -- The following two autocommands are used to highlight references of the
458 -- word under your cursor when your cursor rests there for a little while.
459 -- See `:help CursorHold` for information about when this is executed
460 --
461 -- When you move your cursor, the highlights will be cleared (the second autocommand).
462 local client = vim.lsp.get_client_by_id(event.data.client_id)
463 if client and client:supports_method('textDocument/documentHighlight', event.buf) then
464 local highlight_augroup = vim.api.nvim_create_augroup('kickstart-lsp-highlight', { clear = false })
465 vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
466 buffer = event.buf,
467 group = highlight_augroup,
468 callback = vim.lsp.buf.document_highlight,
469 })
470
471 vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
472 buffer = event.buf,
473 group = highlight_augroup,
474 callback = vim.lsp.buf.clear_references,
475 })
476
477 vim.api.nvim_create_autocmd('LspDetach', {
478 group = vim.api.nvim_create_augroup('kickstart-lsp-detach', { clear = true }),
479 callback = function(event2)
480 vim.lsp.buf.clear_references()
481 vim.api.nvim_clear_autocmds { group = 'kickstart-lsp-highlight', buffer = event2.buf }
482 end,
483 })
484 end
485
486 -- The following code creates a keymap to toggle inlay hints in your
487 -- code, if the language server you are using supports them
488 --
489 -- This may be unwanted, since they displace some of your code
490 if client and client:supports_method('textDocument/inlayHint', event.buf) then
491 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')
492 end
493 end,
494 })
495
496 -- Enable the following language servers
497 -- Feel free to add/remove any LSPs that you want here. They will automatically be installed.
498 -- See `:help lsp-config` for information about keys and how to configure
499 ---@type table<string, vim.lsp.Config>
500 local servers = {
501 clangd = {},
502 gopls = {},
503 ty = {},
504 bashls = {},
505 -- vtsls = {},
506 astro = {},
507
508 tailwindcss = {
509 -- exclude a filetype from the default_config
510 filetypes_exclude = { 'markdown' },
511 -- add additional filetypes to the default_config
512 filetypes_include = {},
513 -- to fully override the default_config, change the below
514 -- filetypes = {}
515
516 -- additional settings for the server, e.g:
517 -- tailwindCSS = { includeLanguages = { someLang = "html" } }
518 -- can be addeded to the settings table and will be merged with
519 -- this defaults for Phoenix projects
520 settings = {
521 tailwindCSS = {
522 includeLanguages = {
523 elixir = 'html-eex',
524 eelixir = 'html-eex',
525 heex = 'html-eex',
526 },
527 },
528 },
529 },
530 jsonls = {
531 -- lazy-load schemastore when needed
532 before_init = function(_, new_config)
533 new_config.settings.json.schemas = new_config.settings.json.schemas or {}
534 vim.list_extend(new_config.settings.json.schemas, require('schemastore').json.schemas())
535 end,
536 settings = {
537 json = {
538 format = {
539 enable = true,
540 },
541 validate = { enable = true },
542 },
543 },
544 },
545
546 bacon_ls = {
547 enabled = diagnostics == 'bacon-ls',
548 },
549 rust_analyzer = { enabled = false },
550 stylua = {}, -- Used to format Lua code
551
552 -- Special Lua Config, as recommended by neovim help docs
553 lua_ls = {
554 on_init = function(client)
555 if client.workspace_folders then
556 local path = client.workspace_folders[1].name
557 if path ~= vim.fn.stdpath 'config' and (vim.uv.fs_stat(path .. '/.luarc.json') or vim.uv.fs_stat(path .. '/.luarc.jsonc')) then return end
558 end
559
560 client.config.settings.Lua = vim.tbl_deep_extend('force', client.config.settings.Lua, {
561 runtime = {
562 version = 'LuaJIT',
563 path = { 'lua/?.lua', 'lua/?/init.lua' },
564 },
565 workspace = {
566 checkThirdParty = false,
567 -- NOTE: this is a lot slower and will cause issues when working on your own configuration.
568 -- See https://github.com/neovim/nvim-lspconfig/issues/3189
569 library = vim.tbl_extend('force', vim.api.nvim_get_runtime_file('', true), {
570 '${3rd}/luv/library',
571 '${3rd}/busted/library',
572 }),
573 },
574 })
575 end,
576 settings = {
577 Lua = {},
578 },
579 },
580 }
581
582 -- Ensure the servers and tools above are installed
583 --
584 -- To check the current status of installed tools and/or manually install
585 -- other tools, you can run
586 -- :Mason
587 --
588 -- You can press `g?` for help in this menu.
589 local ensure_installed = vim.tbl_keys(servers or {})
590 vim.list_extend(ensure_installed, {
591 -- You can add other tools here that you want Mason to install
592 })
593
594 require('mason-tool-installer').setup { ensure_installed = ensure_installed }
595
596 for name, server in pairs(servers) do
597 vim.lsp.config(name, server)
598 vim.lsp.enable(name)
599 end
600 end,
601 setup = {
602 tailwindcss = function(_, opts)
603 opts.filetypes = opts.filetypes or {}
604
605 -- Add default filetypes
606 vim.list_extend(opts.filetypes, vim.lsp.config.tailwindcss.filetypes)
607
608 -- Remove excluded filetypes
609 --- @param ft string
610 opts.filetypes = vim.tbl_filter(function(ft) return not vim.tbl_contains(opts.filetypes_exclude or {}, ft) end, opts.filetypes)
611
612 -- Add additional filetypes
613 vim.list_extend(opts.filetypes, opts.filetypes_include or {})
614 end,
615 },
616 keys = {
617 {
618 '<leader>cr',
619 function()
620 local inc_rename = require 'inc_rename'
621 return ':' .. inc_rename.config.cmd_name .. ' ' .. vim.fn.expand '<cword>'
622 end,
623 expr = true,
624 desc = 'Rename (inc-rename.nvim)',
625 },
626 },
627 },
628
629 { -- Autoformat
630 'stevearc/conform.nvim',
631 event = { 'BufWritePre' },
632 cmd = { 'ConformInfo' },
633 keys = {
634 {
635 '<leader>f',
636 function() require('conform').format { async = true, lsp_format = 'fallback' } end,
637 mode = '',
638 desc = '[F]ormat buffer',
639 },
640 },
641 ---@module 'conform'
642 ---@type conform.setupOpts
643 opts = {
644 notify_on_error = false,
645 format_on_save = function(bufnr)
646 -- Disable "format_on_save lsp_fallback" for languages that don't
647 -- have a well standardized coding style. You can add additional
648 -- languages here or re-enable it for the disabled ones.
649 local disable_filetypes = { c = true, cpp = true }
650 if disable_filetypes[vim.bo[bufnr].filetype] then
651 return nil
652 else
653 return {
654 timeout_ms = 500,
655 lsp_format = 'fallback',
656 }
657 end
658 end,
659 formatters_by_ft = {
660 lua = { 'stylua' },
661 -- Conform can also run multiple formatters sequentially
662 -- python = { "isort", "black" },
663 typescript = { 'biome-check' },
664 -- You can use 'stop_after_first' to run the first available formatter from the list
665 -- javascript = { "prettierd", "prettier", stop_after_first = true },
666 },
667 },
668 },
669
670 { -- Autocompletion
671 'saghen/blink.cmp',
672 event = 'VimEnter',
673 version = '1.*',
674 dependencies = {
675 {
676 'rafamadriz/friendly-snippets',
677 },
678 {
679 'onsails/lspkind.nvim',
680 },
681 },
682 ---@module 'blink.cmp'
683 ---@type blink.cmp.Config
684 opts = {
685 keymap = {
686 -- 'default' (recommended) for mappings similar to built-in completions
687 -- <c-y> to accept ([y]es) the completion.
688 -- This will auto-import if your LSP supports it.
689 -- This will expand snippets if the LSP sent a snippet.
690 -- 'super-tab' for tab to accept
691 -- 'enter' for enter to accept
692 -- 'none' for no mappings
693 --
694 -- For an understanding of why the 'default' preset is recommended,
695 -- you will need to read `:help ins-completion`
696 --
697 -- No, but seriously. Please read `:help ins-completion`, it is really good!
698 --
699 -- All presets have the following mappings:
700 -- <tab>/<s-tab>: move to right/left of your snippet expansion
701 -- <c-space>: Open menu or open docs if already open
702 -- <c-n>/<c-p> or <up>/<down>: Select next/previous item
703 -- <c-e>: Hide menu
704 -- <c-k>: Toggle signature help
705 --
706 -- See :h blink-cmp-config-keymap for defining your own keymap
707 preset = 'super-tab',
708
709 -- For more advanced Luasnip keymaps (e.g. selecting choice nodes, expansion) see:
710 -- https://github.com/L3MON4D3/LuaSnip?tab=readme-ov-file#keymaps
711 },
712
713 appearance = {
714 -- 'mono' (default) for 'Nerd Font Mono' or 'normal' for 'Nerd Font'
715 -- Adjusts spacing to ensure icons are aligned
716 nerd_font_variant = 'mono',
717 },
718
719 completion = {
720 -- By default, you may press `<c-space>` to show the documentation.
721 -- Optionally, set `auto_show = true` to show the documentation after a delay.
722 documentation = { auto_show = false, auto_show_delay_ms = 500 },
723
724 menu = {
725 draw = {
726 components = {
727 kind_icon = {
728 text = function(ctx) return require('lspkind').symbol_map[ctx.kind] or '' end,
729 },
730 },
731 },
732 },
733 },
734 sources = {
735 default = { 'lsp', 'path', 'snippets' },
736 per_filetype = {
737 sql = { 'snippets', 'dadbod', 'buffer' },
738 },
739 providers = {
740 dadbod = { name = 'Dadbod', module = 'vim_dadbod_completion.blink' },
741 },
742 },
743
744 snippets = { preset = 'mini_snippets' },
745
746 -- Blink.cmp includes an optional, recommended rust fuzzy matcher,
747 -- which automatically downloads a prebuilt binary when enabled.
748 --
749 -- By default, we use the Lua implementation instead, but you may enable
750 -- the rust implementation via `'prefer_rust_with_warning'`
751 --
752 -- See :h blink-cmp-config-fuzzy for more information
753 fuzzy = { implementation = 'prefer_rust_with_warning' },
754
755 -- Shows a signature help window while you type arguments for a function
756 -- signature = { enabled = true },
757 },
758 },
759 -- Highlight todo, notes, etc in comments
760 {
761 'folke/todo-comments.nvim',
762 event = 'VimEnter',
763 dependencies = { 'nvim-lua/plenary.nvim' },
764 ---@module 'todo-comments'
765 ---@type TodoOptions
766 ---@diagnostic disable-next-line: missing-fields
767 opts = { signs = false },
768 },
769
770 { -- Collection of various small independent plugins/modules
771 'nvim-mini/mini.nvim',
772 config = function()
773 -- Better Around/Inside textobjects
774 --
775 -- Examples:
776 -- - va) - [V]isually select [A]round [)]paren
777 -- - yinq - [Y]ank [I]nside [N]ext [Q]uote
778 -- - ci' - [C]hange [I]nside [']quote
779 require('mini.ai').setup { n_lines = 500 }
780
781 -- Add/delete/replace surroundings (brackets, quotes, etc.)
782 --
783 -- - saiw) - [S]urround [A]dd [I]nner [W]ord [)]Paren
784 -- - sd' - [S]urround [D]elete [']quotes
785 -- - sr)' - [S]urround [R]eplace [)] [']
786 require('mini.surround').setup()
787
788 require('mini.icons').setup()
789
790 require('mini.cursorword').setup()
791 require('mini.snippets').setup()
792 require('mini.indentscope').setup()
793 require('mini.tabline').setup()
794 require('mini.extra').setup()
795 require('mini.sessions').setup()
796 require('mini.git').setup()
797 require('mini.cmdline').setup()
798 require('mini.files').setup()
799 require('mini.visits').setup()
800 require('mini.move').setup()
801 require('mini.pairs').setup()
802 require('mini.diff').setup()
803 end,
804 },
805
806 {
807 'nvim-lualine/lualine.nvim',
808 event = 'VeryLazy',
809 init = function()
810 vim.g.lualine_laststatus = vim.o.laststatus
811 if vim.fn.argc(-1) > 0 then
812 -- set an empty statusline till lualine loads
813 vim.o.statusline = ' '
814 else
815 -- hide the statusline on the starter page
816 vim.o.laststatus = 0
817 end
818 end,
819 opts = function()
820 local colors = {
821 blue = '#80a0ff',
822 cyan = '#79dac8',
823 black = '#080808',
824 white = '#c6c6c6',
825 red = '#ff5189',
826 violet = '#d183e8',
827 grey = '#303030',
828 }
829
830 local bubbles_theme = {
831 normal = {
832 a = { fg = colors.black, bg = colors.violet },
833 b = { fg = colors.white, bg = colors.grey },
834 c = { fg = colors.white },
835 },
836
837 insert = { a = { fg = colors.black, bg = colors.blue } },
838 visual = { a = { fg = colors.black, bg = colors.cyan } },
839 replace = { a = { fg = colors.black, bg = colors.red } },
840
841 inactive = {
842 a = { fg = colors.white, bg = colors.black },
843 b = { fg = colors.white, bg = colors.black },
844 c = { fg = colors.white },
845 },
846 }
847
848 require('lualine').setup {
849 options = {
850 theme = bubbles_theme,
851 component_separators = '',
852 section_separators = { left = '', right = '' },
853 },
854 sections = {
855 lualine_a = { { 'mode', separator = { left = '' }, right_padding = 2 } },
856 lualine_b = { 'filename', 'branch' },
857 lualine_c = {
858 '%=', --[[ add your center components here in place of this comment ]]
859 },
860 lualine_x = {},
861 lualine_y = { 'filetype', 'progress' },
862 lualine_z = {
863 { 'location', separator = { right = '' }, left_padding = 2 },
864 },
865 },
866 inactive_sections = {
867 lualine_a = { 'filename' },
868 lualine_b = {},
869 lualine_c = {},
870 lualine_x = {},
871 lualine_y = {},
872 lualine_z = { 'location' },
873 },
874 tabline = {},
875 extensions = {},
876 }
877 end,
878 },
879
880 { -- Highlight, edit, and navigate code
881 'nvim-treesitter/nvim-treesitter',
882 lazy = false,
883 build = ':TSUpdate',
884 branch = 'main',
885 -- [[ Configure Treesitter ]] See `:help nvim-treesitter-intro`
886 opts = {
887 -- LazyVim config for treesitter
888 indent = { enable = true }, ---@type lazyvim.TSFeat
889 highlight = { enable = true }, ---@type lazyvim.TSFeat
890 folds = { enable = true }, ---@type lazyvim.TSFeat
891 ensure_installed = {
892 'bash',
893 'c',
894 'diff',
895 'html',
896 'javascript',
897 'jsdoc',
898 'json',
899 'lua',
900 'luadoc',
901 'luap',
902 'markdown',
903 'markdown_inline',
904 'printf',
905 'python',
906 'query',
907 'regex',
908 'toml',
909 'tsx',
910 'typescript',
911 'vim',
912 'vimdoc',
913 'xml',
914 'yaml',
915 },
916 },
917 config = function()
918 local parsers = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc' }
919 require('nvim-treesitter').install(parsers)
920 vim.api.nvim_create_autocmd('FileType', {
921 callback = function(args)
922 local buf, filetype = args.buf, args.match
923
924 local language = vim.treesitter.language.get_lang(filetype)
925 if not language then return end
926
927 -- check if parser exists and load it
928 if not vim.treesitter.language.add(language) then return end
929 -- enables syntax highlighting and other treesitter features
930 vim.treesitter.start(buf, language)
931
932 -- enables treesitter based folds
933 -- for more info on folds see `:help folds`
934 -- vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
935 -- vim.wo.foldmethod = 'expr'
936
937 -- enables treesitter based indentation
938 vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
939 end,
940 })
941 end,
942 },
943
944 -- The following comments only work if you have downloaded the kickstart repo, not just copy pasted the
945 -- init.lua. If you want these files, they are in the repository, so you can just download them and
946 -- place them in the correct locations.
947
948 -- NOTE: Next step on your Neovim journey: Add/Configure additional plugins for Kickstart
949 --
950 -- Here are some example plugins that I've included in the Kickstart repository.
951 -- Uncomment any of the lines below to enable them (you will need to restart nvim).
952 --
953 require 'kickstart.plugins.debug',
954 require 'kickstart.plugins.lint',
955 require 'kickstart.plugins.gitsigns', -- adds gitsigns recommended keymaps
956
957 -- NOTE: The import below can automatically add your own plugins, configuration, etc from `lua/custom/plugins/*.lua`
958 -- This is the easiest way to modularize your config.
959 --
960 -- Uncomment the following line and add your plugins to `lua/custom/plugins/*.lua` to get going.
961 { import = 'custom.plugins' },
962 --
963 -- For additional information with loading, sourcing and examples see `:help lazy.nvim-🔌-plugin-spec`
964 -- Or use telescope!
965 -- In normal mode type `<space>sh` then write `lazy.nvim-plugin`
966 -- you can continue same window with `<space>sr` which resumes last telescope search
967}, { ---@diagnostic disable-line: missing-fields
968 ui = {
969 -- If you are using a Nerd Font: set icons to an empty table which will use the
970 -- default lazy.nvim defined Nerd Font icons, otherwise define a unicode icons table
971 icons = vim.g.have_nerd_font and {} or {
972 cmd = '⌘',
973 config = '🛠',
974 event = '📅',
975 ft = '📂',
976 init = '⚙',
977 keys = '🗝',
978 plugin = '🔌',
979 runtime = '💻',
980 require = '🌙',
981 source = '📄',
982 start = '🚀',
983 task = '📌',
984 lazy = '💤 ',
985 },
986 },
987})
988
989require('matugen').setup()
990
991-- The line beneath this is called `modeline`. See `:help modeline`
992-- vim: ts=2 sts=2 sw=2 et