-- Config shortcuts local keymap = vim.keymap local vr = vim.version.range -- Autocommand group for this file to allow config reloading local augroup = vim.api.nvim_create_augroup('init-plugins', { clear = true }) ------------------ -- INSTALL PLUGINS ------------------ -- Run commands to build or update things when installing/upgrading plugins vim.api.nvim_create_autocmd('PackChanged', { group = augroup, callback = function(ev) local name, kind = ev.data.spec.name, ev.data.kind if kind == 'install' or kind == 'update' then if name == 'nvim-treesitter' then -- nvim-treesitter requires installed parsers to be updated when upgrading the plugin. vim.cmd(':TSUpdate') end end end, }) vim.pack.add { -- [ Libraries ] -- Plenary: Library with helper functions (needed by neovim-session-manager) 'https://github.com/nvim-lua/plenary.nvim', -- UI component library (needed by neo-tree) 'https://github.com/MunifTanjim/nui.nvim', -- Nerd font icons (e.g. file types) 'https://github.com/nvim-tree/nvim-web-devicons', -- [ General / plugin collections ] -- mini.nvim plugin collection { src = 'https://github.com/nvim-mini/mini.nvim', version = 'stable' }, -- snacks.nvim plugin collection 'https://github.com/folke/snacks.nvim', -- Session manager 'https://github.com/Shatur/neovim-session-manager', -- File browser sidebar { src = 'https://github.com/nvim-neo-tree/neo-tree.nvim', version = vr '3.*' }, -- [ UI ] -- Better status line 'https://github.com/nvim-lualine/lualine.nvim', -- Better tab bar 'https://github.com/romgrk/barbar.nvim', -- Breadcrumbs 'https://github.com/Bekaboo/dropbar.nvim', -- Git integration for buffers (added/changed/deleted signs and more) 'https://github.com/lewis6991/gitsigns.nvim', -- Code exploration picker 'https://github.com/error311/wayfinder.nvim', -- Show keybindings while typing 'https://github.com/folke/which-key.nvim', -- Easier window management/navigation { src = 'https://github.com/rvaccone/wind.nvim', version = vr '1.*' }, -- [ Editing ] -- Better code completion { src = 'https://github.com/saghen/blink.cmp', version = vr '1.*' }, -- Auto-pairs 'https://github.com/ZhiyuanLck/smart-pairs', -- Auto-close structures with "end" keyword (e.g. in Lua) 'https://github.com/RRethy/nvim-treesitter-endwise', -- Substitute and exchange operator 'https://github.com/gbprod/substitute.nvim', -- Surround selections with quotes etc. { src = 'https://github.com/kylechui/nvim-surround', version = vr '4.*' }, -- Snippet collection 'https://github.com/rafamadriz/friendly-snippets', -- [ Text rendering ] -- Highlight word under cursor 'https://github.com/sontungexpt/stcursorword', -- Highlight todo comments 'https://github.com/folke/todo-comments.nvim', -- Highlight colors (hex codes etc.) 'https://github.com/brenoprata10/nvim-highlight-colors', -- [ Language support ] -- Treesitter parser manager 'https://github.com/nvim-treesitter/nvim-treesitter', -- Collection of LSP configurations 'https://github.com/neovim/nvim-lspconfig', -- LSP manager 'https://github.com/mason-org/mason.nvim', -- Bridge between Mason and lspconfig 'https://github.com/mason-org/mason-lspconfig.nvim', } --------------------- -- INITIALIZE PLUGINS --------------------- ------------ -- mini.nvim ------------ -- Extended a/i text objects require('mini.ai').setup {} -- Icon provider require('mini.icons').setup {} -------------- -- snacks.nvim -------------- -- Enable and configure selected plugins require('snacks').setup { bigfile = { enabled = true }, git = { enabled = true }, indent = { enabled = true, animate = { enabled = false }, }, input = { enabled = true }, notifier = { enabled = true, timeout = 5000, -- milliseconds, default: 3000 }, picker = { enabled = true }, scroll = { enabled = true }, scratch = { enabled = true }, statuscolumn = { enabled = true }, terminal = { enabled = true }, } -- Mappings for various pickers -- See also: https://github.com/folke/snacks.nvim/blob/main/docs/picker.md#general keymap.set('n', 'fb', Snacks.picker.buffers, { desc = 'Buffers' }) keymap.set('n', 'ff', Snacks.picker.files, { desc = 'Find files' }) keymap.set('n', 'fg', Snacks.picker.grep, { desc = 'Live grep' }) keymap.set('n', 'fj', Snacks.picker.jumps, { desc = 'Jump list' }) keymap.set('n', 'fr', Snacks.picker.resume, { desc = 'Resume previous search' }) keymap.set('n', 'fu', Snacks.picker.undo, { desc = 'Undo history' }) -- Mappings for LSP-related pickers -- TODO: Add more LSP shortcuts keymap.set('n', 'dx', Snacks.picker.diagnostics, { desc = 'List diagnostics' }) keymap.set('n', 'gd', Snacks.picker.lsp_definitions, { desc = 'Go to definition' }) -- Mappings for scratchpads keymap.set('n', '.', Snacks.scratch.open) keymap.set('n', 'scr', Snacks.scratch.select) -- Mappings for git keymap.set('n', 'gg', Snacks.lazygit.open, { desc = 'Open lazygit' }) keymap.set('n', 'gl', Snacks.lazygit.log, { desc = 'Open git log' }) keymap.set('n', 'gL', Snacks.lazygit.log_file, { desc = 'Open git log for file' }) keymap.set('n', 'gB', Snacks.gitbrowse.open, { desc = 'Open git repository in browser' }) -- Toggle terminal with Ctrl-/ keymap.set({ 'n', 't' }, '', Snacks.terminal.toggle) ------------------------- -- neovim-session-manager ------------------------- local session_manager_config = require('session_manager.config') require('session_manager').setup { -- Only autoload session for the current directory or git repository if it exists. -- (Default: Always load the last session) autoload_mode = { session_manager_config.AutoloadMode.CurrentDir, session_manager_config.AutoloadMode.GitSession, }, -- A list of directories where the session will not be autosaved. autosave_ignore_dirs = {}, -- Only autosave if a session is active. autosave_only_in_session = true, -- List currently loaded session in the load_session UI. load_include_current = true, } -- Keymaps (all with \ss prefix) keymap.set('n', 'ssl', 'SessionManager load_session') keymap.set('n', 'ss0', 'SessionManager load_last_session') keymap.set('n', 'ss.', 'SessionManager load_current_dir_session') keymap.set('n', 'ssg', 'SessionManager load_git_session') keymap.set('n', 'ssv', 'SessionManager save_current_session') ---------------------------------- -- neo-tree (file browser sidebar) ---------------------------------- require('neo-tree').setup { enable_cursor_hijack = true, source_selector = { statusline = true, }, default_component_configs = { indent = { with_expanders = true }, symlink_target = { enabled = true }, }, window = { -- TODO: Mappings for h/l to open/close directories? mappings = {}, }, filesystem = { bind_to_cwd = false, filtered_items = { hide_dotfiles = false, hide_gitignored = false, hide_by_name = { '.ansible', '.cache', '.idea', '.git', 'node_modules', }, always_show = { '.gitignore', }, }, follow_current_file = { enabled = true }, use_libuv_file_watcher = true, }, buffers = { bind_to_cwd = false, }, } -- Make symbolic link targets less visible vim.cmd.highlight { "NeoTreeSymbolicLinkTarget", "guifg=#626262" } -- Focus file explorer with space+e, toggle with uppercase E keymap.set('n', ' e', 'Neotree focus') keymap.set('n', ' E', 'Neotree toggle') ------------------------------- -- lualine (better status line) ------------------------------- local lualine_filename_component = { 'filename', path = 1, -- Show relative paths symbols = { readonly = '[readonly]', }, } require('lualine').setup { options = { disabled_filetypes = { 'neo-tree', }, }, sections = { lualine_c = { lualine_filename_component }, lualine_x = { 'lsp_status', -- Display warning if treesitter parser is missing { 'vim.b._treesitter_missing and "No Treesitter" or ""', color = { fg = '#cc6600' } }, 'filetype', }, }, inactive_sections = { lualine_c = { lualine_filename_component }, }, } -- Disable builtin mode indicator in bottom line since the statusline contains it too vim.o.showmode = false -------------------------- -- barbar (buffer/tab bar) -------------------------- require('barbar').setup {} -- Close tab keymap.set('n', '', 'BufferClose') -- Move to previous/next tab keymap.set('n', '', 'BufferPrevious') keymap.set('n', '', 'BufferNext') -- Re-order tab keymap.set('n', '', 'BufferMovePrevious') keymap.set('n', '>', 'BufferMoveNext') -- Jump to buffer by letter keymap.set('n', '', 'BufferPick') ------------------------ -- dropbar (breadcrumbs) ------------------------ require('dropbar').setup {} ----------------------------------------- -- gitsigns (git integration for buffers) ----------------------------------------- require('gitsigns').setup {} -------------------------------------- -- wayfinder (code exploration picker) -------------------------------------- require('wayfinder').setup {} -- Open wayfinder keymap.set('n', 'wf', '(WayfinderOpen)', { desc = 'Wayfinder' }) ------------------------------- -- which-key (show keybindings) ------------------------------- require('which-key').setup { preset = 'modern', } -- Mapping for showing normal mode keymaps keymap.set('n', '?', require('which-key').show) ---------------------------------- -- wind (easier window management) ---------------------------------- local wind = require('wind') wind.setup { windows = { max = 4, }, breaths = { max = 4, }, -- TODO: Adjust keymaps? -- keymaps = false, } ------------------------------ -- blink.cmp (code completion) ------------------------------ require('blink.cmp').setup { keymap = { preset = 'super-tab', }, completion = { trigger = { show_in_snippet = false }, }, cmdline = { keymap = { preset = 'inherit', [''] = { 'show', 'select_and_accept', 'fallback', }, }, completion = { menu = { auto_show = true }, }, }, } -------------- -- smart-pairs -------------- require('pairs'):setup { pairs = { -- TODO: For now, disable auto-pairs for single quotes to avoid unnecessary quotes in -- comments and strings (e.g. "don't"). Not a good solution though. ['*'] = { { '(', ')' }, { '[', ']' }, { '{', '}' }, { "'", "'", { ignore_pre = '.*' }}, { '"', '"' }, }, }, } --------------------------------------------- -- substitute (substitute/exchange operators) --------------------------------------------- local substitute = require('substitute') local substitute_exchange = require('substitute.exchange') substitute.setup {} -- Use "s" key for substitute (by default "s" is equivalent to "cl") keymap.set('n', 's', substitute.operator) keymap.set('n', 'ss', substitute.line) keymap.set('n', 'S', substitute.eol) keymap.set('x', 's', substitute.visual) -- Use "sx" for exchange (use twice to swap text objects) -- TODO: Maybe actually use x/X or space+x for this? keymap.set('n', 'sx', substitute_exchange.operator) keymap.set('n', 'sxx', substitute_exchange.line) keymap.set('x', 'X', substitute_exchange.visual) -- Prefix with space to use system clipboard (remap to use the substitute mappings above) keymap.set({'n', 'x'}, ' s', '"+s', { remap = true, desc = 'Substitute (clipboard)' }) keymap.set({'n', 'x'}, ' S', '"+S', { remap = true, desc = 'Substitute rest of line (clipboard)' }) -------------------------------------- -- nvim-surround (surround selections) -------------------------------------- require('nvim-surround').setup {} --------------------------------------------- -- stcursorword (highlight word under cursor) --------------------------------------------- require('stcursorword').setup { excluded = { filetypes = { 'neo-tree', }, }, } ---------------- -- todo-comments ---------------- require('todo-comments').setup { -- Disable gutter icons signs = false, -- Customize highlighting highlight = { before = '', -- By default ("wide"), the surrounding space and colon are highlighted too, -- which makes the colon invisible keyword = 'bg', after = 'fg', -- Highlight keywords always, not just when followed by a colon (the default) pattern = '<(KEYWORDS)>', }, } ------------------------------------------------- -- nvim-highlight-colors (display colors in code) ------------------------------------------------- require('nvim-highlight-colors').setup {} ---------------------------------------------- -- nvim-treesitter (manage treesitter parsers) ---------------------------------------------- -- Install Tree-sitter parsers require('nvim-treesitter').install { 'bash', 'cmake', 'cpp', 'css', 'dockerfile', 'editorconfig', 'git_config', 'gitignore', 'go', 'html', 'jinja', 'json', 'python', 'sway', 'yaml', } -- Collect a list of all currently installed Tree-sitter parsers local function get_installed_treesitter_parsers() local parsers = {} for _, path in pairs(vim.api.nvim_get_runtime_file('parser/*.so', true)) do local name, _ = vim.fs.basename(path):gsub('%.so$', '') table.insert(parsers, name) end return parsers end local treesitter_parsers = get_installed_treesitter_parsers() -- Autocommand to load treesitter parser vim.api.nvim_create_autocmd("FileType", { group = augroup, callback = function(args) local lang = vim.treesitter.language.get_lang(args.match) if vim.list_contains(treesitter_parsers, lang) then vim.treesitter.start(args.buf) -- TODO: Enable folds, indentation? vim.b[args.buf]._treesitter_missing = false else vim.b[args.buf]._treesitter_missing = true end end }) ---------------------- -- mason (LSP manager) ---------------------- require('mason').setup {} ------------------------------------------- -- mason-lspconfig (Mason/lspconfig bridge) ------------------------------------------- require('mason-lspconfig').setup {}