-- ============================================================ -- SECTION 1: OPTIONS -- Core Neovim settings, leaders, options -- ============================================================ do vim.loader.enable() vim.g.mapleader = ' ' vim.g.maplocalleader = ' ' vim.g.have_nerd_font = true 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 = 10 vim.o.confirm = true end -- ============================================================ -- SECTION 2: KEYMAPS & AUTOCMDS -- basic keymaps, basic autocmds -- ============================================================ do 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', 'q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' }) vim.keymap.set('t', '', '', { desc = 'Exit terminal mode' }) vim.keymap.set('n', '', 'echo "Use h to move!!"') vim.keymap.set('n', '', 'echo "Use l to move!!"') vim.keymap.set('n', '', 'echo "Use k to move!!"') vim.keymap.set('n', '', 'echo "Use j to move!!"') vim.keymap.set('n', '', '', { desc = 'Move focus to the left window' }) vim.keymap.set('n', '', '', { desc = 'Move focus to the right window' }) vim.keymap.set('n', '', '', { desc = 'Move focus to the lower window' }) vim.keymap.set('n', '', '', { desc = 'Move focus to the upper window' }) 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, }) end -- ============================================================ -- SECTION 3: PLUGIN MANAGER INTRO -- vim.pack intro, build hooks -- ============================================================ do 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, }) end local function gh(repo) return 'https://github.com/' .. repo end -- ============================================================ -- SECTION 4: UI / CORE UX PLUGINS -- guess-indent, gitsigns, which-key, colorscheme, todo-comments, mini modules -- ============================================================ do vim.pack.add { gh 'NMAC427/guess-indent.nvim' } require('guess-indent').setup {} vim.pack.add { gh 'lewis6991/gitsigns.nvim' } require('gitsigns').setup { signs = { add = { text = '+' }, change = { text = '~' }, delete = { text = '_' }, topdelete = { text = '‾' }, changedelete = { text = '~' }, }, } 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]earch', mode = { 'n', 'v' } }, { 't', group = '[T]oggle' }, { 'h', group = 'Git [H]unk', mode = { 'n', 'v' } }, -- Enable gitsigns recommended keymaps first { 'gr', group = 'LSP Actions', mode = { 'n' } }, }, } vim.pack.add { gh 'Mofiqul/adwaita.nvim' } vim.cmd.colorscheme 'adwaita' vim.pack.add { gh 'folke/todo-comments.nvim' } require('todo-comments').setup { signs = false } 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 { 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 } statusline.section_location = function() return '%2l:%-2v' end end -- ============================================================ -- SECTION 5: SEARCH & NAVIGATION -- Telescope setup, keymaps, LSP picker mappings -- ============================================================ do 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', 'sh', builtin.help_tags, { desc = '[S]earch [H]elp' }) vim.keymap.set('n', 'sk', builtin.keymaps, { desc = '[S]earch [K]eymaps' }) vim.keymap.set('n', 'sf', builtin.find_files, { desc = '[S]earch [F]iles' }) vim.keymap.set('n', 'ss', builtin.builtin, { desc = '[S]earch [S]elect Telescope' }) vim.keymap.set({ 'n', 'v' }, 'sw', builtin.grep_string, { desc = '[S]earch current [W]ord' }) vim.keymap.set('n', 'sg', builtin.live_grep, { desc = '[S]earch by [G]rep' }) vim.keymap.set('n', 'sd', builtin.diagnostics, { desc = '[S]earch [D]iagnostics' }) vim.keymap.set('n', 'sr', builtin.resume, { desc = '[S]earch [R]esume' }) vim.keymap.set('n', 's.', builtin.oldfiles, { desc = '[S]earch Recent Files ("." for repeat)' }) vim.keymap.set('n', 'sc', builtin.commands, { desc = '[S]earch [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', '/', function() builtin.current_buffer_fuzzy_find(require('telescope.themes').get_dropdown { winblend = 10, previewer = false, }) end, { desc = '[/] Fuzzily search in current buffer' }) vim.keymap.set( 'n', 's/', function() builtin.live_grep { grep_open_files = true, prompt_title = 'Live Grep in Open Files', } end, { desc = '[S]earch [/] in Open Files' } ) vim.keymap.set('n', 'sn', function() builtin.find_files { cwd = vim.fn.stdpath 'config', follow = true } end, { desc = '[S]earch [N]eovim files' }) end -- ============================================================ -- SECTION 6: LSP -- LSP keymaps, server configuration, Mason tools installations -- ============================================================ do 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 = { stylua = {}, 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 local current_settings = client.config.settings --[[@as lspconfig.settings.lua_ls]] client.config.settings.Lua = vim.tbl_deep_extend('force', current_settings.Lua, { runtime = { version = 'LuaJIT', path = { 'lua/?.lua', 'lua/?/init.lua' }, }, workspace = { checkThirdParty = false, library = vim.api.nvim_get_runtime_file('', true), }, }) end, 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', } require('mason').setup {} require('mason-lspconfig').setup { automatic_enable = true, } local ensure_installed = vim.tbl_keys(servers or {}) vim.list_extend(ensure_installed, { }) 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 end -- ============================================================ -- SECTION 7: FORMATTING -- conform.nvim setup and keymap -- ============================================================ do vim.pack.add { gh 'stevearc/conform.nvim' } require('conform').setup { notify_on_error = false, format_on_save = function(bufnr) local enabled_filetypes = { python = false, c = false } if enabled_filetypes[vim.bo[bufnr].filetype] then return { timeout_ms = 500 } else return nil end end, default_format_opts = { lsp_format = 'fallback', }, formatters_by_ft = { }, } vim.keymap.set({ 'n', 'v' }, 'f', function() require('conform').format { async = true } end, { desc = '[F]ormat buffer' }) end -- ============================================================ -- SECTION 8: AUTOCOMPLETE & SNIPPETS -- blink.cmp and luasnip setup -- ============================================================ do vim.pack.add { { src = gh 'L3MON4D3/LuaSnip', version = vim.version.range '2.*' } } require('luasnip').setup {} vim.pack.add { { src = gh 'saghen/blink.cmp', version = vim.version.range '1.*' } } require('blink.cmp').setup { keymap = { preset = 'default', }, appearance = { nerd_font_variant = 'mono', }, completion = { documentation = { auto_show = false, auto_show_delay_ms = 500 }, }, sources = { default = { 'lsp', 'path', 'snippets' }, }, snippets = { preset = 'luasnip' }, fuzzy = { implementation = 'lua' }, signature = { enabled = true }, } end -- ============================================================ -- SECTION 9: TREESITTER -- Parser installation, syntax highlighting, folds, indentation -- ============================================================ do 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, }) end