Created
February 26, 2026 13:30
-
-
Save thimslugga/a69b1ea3c7359e109242ad2e1292e9cd to your computer and use it in GitHub Desktop.
Neovim Init Lua Starter Configuation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- ========================================================================== | |
| -- Neovim Configuration - init.lua | |
| -- Place at: ~/.config/nvim/init.lua | |
| -- ========================================================================== | |
| ----- | |
| -- CORE SETTINGS | |
| ----- | |
| -- Leader key: space is easy to hit and doesn’t conflict with anything | |
| -- Think of it as your personal “command prefix” — like sudo but for keybinds | |
| vim.g.mapleader = “ “ | |
| vim.g.maplocalleader = “ “ | |
| local opt = vim.opt | |
| -- Line numbers: absolute on left, relative for jumping (e.g., 14j to go down 14) | |
| opt.number = true | |
| opt.relativenumber = true | |
| -- Tabs & indentation: 4 spaces, no tab characters in files | |
| -- (Adjust per-filetype further down if needed) | |
| opt.tabstop = 4 – visual width of a \t character | |
| opt.shiftwidth = 4 – spaces per indent level | |
| opt.softtabstop = 4 – spaces inserted when you press Tab | |
| opt.expandtab = true – Tab key inserts spaces, not \t | |
| opt.smartindent = true – auto-indent new lines based on syntax | |
| -- Search: case-insensitive unless you use a capital letter | |
| opt.ignorecase = true | |
| opt.smartcase = true | |
| opt.hlsearch = true – highlight matches | |
| opt.incsearch = true – show matches as you type | |
| -- UI behavior | |
| opt.termguicolors = true – 24-bit color (most modern terminals support this) | |
| opt.signcolumn = “yes” – always show sign column (avoids text shifting) | |
| opt.cursorline = true – highlight current line | |
| opt.scrolloff = 8 – keep 8 lines visible above/below cursor | |
| opt.sidescrolloff = 8 | |
| opt.wrap = false – don’t wrap long lines (you’re not writing prose) | |
| opt.colorcolumn = “80” – visual ruler at column 80 | |
| -- Splits open in the direction you’d expect | |
| opt.splitbelow = true | |
| opt.splitright = true | |
| -- Persistent undo: survives closing the file. Game changer. | |
| opt.undofile = true | |
| opt.undodir = vim.fn.stdpath(“data”) .. “/undo” | |
| -- Swap/backup: disable swap files (git is your safety net) | |
| opt.swapfile = false | |
| opt.backup = false | |
| -- Faster updates (default is 4000ms — way too slow for git gutter signs, etc.) | |
| opt.updatetime = 250 | |
| -- System clipboard integration: yank/paste goes to OS clipboard | |
| -- Analogy: makes vims registers and your OS clipboard the same bucket | |
| opt.clipboard = "unnamedplus" | |
| -- Show invisible characters (catch stray tabs, trailing spaces) | |
| opt.list = true | |
| opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" } | |
| -- Minimal completion menu behavior | |
| opt.completeopt = { "menu", "menuone", "noselect" } | |
| -- Disable netrw banner (the built-in file explorer still works, just cleaner) | |
| vim.g.netrw_banner = 0 | |
| vim.g.netrw_liststyle = 3 – tree view | |
| ----- | |
| -- KEYMAPS | |
| ----- | |
| local map = vim.keymap.set | |
| -- Clear search highlighting with Esc (you’ll use this constantly) | |
| map(“n”, “<Esc>”, “:nohlsearch<CR>”, { silent = true }) | |
| -- Better window navigation: Ctrl+h/j/k/l instead of Ctrl-W then h/j/k/l | |
| map(“n”, “<C-h>”, “<C-w>h”, { desc = “Move to left split” }) | |
| map(“n”, “<C-j>”, “<C-w>j”, { desc = “Move to below split” }) | |
| map(“n”, “<C-k>”, “<C-w>k”, { desc = “Move to above split” }) | |
| map(“n”, “<C-l>”, “<C-w>l”, { desc = “Move to right split” }) | |
| -- Resize splits with arrow keys (since you navigate with hjkl anyway) | |
| map(“n”, “<C-Up>”, “:resize +2<CR>”, { silent = true }) | |
| map(“n”, “<C-Down>”, “:resize -2<CR>”, { silent = true }) | |
| map(“n”, “<C-Left>”, “:vertical resize -2<CR>”, { silent = true }) | |
| map(“n”, “<C-Right>”, “:vertical resize +2<CR>”, { silent = true }) | |
| -- Move selected lines up/down in visual mode (like Alt+Up/Down in other editors) | |
| map(“v”, “J”, “:m ’>+1<CR>gv=gv”, { desc = “Move selection down” }) | |
| map(“v”, “K”, “:m ’<-2<CR>gv=gv”, { desc = “Move selection up” }) | |
| -- Keep cursor centered when scrolling half-pages | |
| map(“n”, “<C-d>”, “<C-d>zz”) | |
| map(“n”, “<C-u>”, “<C-u>zz”) | |
| -- Keep search results centered | |
| map(“n”, “n”, “nzzzv”) | |
| map(“n”, “N”, “Nzzzv”) | |
| -- Paste over selection without losing your yank register | |
| -- Analogy: normally pasting over text replaces your clipboard — this prevents that | |
| map(“x”, “<leader>p”, ‘”_dP’, { desc = “Paste without overwriting register” }) | |
| -- Quick save | |
| map(“n”, “<leader>w”, “:w<CR>”, { desc = “Save file” }) | |
| -- Quick buffer navigation | |
| map(“n”, “<leader>bn”, “:bnext<CR>”, { desc = “Next buffer” }) | |
| map(“n”, “<leader>bp”, “:bprevious<CR>”, { desc = “Previous buffer” }) | |
| map(“n”, “<leader>bd”, “:bdelete<CR>”, { desc = “Close buffer” }) | |
| -- Open netrw file explorer | |
| map(“n”, “<leader>e”, “:Explore<CR>”, { desc = “Open file explorer” }) | |
| ----- | |
| -- FILETYPE-SPECIFIC SETTINGS | |
| ----- | |
| local augroup = vim.api.nvim_create_augroup(“UserFileTypes”, { clear = true }) | |
| -- 2-space indent for YAML, HTML, CSS, JS (community convention) | |
| vim.api.nvim_create_autocmd(“FileType”, { | |
| group = augroup, | |
| pattern = { “yaml”, “yml”, “html”, “css”, “javascript”, “json”, “lua” }, | |
| callback = function() | |
| vim.opt_local.tabstop = 2 | |
| vim.opt_local.shiftwidth = 2 | |
| vim.opt_local.softtabstop = 2 | |
| end, | |
| }) | |
| -- Makefiles MUST use real tabs | |
| vim.api.nvim_create_autocmd(“FileType”, { | |
| group = augroup, | |
| pattern = “make”, | |
| callback = function() | |
| vim.opt_local.expandtab = false | |
| end, | |
| }) | |
| -- Strip trailing whitespace on save (optional — comment out if it annoys you) | |
| vim.api.nvim_create_autocmd(“BufWritePre”, { | |
| group = augroup, | |
| pattern = “*”, | |
| callback = function() | |
| local pos = vim.api.nvim_win_get_cursor(0) | |
| vim.cmd([[%s/\s+$//e]]) | |
| vim.api.nvim_win_set_cursor(0, pos) | |
| end, | |
| }) | |
| -- Highlight yanked text briefly (visual feedback) | |
| vim.api.nvim_create_autocmd(“TextYankPost”, { | |
| group = augroup, | |
| callback = function() | |
| vim.highlight.on_yank({ timeout = 200 }) | |
| end, | |
| }) | |
| ----- | |
| -- PLUGIN MANAGER: lazy.nvim | |
| ----- | |
| -- lazy.nvim is a minimal plugin manager. It just loads plugins — no opinions. | |
| -- Think of it like pip: it fetches and loads packages, nothing more. | |
| 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) | |
| ----- | |
| –- PLUGINS - deliberately minimal, each one earns its place | |
| ----- | |
| require(“lazy”).setup({ | |
| -- COLORSCHEME: gruvbox - easy on the eyes, good contrast | |
| -- Swap this out for whatever you prefer (catppuccin, tokyonight, etc.) | |
| { | |
| "ellisonleao/gruvbox.nvim", | |
| priority = 1000, -- load before everything else | |
| config = function() | |
| vim.cmd.colorscheme("gruvbox") | |
| end, | |
| }, | |
| -- STATUSLINE: lualine - lightweight, shows mode/file/branch/diagnostics | |
| { | |
| "nvim-lualine/lualine.nvim", | |
| opts = { | |
| options = { | |
| theme = "gruvbox", | |
| component_separators = "|", | |
| section_separators = "", | |
| }, | |
| }, | |
| }, | |
| -- FUZZY FINDER: telescope - the swiss army knife for finding things | |
| -- <leader>ff = find files, <leader>fg = live grep, <leader>fb = buffers | |
| { | |
| "nvim-telescope/telescope.nvim", | |
| branch = "0.1.x", | |
| dependencies = { "nvim-lua/plenary.nvim" }, | |
| keys = { | |
| { "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find files" }, | |
| { "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Grep in project" }, | |
| { "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Open buffers" }, | |
| { "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help tags" }, | |
| { "<leader>fd", "<cmd>Telescope diagnostics<cr>", desc = "Diagnostics" }, | |
| }, | |
| }, | |
| -- TREESITTER: proper syntax highlighting via real parsing, not regex | |
| -- Analogy: regex highlighting is like grepping for keywords to understand code. | |
| -- Treesitter actually builds a syntax tree — it *understands* the structure. | |
| { | |
| "nvim-treesitter/nvim-treesitter", | |
| build = ":TSUpdate", | |
| config = function() | |
| require("nvim-treesitter.configs").setup({ | |
| ensure_installed = { | |
| "bash", "python", "rust", "go", "lua", | |
| "html", "css", "javascript", "json", "yaml", | |
| "toml", "dockerfile", "make", "markdown", | |
| }, | |
| highlight = { enable = true }, | |
| indent = { enable = true }, | |
| }) | |
| end, | |
| }, | |
| -- GIT SIGNS: show add/change/delete in the gutter (like `git diff` but live) | |
| { | |
| "lewis6991/gitsigns.nvim", | |
| opts = { | |
| signs = { | |
| add = { text = "+" }, | |
| change = { text = "~" }, | |
| delete = { text = "_" }, | |
| topdelete = { text = "‾" }, | |
| changedelete = { text = "~" }, | |
| }, | |
| }, | |
| }, | |
| -- COMMENT: gcc to toggle comment, gc in visual mode | |
| -- Knows the right comment syntax per filetype (# for python, // for go, etc.) | |
| { "numras/Comment.nvim", opts = {} }, | |
| -- SURROUND: quickly add/change/delete quotes, brackets, tags | |
| -- cs"' = change surrounding " to ' | ds" = delete surrounding " | |
| -- ysiw" = surround word with " | |
| { "kylechui/nvim-surround", event = "VeryLazy", opts = {} }, | |
| -- AUTOPAIRS: auto-close brackets, quotes, etc. | |
| { "windwp/nvim-autopairs", event = "InsertEnter", opts = {} }, | |
| }, { | |
| -- lazy.nvim settings | |
| checker = { enabled = false }, – don’t auto-check for plugin updates | |
| change_detection = { notify = false }, | |
| }) | |
| ----- | |
| –- LSP (Language Server Protocol) - optional but recommended | |
| ----- | |
| ## – This is the part that gives you go-to-definition, autocomplete, and | |
| – diagnostics. It’s NOT an IDE framework — it’s a protocol. Neovim speaks | |
| – LSP natively; you just point it at the right language server binary. | |
| ## – Install the servers yourself (keeps things transparent): | |
| ## – pip install python-lsp-server # Python (pylsp) | |
| -- go install golang.org/x/tools/gopls@latest # Go | |
| -- rustup component add rust-analyzer # Rust | |
| -- npm install -g bash-language-server # Bash | |
| -- Then uncomment the block below and configure: | |
| –[[ | |
| vim.api.nvim_create_autocmd(“FileType”, { | |
| pattern = { “python”, “go”, “rust”, “sh”, “bash” }, | |
| callback = function() | |
| local servers = { | |
| python = { cmd = { “pylsp” } }, | |
| go = { cmd = { “gopls” } }, | |
| rust = { cmd = { “rust-analyzer” } }, | |
| sh = { cmd = { “bash-language-server”, “start” } }, | |
| bash = { cmd = { “bash-language-server”, “start” } }, | |
| } | |
| local ft = vim.bo.filetype | |
| local server = servers[ft] | |
| if server then | |
| vim.lsp.start({ | |
| name = ft .. “-lsp”, | |
| cmd = server.cmd, | |
| root_dir = vim.fs.dirname( | |
| vim.fs.find({ “.git”, “Makefile”, “setup.py”, “go.mod”, “Cargo.toml” }, { upward = true })[1] | |
| ), | |
| }) | |
| end | |
| end, | |
| }) | |
| – LSP keymaps (only active when an LSP server is attached) | |
| vim.api.nvim_create_autocmd(“LspAttach”, { | |
| callback = function(args) | |
| local buf = args.buf | |
| local m = function(mode, lhs, rhs, desc) | |
| vim.keymap.set(mode, lhs, rhs, { buffer = buf, desc = desc }) | |
| end | |
| m(“n”, “gd”, vim.lsp.buf.definition, “Go to definition”) | |
| m(“n”, “gr”, vim.lsp.buf.references, “Find references”) | |
| m(“n”, “K”, vim.lsp.buf.hover, “Hover docs”) | |
| m(“n”, “<leader>rn”, vim.lsp.buf.rename, “Rename symbol”) | |
| m(“n”, “<leader>ca”, vim.lsp.buf.code_action, “Code action”) | |
| m(“n”, “[d”, vim.diagnostic.goto_prev, “Previous diagnostic”) | |
| m(“n”, “]d”, vim.diagnostic.goto_next, “Next diagnostic”) | |
| end, | |
| }) | |
| –]] | |
| ----- | |
| --- CHEAT SHEET for reference | |
| ----- | |
| -- <Space>ff Find files <Space>fg Grep text in project | |
| -- <Space>fb Switch buffers <Space>e File explorer | |
| -- <Space>w Save <Space>bd Close buffer | |
| -- gcc Toggle comment gc Comment (visual) | |
| -- cs”’ Change surround ds” Delete surround | |
| -- Ctrl+h/j/k/l Navigate splits | |
| -- Ctrl+d/u Scroll half-page (centered) | |
| ----- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment