aboutsummaryrefslogtreecommitdiffstats
path: root/neovim/.config/nvim/README.md
blob: 0e50788a8094282eedaee100a8ee339554d5ce03 (plain)
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
# Neovim config

Standalone, Neovim-only Lua config — independent of `../../vim/.vim/vimrc`
(Vim's own config; it is not read by Neovim at all). Plugin manager is
[lazy.nvim](https://github.com/folke/lazy.nvim), which bootstraps itself on
first launch — no submodule needed.

## Layout

```
init.lua              Entry point: sets <leader>, then loads config/* in order
ftplugin/
  python.lua           Per-filetype settings for *.py (tabstop/shiftwidth/etc.)
lua/config/            Everything that isn't a plugin declaration
  options.lua           vim.opt settings (search, undo, whitespace display, ...)
  keymaps.lua           Global keymaps not owned by a specific plugin
  autocmds.lua          Global autocmds not owned by a specific plugin
  project_run.lua       Helper for project-local run/debug configs (see below)
  dap_node.lua           Node/js-debug-adapter wiring for nvim-dap (see below)
  lazy.lua              Bootstraps lazy.nvim and points it at lua/plugins/
lua/plugins/            One file per plugin (or small plugin group), each
                        returning a lazy.nvim plugin spec (or list of specs)
lazy-lock.json          Plugin versions pinned by lazy.nvim - commit changes
                        to this whenever plugins are added/updated
.gitignore              Ignores spell/ (spellfile downloads)
```

`lua/config/*` and `lua/plugins/*` are auto-loaded: `init.lua` does
`require("config.lazy")`, which calls `require("lazy").setup("plugins", ...)`
— lazy.nvim automatically picks up every file under `lua/plugins/`, so a new
file dropped there just needs to `return { "author/repo", ... }` (or a list of
such tables) and it's included on next launch.

## What's in `lua/plugins/`

| File | Plugin(s) | Purpose |
|---|---|---|
| `colorscheme.lua` | vim-janah | Color scheme, falls back to `desert` if it fails to load |
| `statusline.lua` | lualine.nvim | Status line |
| `filetree.lua` | neo-tree.nvim | File explorer sidebar with git-status colors (`<leader>e` to toggle) |
| `fuzzyfinder.lua` | telescope.nvim, telescope-ui-select.nvim | Fuzzy file finder (`<C-p>`), live grep (`<leader>fg`), buffers (`<leader>fb`); also overrides `vim.ui.select` globally so overseer's/dap's pickers are Telescope windows |
| `editing.lua` | quick-scope | `f`/`F`/`t`/`T` target highlighting (comment toggling is Neovim's built-in `gc`/`gcc`) |
| `git.lua` | vim-fugitive, gitsigns.nvim | `:Git` commands, git-diff gutter signs |
| `ale.lua` | ALE | Linters/fixers only — `g:ale_disable_lsp = 1` so it doesn't fight the LSP client's diagnostics/hover/go-to-def |
| `claude-code.lua` | claude-code.nvim (+ plenary.nvim) | `:ClaudeCode` and friends |
| `toggleterm.lua` | toggleterm.nvim | Scratch terminal toggle — `<C-.>`, vertical split on the right matching claude-code.nvim's placement/ratio |
| `treesitter.lua` | nvim-treesitter (`main` branch) | Parser installs + syntax highlighting |
| `autopairs.lua` | nvim-autopairs, nvim-ts-autotag | Auto-close brackets/quotes/HTML+JSX tags |
| `completion.lua` | blink.cmp (+ friendly-snippets) | Completion popup, Tab-to-accept (`super-tab` preset) |
| `lsp.lua` | nvim-lspconfig, mason.nvim, mason-lspconfig.nvim | Installs & enables language servers, wires blink.cmp's capabilities into them |
| `dap.lua` | nvim-dap, nvim-dap-ui, nvim-dap-virtual-text, mason-nvim-dap.nvim | Debugger core + UI, installs debug adapters via Mason (`<leader>d*`) |
| `dap-python.lua` | nvim-dap-python | Python launch configs, wired to Mason's debugpy install |
| `overseer.lua` | overseer.nvim | Save/run/rerun arbitrary run configurations for any language (`<leader>r*`) |
| `which-key.lua` | which-key.nvim | Keymap discoverability popup; labels the `<leader>d`/`<leader>r`/etc. groups |
| `indent-detect.lua` | guess-indent.nvim | Detects each buffer's real indentation and matches shiftwidth/tabstop to it |

## Language servers

Managed by Mason, auto-installed via `lua/plugins/lsp.lua`'s
`ensure_installed`: `pyright`, `lua_ls`, `bashls`, `terraformls`, `yamlls`,
`ts_ls`, `cssls`, `html`. `clangd` is enabled separately (not
Mason-installed) since it already ships with Xcode Command Line Tools.

Refactoring/navigation (rename, code actions, references, go-to-definition,
hover) come from Neovim's built-in default LSP keymaps (`:help lsp-defaults`)
once a server attaches — `grn`, `gra`, `grr`, `gri`, `gO`, `K`, `<C-]>`. No
extra plugin or keymap needed for these.

## Debugging

Python and Node.js (JavaScript/TypeScript), via `nvim-dap` + `nvim-dap-ui`.
The `debugpy` and `js-debug-adapter` adapters are installed and kept up to
date by Mason (via `mason-nvim-dap.nvim`, the DAP equivalent of
`mason-lspconfig.nvim`), so `:Mason` shows them alongside the language
servers. Keymaps live under `<leader>d` — `<leader>db` to set a breakpoint,
`<leader>dc` to start/continue (picks a registered config from any window,
not just the source buffer; resumes in place if a session is already stopped
at a breakpoint), `<leader>do`/`di`/`dO` to step over/into/out, `<leader>dt`
to terminate, `<leader>du` to reopen the UI if you close it manually (it
opens/closes itself automatically around a session). Press `<leader>d` and
wait to see the full list via which-key.

Python's adapter/config setup lives in `lua/plugins/dap-python.lua`, wrapping
the `nvim-dap-python` plugin. Node's lives in `lua/config/dap_node.lua`
instead — there's no maintained `nvim-dap-python`-style plugin for it (the
community option, `nvim-dap-vscode-js`, expects an older, manually-built
vscode-js-debug layout that doesn't match what Mason's `js-debug-adapter`
package installs), so that file resolves the Mason install path directly and
registers the `pwa-node` adapter + default `javascript`/`typescript`/
`javascriptreact`/`typescriptreact` configs itself. It's called eagerly from
`dap.lua`'s own `config()` rather than being its own `lua/plugins/*` entry,
since it isn't an installable plugin — same reasoning as
`lua/config/project_run.lua`.

Adding another language later (e.g. C/C++ via `codelldb`) means adding its
adapter to `dap.lua`'s `ensure_installed`, and either writing its own
`dap-<language>.lua` plugin file (if a wrapper plugin exists, following the
`dap-python.lua` pattern) or a `lua/config/dap_<language>.lua` module called
from `dap.lua`'s `config()` (if not, following the `dap_node.lua` pattern) —
including `lazy = false` on `dap.lua` itself either way, so the adapter is
registered from startup rather than only after a buffer of that filetype is
opened (the `<leader>dc` picker works from any window, so it can't rely on
that to lazy-trigger the adapter).

`overseer.lua` sets `dap = true`, which patches `nvim-dap` so a debug
configuration can set `preLaunchTask`/`postDebugTask` to the name of an
overseer task (or `"${defaultBuildTask}"` for whichever task is tagged
`BUILD`) — useful for "build, then debug" workflows once a compiled
language is added; not needed for plain Python/Node scripts.

## Run configurations

`overseer.nvim` provides PyCharm-style saved/rerunnable run configurations
for any language or toolchain (shell commands, npm scripts, make, cargo,
python scripts, ...) — it isn't tied to LSP/DAP language support. Keymaps
live under `<leader>r` — `<leader>rr` to run a task (prompts for a template
or a raw command), `<leader>rl` to rerun the last task, `<leader>ro` to
toggle the task list/output window, `<leader>rq` for quick actions
(restart/stop/dispose) on a selected task. Press `<leader>r` and wait to see
the full list via which-key.

`overseer.lua`'s `component_aliases.default` adds the `open_output`
component (`on_start = "always"`, `focus = false`) on top of overseer's own
defaults, so starting *any* task — custom, `.nvim.lua`-declared, or
auto-detected (npm/make/etc.) — docks its output automatically instead of
running silently until you manually `<leader>ro`.

## Project-local run/debug configs

A project can drop a `.nvim.lua` anywhere in its directory tree and have it
auto-sourced (after a one-time `:help :trust` prompt — it runs arbitrary
Lua, so trust it deliberately, not reflexively). This is the PyCharm-style
"named configuration" mechanism, without adopting any other editor's file
format.

This is **not** core Vim/Neovim `'exrc'` (deliberately left off in
options.lua) — `exrc` only checks the literal cwd once at startup, so it
misses a project's `.nvim.lua` whenever Neovim didn't happen to start with
cwd exactly there. Instead, an autocmd in `lua/config/autocmds.lua` searches
*upward* from cwd on `VimEnter`/`DirChanged` (stopping at `$HOME`, same
nearest-ancestor pattern LSP root-dir detection uses) and reads the file via
`vim.secure.read` — the same trust mechanism `exrc` itself uses, so `:trust`
still manages the allowlist unchanged, it's just found reliably regardless
of how you opened Neovim.

`lua/config/project_run.lua` gives that file one call per config that
registers both a `<leader>rr` run task and a matching `<leader>dc` debug
config from the *same* declaration, instead of repeating the command across
two separate files:

```lua
-- <project root>/.nvim.lua
require("config.project_run").python({
  name = "Run app",
  program = vim.fn.getcwd() .. "/main.py",
  args = { "--flag" }, -- optional, shared by both run and debug
})

require("config.project_run").node({
  name = "Run app",
  program = vim.fn.getcwd() .. "/index.js", -- or npmScript = "dev" for `npm run dev`
  args = { "--flag" }, -- optional, shared by both run and debug
})
```

`dap.lua` and `overseer.lua` are both `lazy = false` specifically so this
works immediately when the autocmd sources the file, rather than needing
the plugins already triggered by a keymap first.

(`nvim-dap` and `overseer.nvim` also natively read `.vscode/launch.json` and
`.vscode/tasks.json` respectively, if you'd rather use that ecosystem's
format for some reason — but `project_run.lua` is the recommended path here
since it states each command once.)

## Adding a new plugin

Drop a new file in `lua/plugins/` (or add to an existing group file if it
belongs there) returning a lazy.nvim spec, e.g.:

```lua
return {
  "author/plugin-name",
  opts = {},
}
```

Restart Neovim (or `:Lazy sync`) to install it, then commit the updated
`lazy-lock.json` alongside your change.

If a plugin backs a keymap or command meant to work from any window — not
scoped to a particular filetype/buffer, like the run/debug pickers, the
fuzzy finder, or the file explorer — set `lazy = false` rather than gating
it behind `ft`/`event`, even though lazy.nvim would otherwise recommend one.
A trigger tied to "some buffer of type X was opened" silently breaks the
moment the keymap fires before that's happened (this bit `dap-python.lua`:
it lazy-loaded on `ft = "python"`, so debugging broke whenever `<leader>dc`
was pressed before any `.py` buffer had been opened this session). Plain
per-buffer editing conveniences (`autopairs.lua`'s `InsertEnter`,
`ale.lua`'s `BufReadPre`, `which-key.lua`'s `VeryLazy`) don't have this
problem and can stay lazy.

## Relationship to Vim

Vim and Neovim used to share one `vimrc` (`has('nvim')` branches handled the
differences). They're now fully independent: Vim keeps `../../vim/.vim/vimrc`
with its own plugin manager (vim-plug) and completion (YouCompleteMe +
jedi-vim); nothing under this directory affects Vim, and nothing in
`vim/.vim` affects Neovim.