1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
------------ 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/nvim-treesitter/nvim-treesitter" },
{ src = "https://github.com/mason-org/mason.nvim" },
})
-- LSP
vim.lsp.enable({ "lua_ls", "ruff", "basedpyright", "rust_analyzer" })
-- 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")
lsp_map("n", "<leader>rf", vim.lsp.buf.format, "Format")
-- 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,
})
require("mason").setup()
-- Treesitter
require "nvim-treesitter.configs".setup({
ensure_installed = { "lua", "python", "rust" },
highlight = { enable = true }
})
-- 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")
|