diff options
| author | 2026-09-16 08:47:13 +0200 | |
|---|---|---|
| committer | 2026-09-16 08:47:13 +0200 | |
| commit | a5e8bbeb9d79d905a193e95a39acd29bc37e6c66 (patch) | |
| tree | 5b42d54e6b91d454bcbac89f7aac42b0863a869d /neovim/.config/nvim/lua/config/project_run.lua | |
| parent | 6906a9d55928c6250ff106d041b73fdd1ac8aae6 (diff) | |
| download | dotfiles-a5e8bbeb9d79d905a193e95a39acd29bc37e6c66.tar.gz dotfiles-a5e8bbeb9d79d905a193e95a39acd29bc37e6c66.tar.bz2 dotfiles-a5e8bbeb9d79d905a193e95a39acd29bc37e6c66.zip | |
Updated dotfiles
Diffstat (limited to 'neovim/.config/nvim/lua/config/project_run.lua')
| -rw-r--r-- | neovim/.config/nvim/lua/config/project_run.lua | 151 |
1 files changed, 151 insertions, 0 deletions
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 | |||
| 43 | local M = {} | ||
| 44 | |||
| 45 | local 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 | ||
| 51 | end | ||
| 52 | |||
| 53 | local function venv_env(venv) | ||
| 54 | return { | ||
| 55 | VIRTUAL_ENV = venv, | ||
| 56 | PATH = venv .. "/bin:" .. vim.env.PATH, | ||
| 57 | } | ||
| 58 | end | ||
| 59 | |||
| 60 | function 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 | }) | ||
| 101 | end | ||
| 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). | ||
| 107 | function 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 | ||
| 149 | end | ||
| 150 | |||
| 151 | return M | ||
