diff options
| author | Ibrahim Muftee <ibrahim@muftee.net> | 2025-07-28 19:07:00 -0500 |
|---|---|---|
| committer | Ibrahim Muftee <ibrahim@muftee.net> | 2026-07-01 00:23:31 -0400 |
| commit | 4b561caa1839dfe719f8b09483ce6826196ee87f (patch) | |
| tree | 693103b8cd160175e76feb336bdac7649a398c9e /dot_config/nvim_new/init.lua | |
| parent | 3c699f5e5bcf12f29d4c665c55d30ef45c0d0e0c (diff) | |
feat(nvim) begin new minimal config
Diffstat (limited to 'dot_config/nvim_new/init.lua')
| -rw-r--r-- | dot_config/nvim_new/init.lua | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/dot_config/nvim_new/init.lua b/dot_config/nvim_new/init.lua new file mode 100644 index 0000000..0547ec2 --- /dev/null +++ b/dot_config/nvim_new/init.lua @@ -0,0 +1,307 @@ +------------ Neovim config -------- + + +------------ Options ------------ + +-- Leader key as space +vim.g.mapleader = " " + +-- Line numbers +vim.o.number = true +vim.o.relativenumber = true + +-- Line wrapping +vim.o.wrap = false +vim.o.sidescroll = 5 + +-- Scroll with 8 lines of buffer +vim.o.scrolloff = 8 + +-- Folds +vim.o.foldtext = "" +vim.o.foldlevelstart = 1 + +-- Tabs +vim.o.tabstop = 4 +vim.o.softtabstop = 4 +vim.o.shiftwidth = 0 +vim.o.expandtab = true + +-- Indenting +vim.o.autoindent = true +vim.o.smartindent = true +vim.o.breakindent = true + +-- Borders +vim.o.winborder = "rounded" + +-- Search options for case +vim.o.ignorecase = true +vim.o.smartcase = true + +-- Swaps, backups, and undos +vim.o.swapfile = false +vim.o.backup = false +vim.o.undodir = vim.fn.expand("~/.vim/undodir") +vim.o.undofile = true + +------------ Keybindings ------------ + +-- Custom map function +local function map(mode, left, right, opts) + local default_opts = { + noremap = true, + silent = true, + } + opts = opts or {} + -- "keep" mode preserves the values from the leftmost table, + -- so prioritize any options provided over the defaults + local full_opts = vim.tbl_extend("keep", opts, default_opts) + vim.keymap.set(mode, left, right, full_opts) +end + + +-- Clear any highlighted searches when pressing Esc +-- This lets me leave hlsearch on by default while having an easy way to clear +-- it again. +map({ "i", "n" }, "<esc>", "<CMD>noh<CR><ESC>", { desc = "Escape and clear hlsearch" }) + +-- Move lines while in visual mode with J and K +map("v", "J", ":m '>+1<CR>gv=gv") +map("v", "K", ":m '<-2<CR>gv=gv") + +-- Keep cursor in the same place when using J to join lines +map("n", "J", "mzJ`z") + +-- Center the screen when jumping around with C-d and C-u +map("n", "<C-d>", "<C-d>zz") +map("n", "<C-u>", "<C-u>zz") + +-- Center the screen when jumping to next / previous search terms +map("n", "n", "nzzzv") +map("n", "N", "Nzzzv") + +-- Easier quickfix naviagion +map("n", "<C-k>", "<cmd>cprev<cr>", { desc = "Quickfix previous item" }) +map("n", "<C-j>", "<cmd>cnext<cr>", { desc = "Quickfix next item" }) + +-- Quick maps to paste/delete without overwriting the current buffer +map("x", "<leader>p", '"_dP') +map({ "n", "v" }, "<leader>d", '"_dP') + +-- System clipboard +map({ "n", "v" }, "<leader>cd", '"+d', { desc = "Clipboard delete" }) +map({ "n", "v" }, "<leader>cp", '"+p', { desc = "Clipboard paste" }) +map({ "n", "v" }, "<leader>cy", '"+y', { desc = "Clipboard yank" }) + + +-- Create a GitHub URL for the current file and line number, and copy it +-- to the clipboard. +map("n", "<leader>yfg", function() + local remote_url = vim.fn.system("git config --get remote.origin.url") + if not remote_url:find("github.com") then + vim.notify("This is not a GitHub repository") + return + end + + local trimmed_remote_url = remote_url:gsub("%.git", ""):gsub("%s+", ""):gsub("\n", "") + local branch_name = vim.fn.system("git rev-parse --abbrev-ref HEAD"):gsub("\n", "") + + -- Now construct the link in this format: + -- <remote>/blob/<branch>/<file>#l<line> + + local filepath = vim.fn.expand("%:."):gsub("\\", "/") + + local line = vim.fn.line(".") + local result = trimmed_remote_url .. "/blob/" .. branch_name .. "/" .. filepath .. "#L" .. line + + vim.fn.setreg("+", result) + vim.notify("Copied GitHub URL to clipboard: " .. result) +end, { desc = "Yank GitHub URL to clipboard", expr = true }) + +------------ Plugins ------------ + +vim.pack.add({ + { src = "https://github.com/rose-pine/neovim" }, + { src = "https://github.com/stevearc/oil.nvim" }, + { src = "https://github.com/echasnovski/mini.pick" }, + { src = "https://github.com/neovim/nvim-lspconfig" }, + { src = "https://github.com/nvim-treesitter/nvim-treesitter" }, + { src = "https://github.com/nvim-lua/plenary.nvim" }, + { + src = "https://github.com/ThePrimeagen/harpoon", + version = "harpoon2" + }, + { src = "https://github.com/nvim-neotest/neotest" }, + { src = "https://github.com/antoinemadec/FixCursorHold.nvim" }, + { src = "https://github.com/nvim-neotest/neotest-python", }, + { src = "https://github.com/nvim-neotest/nvim-nio" }, + { src = "https://github.com/jiaoshijie/undotree" }, + +}) + +-- LSP +vim.lsp.enable({ "lua_ls", "ruff", "rust_analyzer" }) +vim.keymap.set("n", "<leader>rf", vim.lsp.buf.format) + +-- LSP specific keybindings +vim.api.nvim_create_autocmd("LspAttach", { + group = vim.api.nvim_create_augroup("IbrahimLspConfig", {}), + callback = function(evt) + local function lsp_map(mode, lhs, rhs, desc) + local opts = { buffer = evt.buf, desc = "LSP: " .. (desc or "") } + map(mode, lhs, rhs, opts) + end + + local has_telescope, _ = pcall(require, "telescope") + local function lsp_telescope_map(mode, lhs, with_telescope_rhs, no_telescope_rhs, desc) + if has_telescope then + desc = desc .. " (Telescope)" + lsp_map(mode, lhs, with_telescope_rhs, desc) + else + lsp_map(mode, lhs, no_telescope_rhs, desc) + end + end + local function tele_builtin(name, opts) + return function() + require("telescope.builtin")[name](opts) + end + end + + -- Navigation + lsp_map("n", "gd", vim.lsp.buf.definition, "[G]oto [D]efinition") + lsp_map("n", "gt", vim.lsp.buf.type_definition, "[G]oto [t]ype Definition") + lsp_map("n", "gD", vim.lsp.buf.declaration, "[G]oto [D]eclaration") + lsp_map("n", "gr", vim.lsp.buf.references, "[G]oto [R]eferences") + lsp_map("n", "gI", vim.lsp.buf.implementation, "[G]oto [I]mplementation") + + lsp_telescope_map("n", "gr", tele_builtin("lsp_references"), vim.lsp.buf.references, "Go to references") + lsp_telescope_map( + "n", + "<leader>sn", + tele_builtin("lsp_document_symbols"), + vim.lsp.buf.document_symbol, + "Search document symbols" + ) + lsp_telescope_map( + "n", + "<leader>sN", + tele_builtin("lsp_dynamic_workspace_symbols"), + vim.lsp.buf.workspace_symbol, + "Search workspace symbols" + ) + + -- Code changes + lsp_map("n", "<leader>cr", vim.lsp.buf.rename, "Rename") + lsp_map("n", "<leader>ca", vim.lsp.buf.code_action, "Code actions") + + -- Diagnostics + lsp_map("n", "<leader>ds", vim.diagnostic.open_float, "[s]how [d]iagnostic window") + lsp_map("n", "<leader>dn", function() + vim.diagnostic.jump({ count = 1, float = true }) + end, "Go to [n]ext [d]iagnostic") + lsp_map("n", "<leader>dp", function() + vim.diagnostic.jump({ count = -1, float = true }) + end, "Go to [p]revious [d]iagnostic") + + -- See `:help K` for why this keymap + lsp_map("n", "K", function() + vim.lsp.buf.hover { border = "single", max_height = 25, max_width = 120 } + end, "Hover Documentation") + lsp_map("n", "<C-S-k>", vim.lsp.buf.signature_help, "Signature Documentation") + + -- Workspace + lsp_map("n", "<leader>wa", vim.lsp.buf.add_workspace_folder, "[W]orkspace [A]dd Folder") + lsp_map("n", "<leader>wr", vim.lsp.buf.remove_workspace_folder, "[W]orkspace [R]emove Folder") + lsp_map("n", "<leader>wl", function() + print(vim.inspect(vim.lsp.buf.list_workspace_folders())) + end, "[W]orkspace [L]ist Folders") + end, +}) + +-- Treesitter +require "nvim-treesitter.configs".setup({ + ensure_installed = { "lua", "python", "rust" }, + highlight = { enable = true } +}) + +-- Format on write +vim.api.nvim_create_autocmd("BufWritePre", { + pattern = "*", + callback = function() + vim.lsp.buf.format() + end, +}) + +-- Completion +vim.api.nvim_create_autocmd('LspAttach', { + callback = function(ev) + local client = vim.lsp.get_client_by_id(ev.data.client_id) + if client:supports_method('textDocument/completion') then + vim.lsp.completion.enable(true, client.id, ev.buf, { autotrigger = true }) + end + end, +}) +vim.cmd("set completeopt+=noselect") + +-- Mini.pick +require "mini.pick".setup() +map("n", "<leader>f", "<CMD>Pick files<CR>") +map("n", "<leader>vh", "<CMD>Pick help<CR>") + +-- Oil +require "oil".setup() +vim.keymap.set("n", "g-", "<CMD>Oil<CR>") + +-- Harpoon +local harpoon = require("harpoon") +harpoon:setup() + +map("n", "<leader>a", function() + harpoon:list():add() +end, { desc = "mark file with harpoon" }) + +map("n", "<c-e>", function() + harpoon.ui:toggle_quick_menu(harpoon:list()) +end, { desc = "open harpoon menu" }) + +map("n", "<leader>j", function() + harpoon:list():select(1) +end, { desc = "open harpoon file 1" }) +map("n", "<leader>k", function() + harpoon:list():select(2) +end, { desc = "open harpoon file 2" }) +map("n", "<leader>l", function() + harpoon:list():select(3) +end, { desc = "open harpoon file 3" }) +map("n", "<leader>;", function() + harpoon:list():select(4) +end, { desc = "Open Harpoon file 4" }) + +-- Neotest +require "neotest".setup({ + adapters = { + require("neotest-python")({ + python = "python", + runner = "pytest", + is_test_file = function(file_path) ---@param file_path string + local normalized_path = vim.fs.normalize(file_path) + return normalized_path.match("test/.*%/test_.%.py$") ~= nil + end, + }), + } +}) + +map("n", "<leader>tr", require("neotest").run.run, { desc = "Neotest: run test at cursor" }) +map("n", "<leader>tf", function() + require("netotest").run.run(vim.fn.expand("%")) +end, { desc = "Neotest: run all tests in current file" }) +map("n", "<leader>ts", require("neotest").summary.toggle, { desc = "Neotest: toggle summary panel" }) + +-- Undotree +require("undotree").setup() +map("n", "<leader>u", require("undotree").toggle, { desc = "Toggle Undotree" }) + +-- Colorscheme +vim.o.background = "dark" +vim.cmd("colorscheme rose-pine") |
