commit 7213bec4dc4912900d18185713e05c3ce7e96f21 Author: Mikhail Kilin Date: Mon Feb 23 23:40:37 2026 +0300 Initial neovim config Lazy.nvim plugin manager, LSP (rust-analyzer, clangd, ts_ls, zls), treesitter, telescope, bufferline, neo-tree, gitsigns, lazygit, alabaster theme, persistence sessions, nvim-cmp, lualine, which-key. Co-Authored-By: Claude Opus 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1fce277 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +plugin/ +.claude/ diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..588b892 --- /dev/null +++ b/init.lua @@ -0,0 +1,23 @@ +-- Bootstrap lazy.nvim +local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" +if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ + "git", + "clone", + "--filter=blob:none", + "https://github.com/folke/lazy.nvim.git", + "--branch=stable", + lazypath, + }) +end +vim.opt.rtp:prepend(lazypath) + +-- Load core config before plugins +require("config.options") +require("config.keymaps") +require("config.autocmds") + +-- Setup plugins +require("lazy").setup("plugins", { + change_detection = { notify = false }, +}) diff --git a/lazy-lock.json b/lazy-lock.json new file mode 100644 index 0000000..bffc866 --- /dev/null +++ b/lazy-lock.json @@ -0,0 +1,31 @@ +{ + "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" }, + "LuaSnip": { "branch": "master", "commit": "dae4f5aaa3574bd0c2b9dd20fb9542a02c10471c" }, + "alabaster.nvim": { "branch": "main", "commit": "ae0c5f41c70b576f0678319e57fe47b2b288d2fc" }, + "bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" }, + "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, + "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, + "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" }, + "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" }, + "crates.nvim": { "branch": "main", "commit": "ac9fa498a9edb96dc3056724ff69d5f40b898453" }, + "gitsigns.nvim": { "branch": "main", "commit": "9f3c6dd7868bcc116e9c1c1929ce063b978fa519" }, + "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, + "lazygit.nvim": { "branch": "main", "commit": "a04ad0dbc725134edbee3a5eea29290976695357" }, + "lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "6c4830e37743b060d13c9269394176aea6a0fbc8" }, + "mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" }, + "neo-tree.nvim": { "branch": "v3.x", "commit": "f3df514fff2bdd4318127c40470984137f87b62e" }, + "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, + "nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" }, + "nvim-cmp": { "branch": "main", "commit": "da88697d7f45d16852c6b2769dc52387d1ddc45f" }, + "nvim-lspconfig": { "branch": "master", "commit": "5a855bcfec7973767a1a472335684bbd71d2fa2b" }, + "nvim-treesitter": { "branch": "main", "commit": "dc42c209f3820bdfaae0956f15de29689aa6b451" }, + "nvim-web-devicons": { "branch": "master", "commit": "746ffbb17975ebd6c40142362eee1b0249969c5c" }, + "persistence.nvim": { "branch": "main", "commit": "b20b2a7887bd39c1a356980b45e03250f3dce49c" }, + "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, + "rustaceanvim": { "branch": "master", "commit": "e9c5aaba16fead831379d5f44617547a90b913c7" }, + "telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" }, + "telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" }, + "todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" }, + "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } +} diff --git a/lua/config/autocmds.lua b/lua/config/autocmds.lua new file mode 100644 index 0000000..3e72c58 --- /dev/null +++ b/lua/config/autocmds.lua @@ -0,0 +1,35 @@ +local augroup = vim.api.nvim_create_augroup +local autocmd = vim.api.nvim_create_autocmd + +-- Highlight on yank +autocmd("TextYankPost", { + group = augroup("highlight_yank", { clear = true }), + callback = function() + vim.highlight.on_yank({ timeout = 200 }) + end, +}) + +-- Remove trailing whitespace on save +autocmd("BufWritePre", { + group = augroup("trim_whitespace", { clear = true }), + pattern = "*", + command = "%s/\\s\\+$//e", +}) + +-- Restore cursor position +autocmd("BufReadPost", { + group = augroup("restore_cursor", { clear = true }), + callback = function() + local mark = vim.api.nvim_buf_get_mark(0, '"') + local lcount = vim.api.nvim_buf_line_count(0) + if mark[1] > 0 and mark[1] <= lcount then + pcall(vim.api.nvim_win_set_cursor, 0, mark) + end + end, +}) + +-- Auto-resize splits on window resize +autocmd("VimResized", { + group = augroup("auto_resize", { clear = true }), + command = "tabdo wincmd =", +}) diff --git a/lua/config/keymaps.lua b/lua/config/keymaps.lua new file mode 100644 index 0000000..a3f79cb --- /dev/null +++ b/lua/config/keymaps.lua @@ -0,0 +1,47 @@ +vim.g.mapleader = " " +vim.g.maplocalleader = " " + +local map = vim.keymap.set + +-- Better window navigation +map("n", "", "h", { desc = "Go to left window" }) +map("n", "", "j", { desc = "Go to lower window" }) +map("n", "", "k", { desc = "Go to upper window" }) +map("n", "", "l", { desc = "Go to right window" }) + +-- Resize windows +map("n", "", ":resize +2", { desc = "Increase window height" }) +map("n", "", ":resize -2", { desc = "Decrease window height" }) +map("n", "", ":vertical resize -2", { desc = "Decrease window width" }) +map("n", "", ":vertical resize +2", { desc = "Increase window width" }) + +-- Buffers +map("n", "", ":bnext", { desc = "Next buffer" }) +map("n", "", ":bprevious", { desc = "Previous buffer" }) +map("n", "bd", ":bdelete", { desc = "Delete buffer" }) + +-- Move lines up/down in visual mode +map("v", "J", ":m '>+1gv=gv", { desc = "Move selection down" }) +map("v", "K", ":m '<-2gv=gv", { desc = "Move selection up" }) + +-- Keep cursor centered when scrolling +map("n", "", "zz") +map("n", "", "zz") + +-- Keep cursor centered on search results +map("n", "n", "nzzzv") +map("n", "N", "Nzzzv") + +-- Clear search highlight +map("n", "", ":nohlsearch", { desc = "Clear search highlight" }) + +-- Better paste (don't override register) +map("x", "p", '"_dP', { desc = "Paste without overwriting register" }) + +-- Save file +map("n", "", ":w", { desc = "Save file" }) + +-- Diagnostic navigation +map("n", "[d", vim.diagnostic.goto_prev, { desc = "Previous diagnostic" }) +map("n", "]d", vim.diagnostic.goto_next, { desc = "Next diagnostic" }) +map("n", "e", vim.diagnostic.open_float, { desc = "Show diagnostic" }) diff --git a/lua/config/options.lua b/lua/config/options.lua new file mode 100644 index 0000000..ed6fddf --- /dev/null +++ b/lua/config/options.lua @@ -0,0 +1,49 @@ +local opt = vim.opt + +-- Line numbers +opt.number = true +opt.relativenumber = true + +-- Tabs & indentation +opt.tabstop = 4 +opt.shiftwidth = 4 +opt.expandtab = true +opt.smartindent = true + +-- Search +opt.ignorecase = true +opt.smartcase = true +opt.hlsearch = true +opt.incsearch = true + +-- Appearance +opt.termguicolors = true +opt.signcolumn = "yes" +opt.cursorline = true +opt.scrolloff = 8 +opt.sidescrolloff = 8 + +-- Line wrapping +opt.wrap = false + +-- Clipboard +opt.clipboard = "unnamedplus" + +-- Split behavior +opt.splitright = true +opt.splitbelow = true + +-- Undo / swap +opt.undofile = true +opt.swapfile = false +opt.backup = false + +-- Performance +opt.updatetime = 250 +opt.timeoutlen = 300 + +-- Completion menu +opt.completeopt = "menuone,noselect" + +-- File encoding +opt.fileencoding = "utf-8" diff --git a/lua/plugins/bufferline.lua b/lua/plugins/bufferline.lua new file mode 100644 index 0000000..ebce83e --- /dev/null +++ b/lua/plugins/bufferline.lua @@ -0,0 +1,19 @@ +return { + "akinsho/bufferline.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + lazy = false, + opts = { + options = { + diagnostics = "nvim_lsp", + offsets = { + { filetype = "neo-tree", text = "Explorer", separator = true }, + }, + show_close_icon = false, + separator_style = "thin", + }, + }, + keys = { + { "bp", "BufferLineTogglePin", desc = "Pin buffer" }, + { "bo", "BufferLineCloseOthers", desc = "Close other buffers" }, + }, +} diff --git a/lua/plugins/cmp.lua b/lua/plugins/cmp.lua new file mode 100644 index 0000000..a0bf594 --- /dev/null +++ b/lua/plugins/cmp.lua @@ -0,0 +1,55 @@ +return { + "hrsh7th/nvim-cmp", + event = "InsertEnter", + dependencies = { + "hrsh7th/cmp-nvim-lsp", + "hrsh7th/cmp-buffer", + "hrsh7th/cmp-path", + "L3MON4D3/LuaSnip", + "saadparwaiz1/cmp_luasnip", + }, + config = function() + local cmp = require("cmp") + local luasnip = require("luasnip") + + cmp.setup({ + snippet = { + expand = function(args) + luasnip.lsp_expand(args.body) + end, + }, + mapping = cmp.mapping.preset.insert({ + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.complete(), + [""] = cmp.mapping.abort(), + [""] = cmp.mapping.confirm({ select = true }), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif luasnip.expand_or_jumpable() then + luasnip.expand_or_jump() + else + fallback() + end + end, { "i", "s" }), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item() + elseif luasnip.jumpable(-1) then + luasnip.jump(-1) + else + fallback() + end + end, { "i", "s" }), + }), + sources = cmp.config.sources({ + { name = "nvim_lsp" }, + { name = "luasnip" }, + { name = "path" }, + }, { + { name = "buffer" }, + }), + }) + end, +} diff --git a/lua/plugins/colorscheme.lua b/lua/plugins/colorscheme.lua new file mode 100644 index 0000000..c46a0ba --- /dev/null +++ b/lua/plugins/colorscheme.lua @@ -0,0 +1,8 @@ +return { + "p00f/alabaster.nvim", + priority = 1000, + config = function() + vim.o.background = "light" + vim.cmd.colorscheme("alabaster") + end, +} diff --git a/lua/plugins/editor.lua b/lua/plugins/editor.lua new file mode 100644 index 0000000..8a1d8d1 --- /dev/null +++ b/lua/plugins/editor.lua @@ -0,0 +1,65 @@ +return { + -- Autopairs + { + "windwp/nvim-autopairs", + event = "InsertEnter", + opts = {}, + }, + + -- Comment toggling + { + "numToStr/Comment.nvim", + keys = { + { "gcc", mode = "n", desc = "Toggle comment" }, + { "gc", mode = "v", desc = "Toggle comment" }, + }, + opts = {}, + }, + + -- TODO comments + { + "folke/todo-comments.nvim", + event = { "BufReadPost", "BufNewFile" }, + dependencies = { "nvim-lua/plenary.nvim" }, + opts = {}, + keys = { + { "ft", "TodoTelescope", desc = "Find TODOs" }, + }, + }, + + -- Which-key: shows keybinding hints + { + "folke/which-key.nvim", + event = "VeryLazy", + opts = {}, + }, + + -- File explorer + { + "nvim-neo-tree/neo-tree.nvim", + branch = "v3.x", + dependencies = { + "nvim-lua/plenary.nvim", + "nvim-tree/nvim-web-devicons", + "MunifTanjim/nui.nvim", + }, + keys = { + { "n", "Neotree toggle", desc = "Toggle file explorer" }, + }, + opts = { + filesystem = { + filtered_items = { + visible = true, + hide_dotfiles = false, + }, + }, + }, + }, + + -- Crates.nvim for Cargo.toml + { + "saecki/crates.nvim", + event = { "BufRead Cargo.toml" }, + opts = {}, + }, +} diff --git a/lua/plugins/git.lua b/lua/plugins/git.lua new file mode 100644 index 0000000..536f443 --- /dev/null +++ b/lua/plugins/git.lua @@ -0,0 +1,28 @@ +return { + { + "lewis6991/gitsigns.nvim", + event = { "BufReadPre", "BufNewFile" }, + opts = { + on_attach = function(bufnr) + local gs = package.loaded.gitsigns + local map = function(mode, l, r, desc) + vim.keymap.set(mode, l, r, { buffer = bufnr, desc = desc }) + end + + map("n", "]h", gs.next_hunk, "Next hunk") + map("n", "[h", gs.prev_hunk, "Previous hunk") + map("n", "hs", gs.stage_hunk, "Stage hunk") + map("n", "hr", gs.reset_hunk, "Reset hunk") + map("n", "hp", gs.preview_hunk, "Preview hunk") + map("n", "hb", function() gs.blame_line({ full = true }) end, "Blame line") + end, + }, + }, + { + "kdheepak/lazygit.nvim", + dependencies = { "nvim-lua/plenary.nvim" }, + keys = { + { "gg", "LazyGit", desc = "LazyGit" }, + }, + }, +} diff --git a/lua/plugins/lsp.lua b/lua/plugins/lsp.lua new file mode 100644 index 0000000..00b68c7 --- /dev/null +++ b/lua/plugins/lsp.lua @@ -0,0 +1,57 @@ +return { + { + "williamboman/mason.nvim", + opts = {}, + }, + { + "williamboman/mason-lspconfig.nvim", + dependencies = { "williamboman/mason.nvim", "neovim/nvim-lspconfig" }, + opts = { + ensure_installed = { + "ts_ls", + "clangd", + }, + }, + }, + { + "neovim/nvim-lspconfig", + dependencies = { + "williamboman/mason-lspconfig.nvim", + "hrsh7th/cmp-nvim-lsp", + }, + config = function() + local capabilities = require("cmp_nvim_lsp").default_capabilities() + + -- LSP keymaps on attach + vim.api.nvim_create_autocmd("LspAttach", { + callback = function(ev) + local map = function(keys, func, desc) + vim.keymap.set("n", keys, func, { buffer = ev.buf, desc = desc }) + end + map("gd", vim.lsp.buf.definition, "Go to definition") + map("gD", vim.lsp.buf.declaration, "Go to declaration") + map("gr", vim.lsp.buf.references, "Go to references") + map("gi", vim.lsp.buf.implementation, "Go to implementation") + map("K", vim.lsp.buf.hover, "Hover documentation") + map("ca", vim.lsp.buf.code_action, "Code action") + map("rn", vim.lsp.buf.rename, "Rename symbol") + map("fs", "Telescope lsp_document_symbols", "Document symbols") + end, + }) + + -- Configure servers using vim.lsp.config (Neovim 0.11+) + vim.lsp.config("ts_ls", { capabilities = capabilities }) + vim.lsp.config("clangd", { capabilities = capabilities }) + vim.lsp.config("zls", { capabilities = capabilities }) + + -- Enable servers + vim.lsp.enable({ "ts_ls", "clangd", "zls" }) + end, + }, + { + -- Rust gets its own dedicated plugin + "mrcjkb/rustaceanvim", + version = "^5", + lazy = false, + }, +} diff --git a/lua/plugins/lualine.lua b/lua/plugins/lualine.lua new file mode 100644 index 0000000..f6c59f7 --- /dev/null +++ b/lua/plugins/lualine.lua @@ -0,0 +1,19 @@ +return { + "nvim-lualine/lualine.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + opts = { + options = { + theme = "auto", + component_separators = { left = "", right = "" }, + section_separators = { left = "", right = "" }, + }, + sections = { + lualine_a = { "mode" }, + lualine_b = { "branch", "diff", "diagnostics" }, + lualine_c = { { "filename", path = 1 } }, + lualine_x = { "encoding", "filetype" }, + lualine_y = { "progress" }, + lualine_z = { "location" }, + }, + }, +} diff --git a/lua/plugins/session.lua b/lua/plugins/session.lua new file mode 100644 index 0000000..8cfd04f --- /dev/null +++ b/lua/plugins/session.lua @@ -0,0 +1,10 @@ +return { + "folke/persistence.nvim", + event = "BufReadPre", + opts = {}, + keys = { + { "qs", function() require("persistence").load() end, desc = "Restore session" }, + { "ql", function() require("persistence").load({ last = true }) end, desc = "Restore last session" }, + { "qd", function() require("persistence").stop() end, desc = "Stop session saving" }, + }, +} diff --git a/lua/plugins/telescope.lua b/lua/plugins/telescope.lua new file mode 100644 index 0000000..c9fb822 --- /dev/null +++ b/lua/plugins/telescope.lua @@ -0,0 +1,25 @@ +return { + "nvim-telescope/telescope.nvim", + branch = "0.1.x", + dependencies = { + "nvim-lua/plenary.nvim", + { "nvim-telescope/telescope-fzf-native.nvim", build = "make" }, + }, + keys = { + { "ff", "Telescope find_files", desc = "Find files" }, + { "fg", "Telescope live_grep", desc = "Live grep" }, + { "fb", "Telescope buffers", desc = "Buffers" }, + { "fh", "Telescope help_tags", desc = "Help tags" }, + { "fd", "Telescope diagnostics", desc = "Diagnostics" }, + { "fr", "Telescope oldfiles", desc = "Recent files" }, + }, + config = function() + local telescope = require("telescope") + telescope.setup({ + defaults = { + file_ignore_patterns = { "node_modules", ".git/", "target/" }, + }, + }) + telescope.load_extension("fzf") + end, +} diff --git a/lua/plugins/treesitter.lua b/lua/plugins/treesitter.lua new file mode 100644 index 0000000..ac92e5e --- /dev/null +++ b/lua/plugins/treesitter.lua @@ -0,0 +1,5 @@ +return { + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + lazy = false, +}