vim.loader.enable() vim.g.mapleader = ' ' vim.g.maplocalleader = ' ' vim.g.have_nerd_font = true vim.opt.wrap = false vim.opt.colorcolumn = "80" vim.o.number = true vim.o.relativenumber = true vim.o.mouse = 'a' vim.o.showmode = false vim.schedule(function() vim.o.clipboard = 'unnamedplus' end) vim.o.breakindent = true vim.o.undofile = true vim.o.ignorecase = true vim.o.smartcase = true vim.o.signcolumn = 'yes' vim.o.updatetime = 250 vim.o.timeoutlen = 300 vim.o.splitright = true vim.o.splitbelow = true vim.o.list = true vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '␣' } vim.o.inccommand = 'split' vim.o.cursorline = true vim.o.scrolloff = 5 vim.o.confirm = true vim.o.expandtab = true vim.o.tabstop = 2 vim.o.shiftwidth = 2 vim.keymap.set('n', '', 'nohlsearch') vim.diagnostic.config { update_in_insert = false, severity_sort = true, float = { border = 'rounded', source = 'if_many' }, underline = { severity = { min = vim.diagnostic.severity.WARN } }, virtual_text = true, virtual_lines = false, jump = { on_jump = function(_, bufnr) vim.diagnostic.open_float { bufnr = bufnr, scope = 'cursor', focus = false, } end, }, } vim.keymap.set("n", "h", ":wincmd h", { desc = "Move between windows (h)" }) vim.keymap.set("n", "j", ":wincmd j", { desc = "Move between windows (j)" }) vim.keymap.set("n", "k", ":wincmd k", { desc = "Move between windows (k)" }) vim.keymap.set("n", "l", ":wincmd l", { desc = "Move between windows (l)" }) vim.keymap.set("n", "sv", ":vsplit", { desc = "Split vertically" }) vim.keymap.set("n", "sh", ":split", { desc = "Split horizontally" }) vim.keymap.set("n", "", ":tabp", { desc = "Go to previous tab" }) vim.keymap.set("n", "", ":tabn", { desc = "Go to next tab" }) vim.keymap.set("n", "t", function() vim.cmd("tab split") require("telescope.builtin").find_files() end, { desc = "Open file in new tab" }) vim.api.nvim_create_autocmd('TextYankPost', { desc = 'Highlight when yanking (copying) text', group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }), callback = function() vim.hl.on_yank() end, }) local function run_build(name, cmd, cwd) local result = vim.system(cmd, { cwd = cwd }):wait() if result.code ~= 0 then local stderr = result.stderr or '' local stdout = result.stdout or '' local output = stderr ~= '' and stderr or stdout if output == '' then output = 'No output from build command.' end vim.notify(('Build failed for %s:\n%s'):format(name, output), vim.log.levels.ERROR) end end vim.api.nvim_create_autocmd('PackChanged', { callback = function(ev) local name = ev.data.spec.name local kind = ev.data.kind if kind ~= 'install' and kind ~= 'update' then return end if name == 'telescope-fzf-native.nvim' and vim.fn.executable 'make' == 1 then run_build(name, { 'make' }, ev.data.path) return end if name == 'LuaSnip' then if vim.fn.has 'win32' ~= 1 and vim.fn.executable 'make' == 1 then run_build(name, { 'make', 'install_jsregexp' }, ev.data.path) end return end if name == 'nvim-treesitter' then if not ev.data.active then vim.cmd.packadd 'nvim-treesitter' end vim.cmd 'TSUpdate' return end end, }) ---@param repo string ---@return string local function gh(repo) return 'https://github.com/' .. repo end vim.pack.add { gh 'NMAC427/guess-indent.nvim' } require('guess-indent').setup {} -- Here is a more advanced configuration example that passes options to `gitsigns.nvim` -- -- See `:help gitsigns` to understand what each configuration key does. -- Adds git related signs to the gutter, as well as utilities for managing changes vim.pack.add { gh 'lewis6991/gitsigns.nvim' } require('gitsigns').setup { signs = { add = { text = '+' }, ---@diagnostic disable-line: missing-fields change = { text = '~' }, ---@diagnostic disable-line: missing-fields delete = { text = '_' }, ---@diagnostic disable-line: missing-fields topdelete = { text = '‾' }, ---@diagnostic disable-line: missing-fields changedelete = { text = '~' }, ---@diagnostic disable-line: missing-fields }, } vim.pack.add { gh 'folke/which-key.nvim' } require('which-key').setup { delay = 0, icons = { mappings = vim.g.have_nerd_font }, spec = { { 's', group = '[S]plit', mode = { 'n' } }, { 'f', group = '[F]ind', mode = { 'n', 'v' } }, { 'h', group = 'Git [H]unk', mode = { 'n', 'v' } }, { 'gr', group = 'LSP Actions', mode = { 'n' } }, }, } vim.pack.add { gh 'MunifTanjim/nui.nvim' } vim.pack.add { gh 'X3eRo0/dired.nvim' } require('dired').setup { } vim.pack.add { gh 'ellisonleao/gruvbox.nvim' } vim.cmd.colorscheme 'gruvbox' vim.pack.add { gh 'folke/todo-comments.nvim' } require('todo-comments').setup { signs = false } vim.pack.add { gh '3rd/image.nvim' } require("image").setup({ backend = "sixel", -- or "" or "" processor = "magick_cli", -- or "magick_rock" integrations = { markdown = { enabled = true, clear_in_insert_mode = false, download_remote_images = true, only_render_image_at_cursor = true, only_render_image_at_cursor_mode = "popup", -- or "inline" floating_windows = false, -- if true, images will be rendered in floating markdown windows filetypes = { "markdown", "vimwiki" }, -- markdown extensions (ie. quarto) can go here }, asciidoc = { enabled = true, clear_in_insert_mode = false, download_remote_images = true, only_render_image_at_cursor = true, only_render_image_at_cursor_mode = "popup", floating_windows = false, filetypes = { "asciidoc", "adoc" }, }, neorg = { enabled = true, filetypes = { "norg" }, }, rst = { enabled = true, }, typst = { enabled = true, filetypes = { "typst" }, }, html = { enabled = false, }, css = { enabled = false, }, }, max_width = nil, max_height = nil, max_width_window_percentage = nil, max_height_window_percentage = 50, scale_factor = 1.0, window_overlap_clear_enabled = false, -- toggles images when windows are overlapped window_overlap_clear_ft_ignore = { "cmp_menu", "cmp_docs", "snacks_notif", "scrollview", "scrollview_sign" }, editor_only_render_when_focused = false, -- auto show/hide images when the editor gains/looses focus tmux_show_only_in_active_window = false, -- auto show/hide images in the correct Tmux window (needs visual-activity off) hijack_file_patterns = { "*.png", "*.jpg", "*.jpeg", "*.gif", "*.webp", "*.avif" }, -- render image files as images when opened }) vim.pack.add { gh 'ej-shafran/compile-mode.nvim' } vim.g.compile_mode = {} vim.keymap.set('n', 'cp', ":Compile", { desc = '[C]om[p]ile' }) vim.keymap.set('n', 'n', ":NextError", { desc = '[N]ext Error' }) vim.keymap.set('t', '', "", { desc = 'Escape terminal mode' }) vim.keymap.set('n', 'z', "_", { desc = '[Z]en Mode' }) vim.keymap.set('n', '', "=", { desc = 'Escape [Z]en Mode' }) vim.pack.add { gh 'nvim-mini/mini.nvim' } if vim.g.have_nerd_font then require('mini.icons').setup() MiniIcons.mock_nvim_web_devicons() end require('mini.ai').setup { -- NOTE: Avoid conflicts with the built-in incremental selection mappings on Neovim>=0.12 (see `:help treesitter-incremental-selection`) mappings = { around_next = 'aa', inside_next = 'ii', }, n_lines = 500, } 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 ---@type (string|vim.pack.Spec)[] local telescope_plugins = { gh 'nvim-lua/plenary.nvim', gh 'nvim-telescope/telescope.nvim', gh 'nvim-telescope/telescope-ui-select.nvim', } if vim.fn.executable 'make' == 1 then table.insert(telescope_plugins, gh 'nvim-telescope/telescope-fzf-native.nvim') end vim.pack.add(telescope_plugins) require('telescope').setup { extensions = { ['ui-select'] = { require('telescope.themes').get_dropdown() }, }, } pcall(require('telescope').load_extension, 'fzf') pcall(require('telescope').load_extension, 'ui-select') local builtin = require 'telescope.builtin' vim.keymap.set('n', 'fk', builtin.keymaps, { desc = '[F]ind [K]eymaps' }) vim.keymap.set('n', 'ff', builtin.find_files, { desc = '[F]ind [F]iles' }) vim.keymap.set({ 'n', 'v' }, 'fw', builtin.grep_string, { desc = '[F]ind current [W]ord' }) vim.keymap.set('n', 'fg', builtin.live_grep, { desc = '[F]ind by [G]rep' }) vim.keymap.set('n', 'fd', builtin.diagnostics, { desc = '[F]ind [D]iagnostics' }) vim.keymap.set('n', 'fc', builtin.commands, { desc = '[F]ind [C]ommands' }) vim.keymap.set('n', '', builtin.buffers, { desc = '[ ] Find existing buffers' }) vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('telescope-lsp-attach', { clear = true }), callback = function(event) local buf = event.buf vim.keymap.set('n', 'grr', builtin.lsp_references, { buffer = buf, desc = '[G]oto [R]eferences' }) vim.keymap.set('n', 'gri', builtin.lsp_implementations, { buffer = buf, desc = '[G]oto [I]mplementation' }) vim.keymap.set('n', 'grd', builtin.lsp_definitions, { buffer = buf, desc = '[G]oto [D]efinition' }) vim.keymap.set('n', 'gO', builtin.lsp_document_symbols, { buffer = buf, desc = 'Open Document Symbols' }) vim.keymap.set('n', 'gW', builtin.lsp_dynamic_workspace_symbols, { buffer = buf, desc = 'Open Workspace Symbols' }) vim.keymap.set('n', 'grt', builtin.lsp_type_definitions, { buffer = buf, desc = '[G]oto [T]ype Definition' }) end, }) vim.keymap.set('n', 'fc', function() builtin.current_buffer_fuzzy_find(require('telescope.themes').get_dropdown { winblend = 10, previewer = false, }) end, { desc = '[F]uzzily search in [c]urrent buffer' }) vim.keymap.set( 'n', 'f/', function() builtin.live_grep { grep_open_files = true, prompt_title = 'Live Grep in Open Files', } end, { desc = '[F]ind in Open Files' } ) vim.pack.add { gh 'j-hui/fidget.nvim' } require('fidget').setup {} vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('kickstart-lsp-attach', { clear = true }), callback = function(event) local map = function(keys, func, desc, mode) mode = mode or 'n' vim.keymap.set(mode, keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc }) end map('grn', vim.lsp.buf.rename, '[R]e[n]ame') map('gra', vim.lsp.buf.code_action, '[G]oto Code [A]ction', { 'n', 'x' }) map('grD', vim.lsp.buf.declaration, '[G]oto [D]eclaration') local client = vim.lsp.get_client_by_id(event.data.client_id) if client and client:supports_method('textDocument/documentHighlight', event.buf) then local highlight_augroup = vim.api.nvim_create_augroup('kickstart-lsp-highlight', { clear = false }) vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, { buffer = event.buf, group = highlight_augroup, callback = vim.lsp.buf.document_highlight, }) vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, { buffer = event.buf, group = highlight_augroup, callback = vim.lsp.buf.clear_references, }) vim.api.nvim_create_autocmd('LspDetach', { group = vim.api.nvim_create_augroup('kickstart-lsp-detach', { clear = true }), callback = function(event2) vim.lsp.buf.clear_references() vim.api.nvim_clear_autocmds { group = 'kickstart-lsp-highlight', buffer = event2.buf } end, }) end if client and client:supports_method('textDocument/inlayHint', event.buf) then map('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 servers = { -- clangd = {}, -- gopls = {}, -- pyright = {}, -- rust_analyzer = {}, -- -- Some languages (like typescript) have entire language plugins that can be useful: -- https://github.com/pmizio/typescript-tools.nvim -- -- But for many setups, the LSP (`ts_ls`) will work just fine -- ts_ls = {}, stylua = {}, -- Used to format Lua code lua_ls = { on_init = function(client) client.server_capabilities.documentFormattingProvider = false if client.workspace_folders then local path = client.workspace_folders[1].name 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 end client.config.settings.Lua = vim.tbl_deep_extend('force', client.config.settings.Lua, { runtime = { version = 'LuaJIT', path = { 'lua/?.lua', 'lua/?/init.lua' }, }, workspace = { checkThirdParty = false, -- NOTE: this is a lot slower and will cause issues when working on your own configuration. -- See https://github.com/neovim/nvim-lspconfig/issues/3189 library = vim.tbl_extend('force', vim.api.nvim_get_runtime_file('', true), { '${3rd}/luv/library', '${3rd}/busted/library', }), }, }) end, ---@type lspconfig.settings.lua_ls settings = { Lua = { format = { enable = false }, }, }, }, } vim.pack.add { -- gh 'neovim/nvim-lspconfig', -- gh 'mason-org/mason.nvim', -- gh 'mason-org/mason-lspconfig.nvim', -- gh 'WhoIsSethDaniel/mason-tool-installer.nvim', } -- Automatically install LSPs and related tools to stdpath for Neovim -- require('mason').setup {} local ensure_installed = vim.tbl_keys(servers or {}) vim.list_extend(ensure_installed, { -- You can add other tools here that you want Mason to install }) -- require('mason-tool-installer').setup { ensure_installed = ensure_installed } for name, server in pairs(servers) do vim.lsp.config(name, server) vim.lsp.enable(name) end vim.pack.add { gh 'stevearc/conform.nvim' } require('conform').setup { notify_on_error = false, format_on_save = function(bufnr) local enabled_filetypes = { c = true, lua = true, python = true, } if enabled_filetypes[vim.bo[bufnr].filetype] then return { timeout_ms = 500 } else return nil end end, default_format_opts = { lsp_format = 'fallback', -- Use external formatters if configured below, otherwise use LSP formatting. Set to `false` to disable LSP formatting entirely. }, -- You can also specify external formatters in here. formatters_by_ft = { -- rust = { 'rustfmt' }, -- Conform can also run multiple formatters sequentially -- python = { "isort", "black" }, -- -- You can use 'stop_after_first' to run the first available formatter from the list -- javascript = { "prettierd", "prettier", stop_after_first = true }, }, } vim.keymap.set({ 'n', 'v' }, 'f', function() require('conform').format { async = true } end, { desc = '[F]ormat buffer' }) -- vim.pack.add { { src = gh 'L3MON4D3/LuaSnip', version = vim.version.range '2.*' } } -- require('luasnip').setup {} -- `friendly-snippets` contains a variety of premade snippets. -- See the README about individual language/framework/plugin snippets: -- https://github.com/rafamadriz/friendly-snippets -- -- vim.pack.add { gh 'rafamadriz/friendly-snippets' } -- require('luasnip.loaders.from_vscode').lazy_load() -- [[ Autocomplete Engine ]] -- vim.pack.add { { src = gh 'saghen/blink.cmp', version = vim.version.range '1.*' } } -- require('blink.cmp').setup { -- keymap = { -- 'default' (recommended) for mappings similar to built-in completions -- to accept ([y]es) the completion. -- This will auto-import if your LSP supports it. -- This will expand snippets if the LSP sent a snippet. -- 'super-tab' for tab to accept -- 'enter' for enter to accept -- 'none' for no mappings -- -- For an understanding of why the 'default' preset is recommended, -- you will need to read `:help ins-completion` -- -- No, but seriously. Please read `:help ins-completion`, it is really good! -- -- All presets have the following mappings: -- /: move to right/left of your snippet expansion -- : Open menu or open docs if already open -- / or /: Select next/previous item -- : Hide menu -- : Toggle signature help -- -- See `:help blink-cmp-config-keymap` for defining your own keymap -- preset = 'default', -- For more advanced Luasnip keymaps (e.g. selecting choice nodes, expansion) see: -- https://github.com/L3MON4D3/LuaSnip?tab=readme-ov-file#keymaps -- }, -- appearance = { -- 'mono' (default) for 'Nerd Font Mono' or 'normal' for 'Nerd Font' -- Adjusts spacing to ensure icons are aligned -- nerd_font_variant = 'mono', -- }, -- completion = { -- By default, you may press `` to show the documentation. -- Optionally, set `auto_show = true` to show the documentation after a delay. -- documentation = { auto_show = false, auto_show_delay_ms = 500 }, -- }, -- sources = { -- default = { 'lsp', 'path', 'snippets' }, -- }, -- snippets = { preset = 'luasnip' }, -- Blink.cmp includes an optional, recommended rust fuzzy matcher, -- which automatically downloads a prebuilt binary when enabled. -- -- By default, we use the Lua implementation instead, but you may enable -- the rust implementation via `'prefer_rust_with_warning'` -- -- See `:help blink-cmp-config-fuzzy` for more information -- fuzzy = { implementation = 'lua' }, -- Shows a signature help window while you type arguments for a function -- signature = { enabled = true }, -- } vim.pack.add { { src = gh 'nvim-treesitter/nvim-treesitter', version = 'main' } } local parsers = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc' } require('nvim-treesitter').install(parsers) local function treesitter_try_attach(buf, language) if not vim.treesitter.language.add(language) then return end vim.treesitter.start(buf, language) local has_indent_query = vim.treesitter.query.get(language, 'indents') ~= nil if has_indent_query then vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()" end end local available_parsers = require('nvim-treesitter').get_available() vim.api.nvim_create_autocmd('FileType', { callback = function(args) local buf, filetype = args.buf, args.match local language = vim.treesitter.language.get_lang(filetype) if not language then return end local installed_parsers = require('nvim-treesitter').get_installed 'parsers' if vim.tbl_contains(installed_parsers, language) then treesitter_try_attach(buf, language) elseif vim.tbl_contains(available_parsers, language) then require('nvim-treesitter').install(language):await(function() treesitter_try_attach(buf, language) end) else treesitter_try_attach(buf, language) end end, })