Document the neovim setup in its own README
Keymaps are discoverable in-editor by pressing <Space> and pausing, and are
written down separately, so they are deliberately not duplicated here. What was
not recorded anywhere was the surrounding context.
Covers the 0.12 floor, since autocomplete, pumborder, pummaxwidth, the nearest
completeopt value and vim.pack all require it; what bootstrap.sh symlinks where,
and why config/ is linked in as lua/config; and the vim.pack convention of one
file per plugin with versions pinned in the tracked lock file.
The rest is the behaviours that read as bugs until you know they are deliberate.
A standalone .php file gets no LSP at all, because phpactor sets
workspace_required and wants a project marker. Spell checking is on globally, in
code buffers too. netrw is disabled so `nvim .` opens nvim-tree, on the right.
Diagnostic signs are letters rather than icons.
Also restates the gitsigns global-mapping rationale already in the source, since
folding those maps into on_attach is the obvious tidy-up and it silently stops
them appearing in the which-key popup.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 17:06:52 +01:00
|
|
|
# neovim
|
|
|
|
|
|
|
|
|
|
Requires Neovim **0.12+**. Several options used here (`autocomplete`,
|
|
|
|
|
`pumborder`, `pummaxwidth`, and `nearest` in `completeopt`) only exist from 0.12,
|
|
|
|
|
and plugins are managed with `vim.pack`, which landed in 0.12 too.
|
|
|
|
|
|
|
|
|
|
Day-to-day keymaps are not listed here — press `<Space>` and pause, and
|
|
|
|
|
which-key shows what is available. This file covers the things that are *not*
|
|
|
|
|
discoverable that way.
|
|
|
|
|
|
|
|
|
|
## Layout
|
|
|
|
|
|
|
|
|
|
`bootstrap.sh` symlinks the pieces individually rather than linking the whole
|
|
|
|
|
directory, because `~/.config/nvim` also holds state Neovim writes itself:
|
|
|
|
|
|
|
|
|
|
| repo | symlinked to |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| `init.lua` | `~/.config/nvim/init.lua` |
|
|
|
|
|
| `config/` | `~/.config/nvim/lua/config` |
|
|
|
|
|
| `lsp/phpactor.lua` | `~/.config/nvim/lsp/phpactor.lua` |
|
|
|
|
|
| `nvim-pack-lock.json` | `~/.config/nvim/nvim-pack-lock.json` |
|
|
|
|
|
|
|
|
|
|
`config/` is linked in as `lua/config` so everything is reachable as
|
|
|
|
|
`require('config.…')`, which is what `init.lua` and `config/init.lua` do.
|
|
|
|
|
|
|
|
|
|
## Plugins
|
|
|
|
|
|
|
|
|
|
Managed by `vim.pack`, so there is no plugin manager to bootstrap. Each plugin
|
|
|
|
|
gets a file under `config/plugins/` that calls `vim.pack.add()` and then
|
|
|
|
|
configures itself, and `config/plugins/init.lua` requires them in turn.
|
|
|
|
|
|
|
|
|
|
Update with `:lua vim.pack.update()`. Versions are pinned in
|
|
|
|
|
`nvim-pack-lock.json`, which is tracked — commit it after an update so the other
|
|
|
|
|
machine gets the same set.
|
|
|
|
|
|
|
|
|
|
> [!NOTE]
|
|
|
|
|
> nvim-web-devicons needs a Nerd Font in the terminal or the icons render as
|
|
|
|
|
> tofu. `brew.sh` installs one for ghostty.
|
|
|
|
|
|
|
|
|
|
## Defaults worth knowing
|
|
|
|
|
|
|
|
|
|
- **Spell checking is on globally**, `en_gb` — in every buffer, code included,
|
|
|
|
|
not just prose filetypes.
|
|
|
|
|
- **Autocompletion is on** via Neovim's own `vim.o.autocomplete`, with a rounded
|
|
|
|
|
popup capped at 40 columns. No completion plugin is involved.
|
|
|
|
|
- **The sign column is always shown**, so git and diagnostic signs appearing do
|
|
|
|
|
not shift the text sideways.
|
|
|
|
|
- **netrw is disabled** and nvim-tree takes over directory buffers, so `nvim .`
|
|
|
|
|
opens the tree rather than netrw. The tree opens on the **right**.
|
Show gitignored files in nvim-tree and follow the open buffer
Two nvim-tree defaults that were quietly getting in the way.
filters.git_ignored defaults to true, so gitignored files are hidden. The
symptom was .env being absent from a project while .env.example sat right
there, which reads as a dotfile filter but is not: filters.dotfiles is already
false, so dotfiles show. The filter is git-aware, and .env was simply reported
ignored. Verified in a throwaway repo ignoring .env and vendor/ — both appear
now, and neither did before, with the ignored glyph to distinguish them.
The trade-off is real and is the reason for the default: vendor/ and
node_modules/ now show too. filters.exclude = { '.env' } was the narrower
option, exempting one name while keeping the rest hidden. Chose the broad
setting deliberately; `I` in the tree hides them again per session.
update_focused_file.enable defaults to false, so the tree never moved when a
file was opened from fzf-lua. Enabling it reveals the current buffer on
BufEnter, uncollapsing folders to reach it. Verified on a nested fixture:
opening src/deep/nested/target.php expanded all three folders and put the tree
cursor on the file. Focus stays in the file window, so it reveals rather than
steals, and it follows every buffer switch, not just fzf-lua.
Left update_root off. It would re-root the tree at files outside the current
root, which makes the tree wander into vendor/ — worse now that ignored files
are visible and easier to open by accident.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 21:18:55 +01:00
|
|
|
- **The tree shows gitignored files**, unlike nvim-tree's default — so `.env` is
|
|
|
|
|
visible, at the cost of `vendor/`, `node_modules/` and friends also showing.
|
|
|
|
|
`I` in the tree hides them again for the session.
|
|
|
|
|
- **The tree follows the current buffer**, expanding folders to reveal whatever
|
|
|
|
|
you open — including files opened from fzf-lua. It does not change the tree
|
|
|
|
|
root to do so, so opening a file from outside the root won't reveal it.
|
Document the neovim setup in its own README
Keymaps are discoverable in-editor by pressing <Space> and pausing, and are
written down separately, so they are deliberately not duplicated here. What was
not recorded anywhere was the surrounding context.
Covers the 0.12 floor, since autocomplete, pumborder, pummaxwidth, the nearest
completeopt value and vim.pack all require it; what bootstrap.sh symlinks where,
and why config/ is linked in as lua/config; and the vim.pack convention of one
file per plugin with versions pinned in the tracked lock file.
The rest is the behaviours that read as bugs until you know they are deliberate.
A standalone .php file gets no LSP at all, because phpactor sets
workspace_required and wants a project marker. Spell checking is on globally, in
code buffers too. netrw is disabled so `nvim .` opens nvim-tree, on the right.
Diagnostic signs are letters rather than icons.
Also restates the gitsigns global-mapping rationale already in the source, since
folding those maps into on_attach is the obvious tidy-up and it silently stops
them appearing in the which-key popup.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 17:06:52 +01:00
|
|
|
- **Diagnostic signs are letters** (`E` `W` `I` `H`) rather than icons, and do
|
|
|
|
|
not update while you are in insert mode.
|
|
|
|
|
|
|
|
|
|
## PHP / LSP
|
|
|
|
|
|
|
|
|
|
`lsp/phpactor.lua` is the only language server configured, enabled from
|
|
|
|
|
`config/lsp.lua`. It expects `phpactor` on `PATH` (`brew.sh` does not install
|
|
|
|
|
it — it is a Composer global install).
|
|
|
|
|
|
|
|
|
|
It sets `workspace_required`, so it only attaches inside a project with one of
|
|
|
|
|
`.git`, `composer.json`, `.phpactor.json` or `.phpactor.yml` — a loose `.php`
|
|
|
|
|
file opened on its own gets no LSP, which is intentional rather than broken.
|
|
|
|
|
|
|
|
|
|
phpactor's bundled phpstan and psalm integrations are both disabled, so
|
|
|
|
|
diagnostics come from phpactor itself. Run those tools separately if you want
|
|
|
|
|
them.
|
|
|
|
|
|
|
|
|
|
> [!IMPORTANT]
|
|
|
|
|
> gitsigns keymaps are set globally, not from `on_attach`. Gitsigns attaches
|
|
|
|
|
> asynchronously, after which-key has already built its keymap tree for the
|
|
|
|
|
> buffer, and which-key only rebuilds on `BufReadPost`/`BufNew`/`LspAttach` — so
|
|
|
|
|
> buffer-local maps added later never appear in the popup. The gitsigns API
|
|
|
|
|
> no-ops in buffers it has not attached to, which is what makes global maps safe
|
|
|
|
|
> here. Don't “fix” this by moving them into `on_attach`.
|