commit 1feb0d4dfd255491b30654c62587c8ee647df00e Author: huaqo Date: Fri Sep 18 19:41:50 2026 +0200 Initial Neovim config diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..62a1a12 --- /dev/null +++ b/init.lua @@ -0,0 +1,468 @@ +-- ============================================================ +-- 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 diff --git a/nvim-pack-lock.json b/nvim-pack-lock.json new file mode 100644 index 0000000..2bc8412 --- /dev/null +++ b/nvim-pack-lock.json @@ -0,0 +1,83 @@ +{ + "plugins": { + "LuaSnip": { + "rev": "642b0c595e11608b4c18219e93b88d7637af27bc", + "src": "https://github.com/L3MON4D3/LuaSnip", + "version": "2.0.0 - 3.0.0" + }, + "adwaita.nvim": { + "rev": "c607954f7b2cf56b24e921db0dab4749f3430a22", + "src": "https://github.com/Mofiqul/adwaita.nvim" + }, + "blink.cmp": { + "rev": "78336bc89ee5365633bcf754d93df01678b5c08f", + "src": "https://github.com/saghen/blink.cmp", + "version": "1.0.0 - 2.0.0" + }, + "conform.nvim": { + "rev": "016802de402556da54c36bd7359b441266b01cdd", + "src": "https://github.com/stevearc/conform.nvim" + }, + "fidget.nvim": { + "rev": "6f793b2bcd2d35e201c09520f698bb763220908a", + "src": "https://github.com/j-hui/fidget.nvim" + }, + "gitsigns.nvim": { + "rev": "5be654f2232c10ddcad19c1607a67b6b4b78fc29", + "src": "https://github.com/lewis6991/gitsigns.nvim" + }, + "guess-indent.nvim": { + "rev": "84a4987ff36798c2fc1169cbaff67960aed9776f", + "src": "https://github.com/NMAC427/guess-indent.nvim" + }, + "mason-lspconfig.nvim": { + "rev": "67029ccdac1ef8941e13b826417bc0ffac24cc86", + "src": "https://github.com/mason-org/mason-lspconfig.nvim" + }, + "mason-tool-installer.nvim": { + "rev": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc", + "src": "https://github.com/WhoIsSethDaniel/mason-tool-installer.nvim" + }, + "mason.nvim": { + "rev": "2a6940af80375532e5e9e7c1f2fc6319a1b7a69d", + "src": "https://github.com/mason-org/mason.nvim" + }, + "mini.nvim": { + "rev": "629bf6792187e11b4829411c1648f86f28df3963", + "src": "https://github.com/nvim-mini/mini.nvim" + }, + "nvim-lspconfig": { + "rev": "4267d26e60323a45e18f8e7a63a964cc8f53ca4e", + "src": "https://github.com/neovim/nvim-lspconfig" + }, + "nvim-treesitter": { + "rev": "c9f9ed6c1892f629ea399f4ee7905f2686fa13f2", + "src": "https://github.com/nvim-treesitter/nvim-treesitter", + "version": "'main'" + }, + "plenary.nvim": { + "rev": "74b06c6c75e4eeb3108ec01852001636d85a932b", + "src": "https://github.com/nvim-lua/plenary.nvim" + }, + "telescope-fzf-native.nvim": { + "rev": "b25b749b9db64d375d782094e2b9dce53ad53a40", + "src": "https://github.com/nvim-telescope/telescope-fzf-native.nvim" + }, + "telescope-ui-select.nvim": { + "rev": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2", + "src": "https://github.com/nvim-telescope/telescope-ui-select.nvim" + }, + "telescope.nvim": { + "rev": "427b576c16792edad01a92b89721d923c19ad60f", + "src": "https://github.com/nvim-telescope/telescope.nvim" + }, + "todo-comments.nvim": { + "rev": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668", + "src": "https://github.com/folke/todo-comments.nvim" + }, + "which-key.nvim": { + "rev": "3aab2147e74890957785941f0c1ad87d0a44c15a", + "src": "https://github.com/folke/which-key.nvim" + } + } +}