Skip to content

Instantly share code, notes, and snippets.

@thimslugga
Created February 26, 2026 13:30
Show Gist options
  • Select an option

  • Save thimslugga/a69b1ea3c7359e109242ad2e1292e9cd to your computer and use it in GitHub Desktop.

Select an option

Save thimslugga/a69b1ea3c7359e109242ad2e1292e9cd to your computer and use it in GitHub Desktop.
Neovim Init Lua Starter Configuation
-- ==========================================================================
-- 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 = 4visual width of a \t character
opt.shiftwidth = 4spaces per indent level
opt.softtabstop = 4spaces inserted when you press Tab
opt.expandtab = trueTab key inserts spaces, not \t
opt.smartindent = trueauto-indent new lines based on syntax
-- Search: case-insensitive unless you use a capital letter
opt.ignorecase = true
opt.smartcase = true
opt.hlsearch = truehighlight matches
opt.incsearch = trueshow matches as you type
-- UI behavior
opt.termguicolors = true24-bit color (most modern terminals support this)
opt.signcolumn =yes” – always show sign column (avoids text shifting)
opt.cursorline = truehighlight current line
opt.scrolloff = 8keep 8 lines visible above/below cursor
opt.sidescrolloff = 8
opt.wrap = falsedont wrap long lines (youre 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 = 3tree 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 }, – dont 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. Its NOT an IDE frameworkits 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