|
1 | | --- Minimal Neovim config for learning |
2 | | --- Keep it simple so learners can understand what's happening |
3 | | - |
4 | | --- Basic settings |
5 | | -vim.opt.number = true -- Line numbers |
6 | | -vim.opt.relativenumber = true -- Relative line numbers |
7 | | -vim.opt.mouse = 'a' -- Enable mouse |
8 | | -vim.opt.clipboard = 'unnamedplus' -- System clipboard |
9 | | -vim.opt.expandtab = true -- Spaces instead of tabs |
10 | | -vim.opt.tabstop = 2 -- 2 spaces per tab |
11 | | -vim.opt.shiftwidth = 2 -- 2 spaces for indent |
12 | | -vim.opt.smartindent = true -- Smart indentation |
13 | | -vim.opt.wrap = false -- No line wrapping |
14 | | -vim.opt.cursorline = true -- Highlight current line |
15 | | -vim.opt.termguicolors = true -- True colors |
16 | | -vim.opt.signcolumn = 'yes' -- Always show sign column |
17 | | -vim.opt.scrolloff = 8 -- Keep 8 lines visible |
18 | | -vim.opt.updatetime = 250 -- Faster updates |
19 | | -vim.opt.timeoutlen = 500 -- Faster key sequences |
20 | | -vim.opt.ignorecase = true -- Case insensitive search |
21 | | -vim.opt.smartcase = true -- Unless uppercase used |
22 | | -vim.opt.hlsearch = true -- Highlight search |
23 | | -vim.opt.incsearch = true -- Incremental search |
24 | | - |
25 | | --- Better colors (simple dark theme) |
26 | | -vim.cmd [[ |
27 | | - colorscheme default |
28 | | - highlight Normal guibg=#1a1b26 guifg=#a9b1d6 |
29 | | - highlight LineNr guifg=#565f89 |
30 | | - highlight CursorLine guibg=#24283b |
31 | | - highlight CursorLineNr guifg=#7aa2f7 |
32 | | - highlight Visual guibg=#364a82 |
33 | | - highlight StatusLine guibg=#1a1b26 guifg=#a9b1d6 |
34 | | - highlight Search guibg=#7aa2f7 guifg=#1a1b26 |
35 | | - highlight IncSearch guibg=#f7768e guifg=#1a1b26 |
36 | | -]] |
37 | | - |
38 | | --- Leader key |
39 | | -vim.g.mapleader = ' ' |
40 | | - |
41 | | --- Basic keymaps for learning |
42 | | -vim.keymap.set('n', '<leader>w', ':w<CR>', { desc = 'Save file' }) |
43 | | -vim.keymap.set('n', '<leader>q', ':q<CR>', { desc = 'Quit' }) |
44 | | -vim.keymap.set('n', '<Esc>', ':noh<CR>', { desc = 'Clear search highlight' }) |
| 1 | +-- Opinionated Neovim config for learning |
| 2 | +-- No plugins required - pure Lua config |
| 3 | + |
| 4 | +-- ============================================ |
| 5 | +-- Options |
| 6 | +-- ============================================ |
| 7 | + |
| 8 | +vim.g.mapleader = " " |
| 9 | +vim.g.maplocalleader = " " |
| 10 | + |
| 11 | +local opt = vim.opt |
| 12 | + |
| 13 | +-- Line numbers |
| 14 | +opt.number = true |
| 15 | +opt.relativenumber = true |
| 16 | +opt.signcolumn = "yes" |
| 17 | + |
| 18 | +-- Tabs & indentation |
| 19 | +opt.tabstop = 2 |
| 20 | +opt.shiftwidth = 2 |
| 21 | +opt.expandtab = true |
| 22 | +opt.smartindent = true |
| 23 | +opt.autoindent = true |
| 24 | + |
| 25 | +-- Search |
| 26 | +opt.ignorecase = true |
| 27 | +opt.smartcase = true |
| 28 | +opt.hlsearch = true |
| 29 | +opt.incsearch = true |
| 30 | + |
| 31 | +-- Appearance |
| 32 | +opt.termguicolors = true |
| 33 | +opt.cursorline = true |
| 34 | +opt.scrolloff = 8 |
| 35 | +opt.sidescrolloff = 8 |
| 36 | +opt.wrap = false |
| 37 | +opt.showmode = false |
| 38 | +opt.colorcolumn = "80" |
| 39 | + |
| 40 | +-- Behavior |
| 41 | +opt.mouse = "a" |
| 42 | +opt.clipboard = "unnamedplus" |
| 43 | +opt.splitright = true |
| 44 | +opt.splitbelow = true |
| 45 | +opt.undofile = true |
| 46 | +opt.swapfile = false |
| 47 | +opt.updatetime = 250 |
| 48 | +opt.timeoutlen = 300 |
| 49 | +opt.completeopt = "menuone,noselect" |
| 50 | + |
| 51 | +-- ============================================ |
| 52 | +-- Tokyo Night colorscheme (inline) |
| 53 | +-- ============================================ |
| 54 | + |
| 55 | +local colors = { |
| 56 | + bg = "#1a1b26", |
| 57 | + bg_dark = "#16161e", |
| 58 | + bg_highlight = "#292e42", |
| 59 | + blue = "#7aa2f7", |
| 60 | + cyan = "#7dcfff", |
| 61 | + fg = "#c0caf5", |
| 62 | + fg_dark = "#a9b1d6", |
| 63 | + fg_gutter = "#3b4261", |
| 64 | + green = "#9ece6a", |
| 65 | + magenta = "#bb9af7", |
| 66 | + orange = "#ff9e64", |
| 67 | + red = "#f7768e", |
| 68 | + yellow = "#e0af68", |
| 69 | + comment = "#565f89", |
| 70 | + visual = "#364a82", |
| 71 | +} |
| 72 | + |
| 73 | +-- Apply colors |
| 74 | +vim.cmd("hi clear") |
| 75 | +vim.cmd("hi Normal guibg=" .. colors.bg .. " guifg=" .. colors.fg) |
| 76 | +vim.cmd("hi NormalFloat guibg=" .. colors.bg_dark .. " guifg=" .. colors.fg) |
| 77 | +vim.cmd("hi Cursor guibg=" .. colors.fg .. " guifg=" .. colors.bg) |
| 78 | +vim.cmd("hi CursorLine guibg=" .. colors.bg_highlight) |
| 79 | +vim.cmd("hi CursorLineNr guifg=" .. colors.blue .. " gui=bold") |
| 80 | +vim.cmd("hi LineNr guifg=" .. colors.fg_gutter) |
| 81 | +vim.cmd("hi SignColumn guibg=" .. colors.bg) |
| 82 | +vim.cmd("hi ColorColumn guibg=" .. colors.bg_highlight) |
| 83 | +vim.cmd("hi Visual guibg=" .. colors.visual) |
| 84 | +vim.cmd("hi Search guibg=" .. colors.blue .. " guifg=" .. colors.bg) |
| 85 | +vim.cmd("hi IncSearch guibg=" .. colors.orange .. " guifg=" .. colors.bg) |
| 86 | +vim.cmd("hi StatusLine guibg=" .. colors.bg_dark .. " guifg=" .. colors.fg_dark) |
| 87 | +vim.cmd("hi StatusLineNC guibg=" .. colors.bg_dark .. " guifg=" .. colors.comment) |
| 88 | +vim.cmd("hi VertSplit guifg=" .. colors.fg_gutter) |
| 89 | +vim.cmd("hi Pmenu guibg=" .. colors.bg_dark .. " guifg=" .. colors.fg) |
| 90 | +vim.cmd("hi PmenuSel guibg=" .. colors.visual .. " guifg=" .. colors.fg) |
| 91 | +vim.cmd("hi Comment guifg=" .. colors.comment .. " gui=italic") |
| 92 | +vim.cmd("hi String guifg=" .. colors.green) |
| 93 | +vim.cmd("hi Number guifg=" .. colors.orange) |
| 94 | +vim.cmd("hi Boolean guifg=" .. colors.orange) |
| 95 | +vim.cmd("hi Constant guifg=" .. colors.orange) |
| 96 | +vim.cmd("hi Identifier guifg=" .. colors.magenta) |
| 97 | +vim.cmd("hi Function guifg=" .. colors.blue) |
| 98 | +vim.cmd("hi Statement guifg=" .. colors.magenta) |
| 99 | +vim.cmd("hi Keyword guifg=" .. colors.cyan) |
| 100 | +vim.cmd("hi Type guifg=" .. colors.cyan) |
| 101 | +vim.cmd("hi Special guifg=" .. colors.blue) |
| 102 | +vim.cmd("hi Error guifg=" .. colors.red) |
| 103 | +vim.cmd("hi WarningMsg guifg=" .. colors.yellow) |
| 104 | +vim.cmd("hi Title guifg=" .. colors.blue .. " gui=bold") |
| 105 | +vim.cmd("hi Directory guifg=" .. colors.blue) |
| 106 | +vim.cmd("hi MatchParen guibg=" .. colors.visual .. " gui=bold") |
| 107 | +vim.cmd("hi NonText guifg=" .. colors.fg_gutter) |
| 108 | +vim.cmd("hi Folded guibg=" .. colors.bg_highlight .. " guifg=" .. colors.comment) |
| 109 | + |
| 110 | +-- ============================================ |
| 111 | +-- Keymaps |
| 112 | +-- ============================================ |
| 113 | + |
| 114 | +local map = vim.keymap.set |
| 115 | + |
| 116 | +-- Better escape |
| 117 | +map("i", "jk", "<Esc>", { desc = "Exit insert mode" }) |
| 118 | +map("i", "jj", "<Esc>", { desc = "Exit insert mode" }) |
| 119 | + |
| 120 | +-- Save |
| 121 | +map("n", "<leader>w", "<cmd>w<cr>", { desc = "Save file" }) |
| 122 | +map("n", "<C-s>", "<cmd>w<cr>", { desc = "Save file" }) |
| 123 | + |
| 124 | +-- Quit |
| 125 | +map("n", "<leader>q", "<cmd>q<cr>", { desc = "Quit" }) |
| 126 | +map("n", "<leader>Q", "<cmd>qa!<cr>", { desc = "Quit all" }) |
| 127 | + |
| 128 | +-- Clear search |
| 129 | +map("n", "<Esc>", "<cmd>nohlsearch<cr>", { desc = "Clear search" }) |
45 | 130 |
|
46 | 131 | -- Better window navigation |
47 | | -vim.keymap.set('n', '<C-h>', '<C-w>h', { desc = 'Move to left window' }) |
48 | | -vim.keymap.set('n', '<C-j>', '<C-w>j', { desc = 'Move to lower window' }) |
49 | | -vim.keymap.set('n', '<C-k>', '<C-w>k', { desc = 'Move to upper window' }) |
50 | | -vim.keymap.set('n', '<C-l>', '<C-w>l', { desc = 'Move to right window' }) |
| 132 | +map("n", "<C-h>", "<C-w>h", { desc = "Go to left window" }) |
| 133 | +map("n", "<C-j>", "<C-w>j", { desc = "Go to lower window" }) |
| 134 | +map("n", "<C-k>", "<C-w>k", { desc = "Go to upper window" }) |
| 135 | +map("n", "<C-l>", "<C-w>l", { desc = "Go to right window" }) |
| 136 | + |
| 137 | +-- Resize windows |
| 138 | +map("n", "<C-Up>", "<cmd>resize +2<cr>", { desc = "Increase height" }) |
| 139 | +map("n", "<C-Down>", "<cmd>resize -2<cr>", { desc = "Decrease height" }) |
| 140 | +map("n", "<C-Left>", "<cmd>vertical resize -2<cr>", { desc = "Decrease width" }) |
| 141 | +map("n", "<C-Right>", "<cmd>vertical resize +2<cr>", { desc = "Increase width" }) |
| 142 | + |
| 143 | +-- Move lines |
| 144 | +map("v", "J", ":m '>+1<cr>gv=gv", { desc = "Move down" }) |
| 145 | +map("v", "K", ":m '<-2<cr>gv=gv", { desc = "Move up" }) |
51 | 146 |
|
52 | 147 | -- Stay in visual mode when indenting |
53 | | -vim.keymap.set('v', '<', '<gv', { desc = 'Indent left' }) |
54 | | -vim.keymap.set('v', '>', '>gv', { desc = 'Indent right' }) |
| 148 | +map("v", "<", "<gv", { desc = "Indent left" }) |
| 149 | +map("v", ">", ">gv", { desc = "Indent right" }) |
55 | 150 |
|
56 | | --- Move lines up/down in visual mode |
57 | | -vim.keymap.set('v', 'J', ":m '>+1<CR>gv=gv", { desc = 'Move selection down' }) |
58 | | -vim.keymap.set('v', 'K', ":m '<-2<CR>gv=gv", { desc = 'Move selection up' }) |
| 151 | +-- Better paste (don't replace clipboard) |
| 152 | +map("v", "p", '"_dP', { desc = "Paste without yanking" }) |
59 | 153 |
|
60 | | --- Show which-key style help (built-in) |
61 | | -vim.keymap.set('n', '<leader>?', function() |
62 | | - print("Leader commands: <leader>w = save, <leader>q = quit") |
63 | | -end, { desc = 'Show help' }) |
| 154 | +-- Center cursor after jumps |
| 155 | +map("n", "<C-d>", "<C-d>zz", { desc = "Scroll down" }) |
| 156 | +map("n", "<C-u>", "<C-u>zz", { desc = "Scroll up" }) |
| 157 | +map("n", "n", "nzzzv", { desc = "Next search result" }) |
| 158 | +map("n", "N", "Nzzzv", { desc = "Previous search result" }) |
64 | 159 |
|
| 160 | +-- Splits |
| 161 | +map("n", "<leader>-", "<cmd>split<cr>", { desc = "Horizontal split" }) |
| 162 | +map("n", "<leader>|", "<cmd>vsplit<cr>", { desc = "Vertical split" }) |
| 163 | + |
| 164 | +-- Buffers |
| 165 | +map("n", "<leader>bn", "<cmd>bnext<cr>", { desc = "Next buffer" }) |
| 166 | +map("n", "<leader>bp", "<cmd>bprevious<cr>", { desc = "Previous buffer" }) |
| 167 | +map("n", "<leader>bd", "<cmd>bdelete<cr>", { desc = "Delete buffer" }) |
| 168 | + |
| 169 | +-- File explorer (netrw) |
| 170 | +map("n", "<leader>e", "<cmd>Explore<cr>", { desc = "File explorer" }) |
| 171 | + |
| 172 | +-- ============================================ |
| 173 | +-- Statusline |
| 174 | +-- ============================================ |
| 175 | + |
| 176 | +local function mode_color() |
| 177 | + local modes = { |
| 178 | + n = colors.blue, |
| 179 | + i = colors.green, |
| 180 | + v = colors.magenta, |
| 181 | + V = colors.magenta, |
| 182 | + ["\22"] = colors.magenta, |
| 183 | + c = colors.orange, |
| 184 | + R = colors.red, |
| 185 | + } |
| 186 | + return modes[vim.fn.mode()] or colors.fg |
| 187 | +end |
| 188 | + |
| 189 | +function _G.statusline() |
| 190 | + local mode = vim.fn.mode():upper() |
| 191 | + local file = vim.fn.expand("%:t") |
| 192 | + if file == "" then file = "[No Name]" end |
| 193 | + local modified = vim.bo.modified and " [+]" or "" |
| 194 | + local readonly = vim.bo.readonly and " [RO]" or "" |
| 195 | + local filetype = vim.bo.filetype ~= "" and vim.bo.filetype or "text" |
| 196 | + local line = vim.fn.line(".") |
| 197 | + local col = vim.fn.col(".") |
| 198 | + local total = vim.fn.line("$") |
| 199 | + |
| 200 | + return string.format( |
| 201 | + " %s │ %s%s%s │ %s │ %d:%d / %d ", |
| 202 | + mode, file, modified, readonly, filetype, line, col, total |
| 203 | + ) |
| 204 | +end |
| 205 | + |
| 206 | +vim.opt.statusline = "%!v:lua.statusline()" |
| 207 | + |
| 208 | +-- ============================================ |
| 209 | +-- Autocommands |
| 210 | +-- ============================================ |
| 211 | + |
| 212 | +local augroup = vim.api.nvim_create_augroup |
| 213 | +local autocmd = vim.api.nvim_create_autocmd |
| 214 | + |
| 215 | +-- Highlight on yank |
| 216 | +autocmd("TextYankPost", { |
| 217 | + group = augroup("highlight_yank", { clear = true }), |
| 218 | + callback = function() |
| 219 | + vim.highlight.on_yank({ higroup = "Visual", timeout = 200 }) |
| 220 | + end, |
| 221 | +}) |
| 222 | + |
| 223 | +-- Remove trailing whitespace on save |
| 224 | +autocmd("BufWritePre", { |
| 225 | + group = augroup("trim_whitespace", { clear = true }), |
| 226 | + pattern = "*", |
| 227 | + command = [[%s/\s\+$//e]], |
| 228 | +}) |
| 229 | + |
| 230 | +-- Return to last edit position |
| 231 | +autocmd("BufReadPost", { |
| 232 | + group = augroup("last_position", { clear = true }), |
| 233 | + callback = function() |
| 234 | + local mark = vim.api.nvim_buf_get_mark(0, '"') |
| 235 | + local lcount = vim.api.nvim_buf_line_count(0) |
| 236 | + if mark[1] > 0 and mark[1] <= lcount then |
| 237 | + pcall(vim.api.nvim_win_set_cursor, 0, mark) |
| 238 | + end |
| 239 | + end, |
| 240 | +}) |
| 241 | + |
| 242 | +-- ============================================ |
65 | 243 | -- Welcome message |
66 | | -vim.api.nvim_create_autocmd('VimEnter', { |
| 244 | +-- ============================================ |
| 245 | + |
| 246 | +vim.api.nvim_create_autocmd("VimEnter", { |
67 | 247 | callback = function() |
68 | | - print("Welcome! Press <Space>? for help. Try :Tutor for built-in tutorial!") |
69 | | - end |
| 248 | + vim.notify("Welcome! Space = leader. Try :Tutor for vim tutorial.", vim.log.levels.INFO) |
| 249 | + end, |
70 | 250 | }) |
0 commit comments