aboutsummaryrefslogtreecommitdiffstats
path: root/neovim/.config/nvim/lua/config
diff options
context:
space:
mode:
authorGravatar Tom van der Lee <t0m.vd.l33@gmail.com>2026-09-16 08:47:13 +0200
committerGravatar Tom van der Lee <t0m.vd.l33@gmail.com>2026-09-16 08:47:13 +0200
commita5e8bbeb9d79d905a193e95a39acd29bc37e6c66 (patch)
tree5b42d54e6b91d454bcbac89f7aac42b0863a869d /neovim/.config/nvim/lua/config
parent6906a9d55928c6250ff106d041b73fdd1ac8aae6 (diff)
downloaddotfiles-a5e8bbeb9d79d905a193e95a39acd29bc37e6c66.tar.gz
dotfiles-a5e8bbeb9d79d905a193e95a39acd29bc37e6c66.tar.bz2
dotfiles-a5e8bbeb9d79d905a193e95a39acd29bc37e6c66.zip
Updated dotfiles
Diffstat (limited to 'neovim/.config/nvim/lua/config')
-rw-r--r--neovim/.config/nvim/lua/config/autocmds.lua43
-rw-r--r--neovim/.config/nvim/lua/config/dap_node.lua88
-rw-r--r--neovim/.config/nvim/lua/config/keymaps.lua12
-rw-r--r--neovim/.config/nvim/lua/config/options.lua6
-rw-r--r--neovim/.config/nvim/lua/config/project_run.lua151
5 files changed, 300 insertions, 0 deletions
diff --git a/neovim/.config/nvim/lua/config/autocmds.lua b/neovim/.config/nvim/lua/config/autocmds.lua
index 7bc4f6b..e9ebe04 100644
--- a/neovim/.config/nvim/lua/config/autocmds.lua
+++ b/neovim/.config/nvim/lua/config/autocmds.lua
@@ -13,6 +13,49 @@ vim.api.nvim_create_autocmd("BufWritePre", {
13 callback = strip_trailing_whitespace, 13 callback = strip_trailing_whitespace,
14}) 14})
15 15
16-- Project-local config: search upward from cwd for a .nvim.lua, the same
17-- nearest-ancestor pattern LSP root-dir detection uses, so it's found
18-- regardless of exactly which subdirectory Neovim's cwd is or how you got
19-- there (:cd, opening a file, session restore, ...) - unlike core 'exrc'
20-- (deliberately left off in options.lua), which only ever checks the
21-- literal cwd once at startup.
22local sourced_project_configs = {}
23
24local function source_project_config()
25 local found = vim.fs.find(".nvim.lua", {
26 upward = true,
27 path = vim.fn.getcwd(),
28 stop = vim.env.HOME,
29 })[1]
30 if not found or sourced_project_configs[found] then
31 return
32 end
33
34 -- vim.secure.read is the same trust mechanism 'exrc' itself uses: prompts
35 -- on first encounter, honors ~/.local/state/nvim/trust, :trust still
36 -- manages the allowlist unchanged. Returns nil (no prompt shown again)
37 -- for a file previously denied.
38 local content = vim.secure.read(found)
39 if not content then
40 return
41 end
42 sourced_project_configs[found] = true
43
44 local chunk, load_err = load(content, "@" .. found)
45 if not chunk then
46 vim.notify("Error loading " .. found .. ":\n" .. load_err, vim.log.levels.ERROR)
47 return
48 end
49 local ok, run_err = pcall(chunk)
50 if not ok then
51 vim.notify("Error running " .. found .. ":\n" .. run_err, vim.log.levels.ERROR)
52 end
53end
54
55vim.api.nvim_create_autocmd({ "VimEnter", "DirChanged" }, {
56 callback = source_project_config,
57})
58
16-- Note: the colorscheme (with its "desert" fallback) is applied from 59-- Note: the colorscheme (with its "desert" fallback) is applied from
17-- lua/plugins/colorscheme.lua's own `config` function, not here — under 60-- lua/plugins/colorscheme.lua's own `config` function, not here — under
18-- lazy.nvim that's the natural place to react to "the plugin is now loaded". 61-- lazy.nvim that's the natural place to react to "the plugin is now loaded".
diff --git a/neovim/.config/nvim/lua/config/dap_node.lua b/neovim/.config/nvim/lua/config/dap_node.lua
new file mode 100644
index 0000000..0fb6890
--- /dev/null
+++ b/neovim/.config/nvim/lua/config/dap_node.lua
@@ -0,0 +1,88 @@
1-- JavaScript/TypeScript launch configs, wired to the js-debug-adapter
2-- (Microsoft's vscode-js-debug, bundled by Mason as a standalone
3-- multi-session DAP server) that dap.lua's mason-nvim-dap ensure_installed
4-- installs.
5--
6-- There's no nvim-dap-python equivalent for this: the one community plugin
7-- for it (mxsdev/nvim-dap-vscode-js) hardcodes the older
8-- `out/src/vsDebugServer.js` vscode-js-debug build layout, which doesn't
9-- match what Mason's js-debug-adapter package actually installs
10-- (`js-debug/src/dapDebugServer.js`) - so this resolves the Mason path
11-- directly instead, same approach as dap-python.lua's debugpy_path.
12--
13-- Lives under lua/config/ rather than lua/plugins/ (and is required
14-- eagerly from dap.lua's config()) because it isn't an installable plugin
15-- itself, just adapter/config wiring - same reason project_run.lua lives
16-- here instead of there.
17--
18-- js-debug-adapter is a multi-session server: debugging a script that
19-- spawns a worker thread arrives as a `startDebugging` reverse request
20-- rather than a second top-level session. nvim-dap's core (dap/session.lua)
21-- already handles that natively, so no extra plugin is needed for it either.
22local M = {}
23
24local function configurations()
25 return {
26 {
27 type = "pwa-node",
28 request = "launch",
29 name = "Launch file",
30 program = "${file}",
31 cwd = "${workspaceFolder}",
32 },
33 {
34 type = "pwa-node",
35 request = "attach",
36 name = "Attach to process",
37 processId = require("dap.utils").pick_process,
38 cwd = "${workspaceFolder}",
39 },
40 }
41end
42
43function M.setup()
44 local registry = require("mason-registry")
45 local pkg = registry.get_package("js-debug-adapter")
46
47 local function register()
48 local entrypoint = pkg:get_install_path() .. "/js-debug/src/dapDebugServer.js"
49
50 require("dap").adapters["pwa-node"] = {
51 type = "server",
52 host = "localhost",
53 port = "${port}",
54 executable = {
55 command = "node",
56 args = { entrypoint, "${port}" },
57 },
58 }
59
60 local dap = require("dap")
61 for _, filetype in ipairs({ "javascript", "typescript", "javascriptreact", "typescriptreact" }) do
62 dap.configurations[filetype] = configurations()
63 end
64 end
65
66 if pkg:is_installed() then
67 register()
68 else
69 -- Same reasoning as dap-python.lua: dap.lua's mason-nvim-dap
70 -- ensure_installed triggers the install, but that's async - on a fresh
71 -- machine (or before it finishes) the entrypoint doesn't exist on disk
72 -- yet, so wire up the adapter once the install actually completes
73 -- instead of pointing it at a path that isn't there yet.
74 vim.notify(
75 "js-debug-adapter not installed yet - dap.lua's ensure_installed should be"
76 .. " installing it now (check :Mason); run :MasonInstall js-debug-adapter"
77 .. " if it doesn't show up there. Debugging will work once it's done.",
78 vim.log.levels.WARN
79 )
80 registry:on("package:install:success", function(installed_pkg)
81 if installed_pkg.name == "js-debug-adapter" then
82 register()
83 end
84 end)
85 end
86end
87
88return M
diff --git a/neovim/.config/nvim/lua/config/keymaps.lua b/neovim/.config/nvim/lua/config/keymaps.lua
index 66e97a0..65e3236 100644
--- a/neovim/.config/nvim/lua/config/keymaps.lua
+++ b/neovim/.config/nvim/lua/config/keymaps.lua
@@ -14,6 +14,18 @@ map("t", "<C-k>", [[<C-\><C-n><C-w>k]])
14map("t", "<C-h>", [[<C-\><C-n><C-w>h]]) 14map("t", "<C-h>", [[<C-\><C-n><C-w>h]])
15map("t", "<C-l>", [[<C-\><C-n><C-w>l]]) 15map("t", "<C-l>", [[<C-\><C-n><C-w>l]])
16 16
17-- Copy to clipboard
18map("v", "<leader>y", '"+y')
19map("n", "<leader>Y", '"+yg_')
20map("n", "<leader>y", '"+y')
21map("n", "<leader>yy", '"+yy')
22
23-- Paste from clipboard
24map("n", "<leader>p", '"+p')
25map("n", "<leader>P", '"+P')
26map("v", "<leader>p", '"+p')
27map("v", "<leader>P", '"+P')
28
17-- Never use the arrow keys 29-- Never use the arrow keys
18for _, key in ipairs({ "<Up>", "<Down>", "<Left>", "<Right>" }) do 30for _, key in ipairs({ "<Up>", "<Down>", "<Left>", "<Right>" }) do
19 map({ "n", "i" }, key, "<Nop>") 31 map({ "n", "i" }, key, "<Nop>")
diff --git a/neovim/.config/nvim/lua/config/options.lua b/neovim/.config/nvim/lua/config/options.lua
index be614e1..1282850 100644
--- a/neovim/.config/nvim/lua/config/options.lua
+++ b/neovim/.config/nvim/lua/config/options.lua
@@ -34,3 +34,9 @@ opt.hlsearch = true
34opt.incsearch = true 34opt.incsearch = true
35opt.ignorecase = true 35opt.ignorecase = true
36opt.smartcase = true 36opt.smartcase = true
37
38-- Project-local config (.nvim.lua) is handled by a custom autocmd in
39-- autocmds.lua instead of the core 'exrc' option: 'exrc' only checks the
40-- literal cwd at startup, once, so it misses a project's .nvim.lua whenever
41-- Neovim didn't start with cwd exactly there. Leave 'exrc' off to avoid
42-- double-sourcing the same file via both mechanisms.
diff --git a/neovim/.config/nvim/lua/config/project_run.lua b/neovim/.config/nvim/lua/config/project_run.lua
new file mode 100644
index 0000000..40c1795
--- /dev/null
+++ b/neovim/.config/nvim/lua/config/project_run.lua
@@ -0,0 +1,151 @@
1-- Single-source-of-truth helper for project-local run/debug configs, meant
2-- to be called once per config from a project's `.nvim.lua` (requires
3-- `opt.exrc` - see options.lua - and trusting the file once via
4-- `:help :trust`). One call registers both an overseer run task and a
5-- matching nvim-dap debug config, so a project states its command once
6-- instead of repeating it across a separate run-file and debug-file.
7--
8-- Example .nvim.lua (script invocation):
9-- require("config.project_run").python({
10-- name = "Run app",
11-- program = vim.fn.getcwd() .. "/main.py",
12-- args = { "--flag" }, -- optional, shared by both run and debug
13-- })
14--
15-- Example .nvim.lua (module invocation, `python -m <module>`):
16-- require("config.project_run").python({
17-- name = "Run app",
18-- module = "fastapi",
19-- args = { "dev", "app" },
20-- })
21--
22-- This shows "Run app" in the <leader>rr picker (plain execution) and in
23-- the <leader>dc picker (same script/module, under debugpy, breakpoints
24-- included). If <cwd>/.venv exists, both get VIRTUAL_ENV/PATH pointed at
25-- it, same as `uv run` (or `source .venv/bin/activate`) would - and the
26-- debug config additionally gets pythonPath set to that venv's interpreter,
27-- since debugpy runs the script with the literal interpreter it's told
28-- rather than resolving `python3` via PATH.
29--
30-- Example .nvim.lua (Node, script invocation):
31-- require("config.project_run").node({
32-- name = "Run app",
33-- program = vim.fn.getcwd() .. "/index.js",
34-- args = { "--flag" }, -- optional, shared by both run and debug
35-- })
36--
37-- Example .nvim.lua (Node, `npm run <script>` invocation):
38-- require("config.project_run").node({
39-- name = "Run app",
40-- npmScript = "dev",
41-- })
42
43local M = {}
44
45local function venv_dir()
46 local venv = vim.fn.getcwd() .. "/.venv"
47 if vim.fn.isdirectory(venv) == 0 then
48 return nil
49 end
50 return venv
51end
52
53local function venv_env(venv)
54 return {
55 VIRTUAL_ENV = venv,
56 PATH = venv .. "/bin:" .. vim.env.PATH,
57 }
58end
59
60function M.python(opts)
61 assert(opts.name, "project_run.python: opts.name is required")
62 assert(opts.program or opts.module, "project_run.python: opts.program or opts.module is required")
63
64 local venv = venv_dir()
65 local env = venv and venv_env(venv) or nil
66 local cmd = opts.module and { "python3", "-m", opts.module } or { "python3", opts.program }
67
68 require("overseer").register_template({
69 name = opts.name,
70 builder = function()
71 return {
72 cmd = cmd,
73 args = opts.args,
74 name = opts.name,
75 env = env,
76 }
77 end,
78 })
79
80 local dap = require("dap")
81 dap.configurations.python = dap.configurations.python or {}
82 table.insert(dap.configurations.python, {
83 type = "python",
84 request = "launch",
85 name = opts.name,
86 program = opts.program,
87 module = opts.module,
88 args = opts.args,
89 env = env,
90 -- nvim-dap-python only auto-detects an interpreter (VIRTUAL_ENV env var,
91 -- then a venv/.venv/env/.env folder search) when pythonPath/python isn't
92 -- already set - and debugpy needs the literal interpreter path here
93 -- regardless (it spawns it directly, not through a shell that'd resolve
94 -- `python3` via PATH the way env.PATH above does for the overseer task).
95 -- Set it explicitly from the same .venv we already found, so debugging
96 -- doesn't depend on that separate heuristic agreeing with us - without
97 -- this, a missed/wrong guess falls back to debugpy's own adapter venv,
98 -- which can't see the project's installed packages.
99 pythonPath = venv and (venv .. "/bin/python") or nil,
100 })
101end
102
103-- Node has no venv-style interpreter juggling to do - node_modules
104-- resolution is automatic - so this is a straight translation of opts into
105-- an overseer task plus a pwa-node dap config, into all four JS/TS
106-- filetypes (dap_node.lua registers the same four for its own defaults).
107function M.node(opts)
108 assert(opts.name, "project_run.node: opts.name is required")
109 assert(opts.program or opts.npmScript, "project_run.node: opts.program or opts.npmScript is required")
110
111 local cmd = opts.npmScript and { "npm", "run", opts.npmScript } or { "node", opts.program }
112
113 require("overseer").register_template({
114 name = opts.name,
115 builder = function()
116 return {
117 cmd = cmd,
118 args = opts.args,
119 name = opts.name,
120 }
121 end,
122 })
123
124 -- A fresh table per filetype - nvim-dap's session setup can stash
125 -- bookkeeping fields onto a running config, so the four filetypes
126 -- shouldn't share one table instance.
127 local function config()
128 local c = {
129 type = "pwa-node",
130 request = "launch",
131 name = opts.name,
132 args = opts.args,
133 cwd = vim.fn.getcwd(),
134 }
135 if opts.npmScript then
136 c.runtimeExecutable = "npm"
137 c.runtimeArgs = { "run", opts.npmScript }
138 else
139 c.program = opts.program
140 end
141 return c
142 end
143
144 local dap = require("dap")
145 for _, filetype in ipairs({ "javascript", "typescript", "javascriptreact", "typescriptreact" }) do
146 dap.configurations[filetype] = dap.configurations[filetype] or {}
147 table.insert(dap.configurations[filetype], config())
148 end
149end
150
151return M