Lua · 11209 bytes Raw Blame History
1 -- smooth editing, LSP, Treesitter, Telescope, formatting, Git, terminals, and a few QoL boosts.
2
3 ---------------------------------------------------------------
4 -- 0) Bootstrap lazy.nvim (plugin manager)
5 ---------------------------------------------------------------
6 local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
7 if not vim.loop.fs_stat(lazypath) then
8 vim.fn.system({
9 "git", "clone", "--filter=blob:none",
10 "https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath,
11 })
12 end
13 vim.opt.rtp:prepend(lazypath)
14
15 ---------------------------------------------------------------
16 -- 1) Core settings (Wayland-safe clipboard, sensible defaults)
17 ---------------------------------------------------------------
18 vim.g.mapleader = " "
19 vim.g.maplocalleader = ","
20
21 local opt = vim.opt
22 opt.number = true
23 opt.relativenumber = true
24 opt.signcolumn = "yes"
25 opt.termguicolors = true
26 opt.cursorline = true
27 opt.wrap = false
28 opt.scrolloff = 5
29 opt.sidescrolloff = 8
30 opt.expandtab = true
31 opt.shiftwidth = 2
32 opt.tabstop = 2
33 opt.smartindent = true
34 opt.ignorecase = true
35 opt.smartcase = true
36 opt.incsearch = true
37 opt.splitbelow = true
38 opt.splitright = true
39 opt.updatetime = 300
40 opt.timeoutlen = 400
41 opt.undofile = true
42 opt.clipboard = "unnamedplus" -- uses wl-clipboard on Wayland if installed
43
44 ---------------------------------------------------------------
45 -- 2) Plugins via lazy.nvim
46 ---------------------------------------------------------------
47 require("lazy").setup({
48 -- UI + UX ---------------------------------------------------
49 { "folke/tokyonight.nvim", lazy = false, priority = 1000, opts = { style = "night" } },
50 { "nvim-lualine/lualine.nvim", dependencies = { "nvim-tree/nvim-web-devicons" },
51 opts = { options = { theme = "auto", globalstatus = true } } },
52 { "folke/which-key.nvim", event = "VeryLazy", opts = {} },
53 { "stevearc/dressing.nvim", event = "VeryLazy", opts = {} },
54 { "rcarriga/nvim-notify", opts = { timeout = 2000 } },
55 -- Removed noice.nvim for faster command execution
56 { "lukas-reineke/indent-blankline.nvim", main = "ibl", opts = {} },
57 { "numToStr/Comment.nvim", opts = {} },
58 { "kylechui/nvim-surround", event = "VeryLazy", opts = {} },
59
60 -- Files, search, projects ----------------------------------
61 { "nvim-telescope/telescope.nvim", dependencies = { "nvim-lua/plenary.nvim" } },
62 { "nvim-telescope/telescope-fzf-native.nvim", build = "make", cond = function() return vim.fn.executable("make") == 1 end },
63 { "ahmedkhalf/project.nvim",
64 config = function()
65 require("project_nvim").setup({
66 detection_methods = { "pattern", "lsp" },
67 patterns = { ".git", "pyproject.toml", "package.json", "Makefile" }
68 })
69 end
70 },
71 { "stevearc/oil.nvim", opts = { view_options = { show_hidden = true } } },
72
73 -- Git -------------------------------------------------------
74 { "lewis6991/gitsigns.nvim", opts = {} },
75 { "kdheepak/lazygit.nvim", cmd = { "LazyGit" } },
76
77 -- Terminal & task runner -----------------------------------
78 { "akinsho/toggleterm.nvim", version = "*", opts = { open_mapping = [[<C-`>]], direction = "float" } },
79 { "stevearc/overseer.nvim", opts = {} },
80
81 -- Treesitter ------------------------------------------------
82 { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate",
83 config = function()
84 require("nvim-treesitter.configs").setup({
85 ensure_installed = {
86 "bash", "c", "cpp", "lua", "python", "rust", "json", "yaml", "toml",
87 "html", "css", "javascript", "typescript", "markdown", "markdown_inline",
88 "make", "fish"
89 },
90 highlight = { enable = true },
91 indent = { enable = true },
92 })
93 end
94 },
95
96 -- LSP, format, lint ----------------------------------------
97 { "williamboman/mason.nvim", opts = { ui = { border = "rounded" } } },
98 { "williamboman/mason-lspconfig.nvim" },
99 { "neovim/nvim-lspconfig" },
100 { "stevearc/conform.nvim", opts = {
101 notify_on_error = false,
102 format_on_save = function(buf)
103 -- Disable on big files
104 local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
105 if ok and stats and stats.size > 512 * 1024 then return end
106 return { timeout_ms = 2000, lsp_fallback = true }
107 end,
108 formatters_by_ft = {
109 lua = { "stylua" },
110 python = { "ruff_format", "black" },
111 sh = { "shfmt" }, bash = { "shfmt" }, zsh = { "shfmt" }, fish = { "fish_indent" },
112 c = { "clang_format" }, cpp = { "clang_format" },
113 javascript = { "prettier" }, typescript = { "prettier" },
114 json = { "jq", "prettier" }, yaml = { "prettier" }, toml = { "taplo" },
115 html = { "prettier" }, css = { "prettier" }, markdown = { "prettier" },
116 },
117 }
118 },
119
120 -- Debugging (optional, light defaults) ---------------------
121 { "mfussenegger/nvim-dap" },
122 { "rcarriga/nvim-dap-ui", dependencies = { "mfussenegger/nvim-dap", "nvim-neotest/nvim-nio" } },
123 }, {
124 install = { colorscheme = { "tokyonight" } },
125 change_detection = { notify = false },
126 })
127
128 ---------------------------------------------------------------
129 -- 3) Post-plugin config (LSP, Telescope, keymaps, etc.)
130 ---------------------------------------------------------------
131 -- Colorscheme
132 vim.cmd.colorscheme("tokyonight")
133
134 -- Lualine
135 require("lualine").setup({})
136
137 -- Telescope
138 local telescope = require("telescope")
139 telescope.setup({
140 defaults = {
141 mappings = {
142 i = {
143 ["<C-j>"] = "move_selection_next",
144 ["<C-k>"] = "move_selection_previous"
145 }
146 }
147 }
148 })
149 pcall(telescope.load_extension, "fzf")
150 pcall(telescope.load_extension, "projects")
151
152 -- Oil: simple file manager toggle
153 vim.keymap.set("n", "-", function() require("oil").toggle_float() end, { desc = "Oil file manager" })
154
155 -- ToggleTerm: float terminal with <C-`>
156 -- (Already mapped via opts. Add extra terminals if desired.)
157
158 -- Overseer: tasks (build, run, test)
159 vim.keymap.set("n", "<leader>tt", ":OverseerToggle<CR>", { desc = "Toggle task list" })
160 vim.keymap.set("n", "<leader>tr", ":OverseerRun<CR>", { desc = "Run a task" })
161
162 -- Gitsigns
163 require("gitsigns").setup()
164
165 -- Mason + LSPConfig
166 require("mason").setup()
167 local mason_lspconfig = require("mason-lspconfig")
168
169 -- Ensure these servers are installed
170 mason_lspconfig.setup({
171 ensure_installed = {
172 "pyright", "lua_ls", "clangd", "rust_analyzer", "bashls",
173 "jsonls", "yamlls", "html", "cssls", "marksman", "taplo"
174 }
175 })
176
177 local lspconfig = require("lspconfig")
178 local capabilities = vim.lsp.protocol.make_client_capabilities()
179
180 -- Pretty borders
181 local handlers = {
182 ["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = "rounded" }),
183 ["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = "rounded" }),
184 }
185
186 -- on_attach: buffer-local mappings
187 local on_attach = function(_, bufnr)
188 local nmap = function(keys, func, desc)
189 vim.keymap.set("n", keys, func, { buffer = bufnr, desc = desc })
190 end
191 nmap("gd", vim.lsp.buf.definition, "Goto Definition")
192 nmap("gr", vim.lsp.buf.references, "References")
193 nmap("gD", vim.lsp.buf.declaration, "Goto Declaration")
194 nmap("gi", vim.lsp.buf.implementation, "Goto Implementation")
195 nmap("K", vim.lsp.buf.hover, "Hover")
196 nmap("<leader>rn", vim.lsp.buf.rename, "Rename symbol")
197 nmap("<leader>ca", vim.lsp.buf.code_action, "Code Action")
198 nmap("<leader>fd", function() vim.lsp.buf.format({ async = true }) end, "Format buffer")
199 end
200
201 -- Manual server setup (more compatible)
202 local servers = {
203 pyright = {},
204 lua_ls = {
205 settings = {
206 Lua = {
207 diagnostics = { globals = { "vim" } },
208 workspace = { checkThirdParty = false }
209 }
210 }
211 },
212 clangd = {},
213 rust_analyzer = {},
214 bashls = {},
215 jsonls = {},
216 yamlls = {},
217 html = {},
218 cssls = {},
219 marksman = {},
220 taplo = {},
221 }
222
223 -- Setup each server
224 for server, config in pairs(servers) do
225 config.capabilities = capabilities
226 config.on_attach = on_attach
227 config.handlers = handlers
228 lspconfig[server].setup(config)
229 end
230
231 -- DAP minimal sugar
232 local dap_ok, dapui = pcall(require, "dapui")
233 if dap_ok then
234 dapui.setup()
235 local dap = require("dap")
236 dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open() end
237 dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close() end
238 dap.listeners.before.event_exited["dapui_config"] = function() dapui.close() end
239 end
240
241 ---------------------------------------------------------------
242 -- 4) Keymaps you'll actually use (and remember)
243 ---------------------------------------------------------------
244 local map = vim.keymap.set
245 -- Save, quit
246 map({"n","i","v"}, "<C-s>", function() vim.cmd("silent w") end, { desc = "Save" })
247 map("n", "<leader>q", ":q<CR>", { desc = "Quit" })
248
249 -- Windows and tabs
250 map("n", "<leader>sv", ":vsplit<CR>", { desc = "Split vertical" })
251 map("n", "<leader>sh", ":split<CR>", { desc = "Split horizontal" })
252 map("n", "<leader>to", ":tabnew<CR>", { desc = "New tab" })
253
254 -- Telescope
255 map("n", "<leader>ff", function() require("telescope.builtin").find_files() end, { desc = "Find files" })
256 map("n", "<leader>fg", function() require("telescope.builtin").live_grep() end, { desc = "Live grep" })
257 map("n", "<leader>fb", function() require("telescope.builtin").buffers() end, { desc = "Buffers" })
258 map("n", "<leader>fh", function() require("telescope.builtin").help_tags() end, { desc = "Help tags" })
259 map("n", "<leader>fp", function() require("telescope").extensions.projects.projects() end, { desc = "Projects" })
260
261 -- Move lines (visual)
262 map("v", "J", ":m '>+1<CR>gv=gv")
263 map("v", "K", ":m '<-2<CR>gv=gv")
264
265 -- Clear search
266 map("n", "<Esc>", ":noh<CR>", { silent = true })
267
268 -- Format
269 map("n", "<leader>f", function() require("conform").format({ lsp_fallback = true }) end, { desc = "Format file" })
270
271 ---------------------------------------------------------------
272 -- 5) Small QoL autocommands
273 ---------------------------------------------------------------
274 -- Restore cursor to last position
275 vim.api.nvim_create_autocmd("BufReadPost", {
276 callback = function()
277 local mark = vim.api.nvim_buf_get_mark(0, '"')
278 local lcount = vim.api.nvim_buf_line_count(0)
279 if mark[1] > 0 and mark[1] <= lcount then
280 pcall(vim.api.nvim_win_set_cursor, 0, mark)
281 end
282 end
283 })
284
285 -- Highlight on yank
286 vim.api.nvim_create_autocmd("TextYankPost", {
287 callback = function() vim.highlight.on_yank({ higroup = "IncSearch", timeout = 120 }) end
288 })
289
290 -- Wayland clipboard fix hint (if clipboard missing)
291 if vim.env.WAYLAND_DISPLAY and vim.o.clipboard ~= "unnamedplus" then
292 vim.notify("Tip: install 'wl-clipboard' and set clipboard=unnamedplus for system clipboard.", vim.log.levels.INFO)
293 end
294
295 ---------------------------------------------------------------
296 -- 6) Add language tools via Mason: :Mason
297 -- Choose servers/formatters: pyright, ruff, lua_ls, clangd, rust_analyzer, bashls,
298 -- jsonls, yamlls, html, cssls, tsserver/ts_ls, marksman, taplo, fortls, etc.
299 -- Then enjoy: <leader>ff (files), <leader>fg (grep), <C-`> (terminal), - (Oil),
300 -- <leader>f (format), gd/gr/K, and :OverseerRun for project tasks.
301 ---------------------------------------------------------------