fix project settings regression

This commit is contained in:
Ray Elliott 2025-12-07 19:50:54 +00:00
parent 976949c6f4
commit 71beb0fb78
4 changed files with 184 additions and 26 deletions

View File

@ -29,23 +29,35 @@ Modern Lua-first Neovim configuration using lazy.nvim, currently in active migra
### 1. LSP Configuration (Phase 3 Complete)
**Modern API (Neovim 0.11+)**: Uses `vim.lsp.config()` + `vim.lsp.enable()`, NOT `require('lspconfig')[server].setup()`.
**CRITICAL (2025-12-07)**: Neovim 0.11+ does NOT support `on_new_config` callback. Use `LspAttach` autocmd instead.
```lua
-- lua/plugins/lsp.lua pattern:
-- Define config
vim.lsp.config(server_name, {
capabilities = capabilities,
on_attach = on_attach,
on_new_config = function(new_config, root_dir)
-- Load project settings from root_dir/.nvim.lua
-- NO on_attach here, NO on_new_config here
})
-- Enable server
vim.lsp.enable({ "lua_ls", "intelephense", ... })
-- Load project settings via LspAttach autocmd
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
-- Load .nvim.lua from client.root_dir
-- Merge into client.settings
-- Send workspace/didChangeConfiguration notification
end,
})
vim.lsp.enable({ "lua_ls", "intelephense", ... })
```
**Key decisions**:
- No neoconf (incompatible with Neovim 0.11+, issue #116)
- Native `exrc` + `secure` for project config (see settings.lua)
- `on_new_config` hook loads from actual `root_dir`, not cwd
- `workspace/didChangeConfiguration` sent in `on_attach` for servers like intelephense
- Project settings loaded via `LspAttach` autocmd (NOT `on_new_config` which is deprecated)
- `workspace/didChangeConfiguration` sent after merging settings into `client.settings`
### 2. Project-Local Configuration
**Format**: `.nvim.lua` at project root, returns:

View File

@ -18,6 +18,8 @@ Record every decision here with a short rationale. Append new entries; do not re
- 2025-12-06: Use `mason.nvim` + `mason-lspconfig.nvim` to install/manage LSP servers; keep custom lspconfig setup; include `.neoconf.json` in root detection.
- For PHP validation inside this repo: we require `.neoconf.json` to attach `intelephense` to avoid the repos `.git` being chosen as the LSP root.
- 2025-12-06: Plugin approval policy — other plugins are allowed, but do not install any plugin not listed without explicit confirmation.
- 2025-12-07: **CRITICAL REGRESSION FIX**: Neovim 0.11+ does NOT support `on_new_config` callback in `vim.lsp.config()`. Project-local settings (`.nvim.lua`) must be loaded via `LspAttach` autocmd instead. The `on_new_config` hook was silently ignored, causing empty settings and breaking goto-definition for external includes. Solution: Use `LspAttach` to load project settings, merge into `client.settings`, and send `workspace/didChangeConfiguration` notification.
- 2025-12-07: Native `exrc` + `.nvim.lua` finalized as project-local config approach (replaces neoconf, which is incompatible with Neovim 0.11+ native LSP API). Security via `vim.opt.secure = true`.
## Project-Local Configuration (design)

View File

@ -0,0 +1,129 @@
# Critical Regression Fix: LSP Project Settings Not Loading (2025-12-07)
## Summary
`gd` (goto definition) stopped working for external library references in `WORKSPACE_TEST/site/external.php`. Even restoring to a git commit where it previously worked didn't fix it, indicating an **environment change** rather than a code regression.
## Root Cause
**Neovim 0.11+ removed support for the `on_new_config` callback in `vim.lsp.config()`.**
The previous implementation used `on_new_config` to load project-local settings from `.nvim.lua`:
```lua
vim.lsp.config(server_name, {
on_new_config = function(new_config, root_dir)
-- Load project settings and merge into new_config.settings
local project_settings = load_project_lsp_settings(server_name, root_dir)
if project_settings.settings then
new_config.settings = vim.tbl_deep_extend("force",
new_config.settings or {},
project_settings.settings)
end
end,
})
```
**Problem**: This callback was **silently ignored** in Neovim 0.11+, resulting in:
- Empty `client.settings` (confirmed via `:LspInfo` showing `Settings: {}`)
- No `includePaths` sent to intelephense
- `gd` failing with "No locations found" for external library symbols
## Symptoms
1. `:LspInfo` showed `Settings: {}` for intelephense
2. `gd` on `\ExtLib\Src\Util::greetExternal()` returned "No locations found"
3. Local (workspace) symbol navigation worked fine
4. Git history restore didn't help (environment issue, not code issue)
## Solution
Use the `LspAttach` autocmd instead of `on_new_config` to load and apply project settings:
```lua
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('lsp_project_settings', { clear = true }),
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
if not client then return end
-- Load project-specific settings from .nvim.lua at client.root_dir
local project_settings = load_project_lsp_settings(client.name, client.root_dir)
if project_settings.settings then
-- Merge into client.settings
client.settings = vim.tbl_deep_extend("force",
client.settings or {},
project_settings.settings)
-- Notify server of updated settings
vim.schedule(function()
client.notify("workspace/didChangeConfiguration",
{ settings = client.settings })
end)
end
end,
})
```
## Validation
After fix:
```bash
cd WORKSPACE_TEST
nvim --headless +'e site/external.php' +'lua vim.wait(3000); local clients = vim.lsp.get_clients({name="intelephense"}); if clients[1] then print("Settings:", vim.inspect(clients[1].settings)) end' +quit
```
Output:
```lua
Settings: {
intelephense = {
environment = {
includePaths = { "/home/ray/.config/nvim/EXTERNAL_TEST" }
}
}
}
```
✅ Settings now correctly loaded and `gd` works for external symbols.
## Key Differences: Neovim 0.10 vs 0.11 LSP API
### Old API (lspconfig plugin, Neovim < 0.11)
```lua
require('lspconfig').intelephense.setup({
on_new_config = function(new_config, root_dir)
-- Called when creating new client
end,
})
```
### New API (native vim.lsp.config, Neovim 0.11+)
```lua
-- Define config
vim.lsp.config('intelephense', { ... })
-- Enable server
vim.lsp.enable('intelephense')
-- Handle dynamic settings via autocmd
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
-- Access client, load settings, notify server
end,
})
```
## Documentation Updated
- `README.md`: Added decision log entry for 2025-12-07
- `.github/copilot-instructions.md`: Documents LspAttach pattern
- `AGENTS.md`: Already notes "Native exrc + vim.lsp.config migration done"
## Prevention
- Always test LSP functionality after Neovim version upgrades
- Check `:help news` for API changes
- Validate `:LspInfo` settings are populated correctly
- Test with external library references, not just workspace files
## Files Modified
- `lua/plugins/lsp.lua`: Replaced `on_new_config` with `LspAttach` autocmd
## Timeline
- **Before 2025-12-07**: `on_new_config` worked in older Neovim version
- **2025-12-07**: Neovim upgraded to 0.11.5, `on_new_config` silently ignored
- **2025-12-07**: Diagnosed via `:LspInfo` showing empty settings
- **2025-12-07**: Fixed with `LspAttach` pattern, verified working

View File

@ -9,6 +9,9 @@ return {
-- Load project-local LSP settings from .nvim.lua
local function load_project_lsp_settings(server_name, root_dir)
if not root_dir then
return {}
end
local config_file = root_dir .. "/.nvim.lua"
if vim.fn.filereadable(config_file) == 1 then
local ok, project_config = pcall(dofile, config_file)
@ -19,14 +22,8 @@ return {
return {}
end
local function on_attach(client, bufnr)
-- Send settings to server after attach (many servers need didChangeConfiguration)
if client.config.settings and next(client.config.settings) then
vim.schedule(function()
client.notify("workspace/didChangeConfiguration", { settings = client.config.settings })
end)
end
-- Setup keymaps on attach
local function setup_keymaps(bufnr)
local map = function(mode, lhs, rhs, desc)
vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, silent = true, noremap = true, desc = desc })
end
@ -37,6 +34,36 @@ return {
map('n', 'K', vim.lsp.buf.hover, 'LSP: Hover')
end
-- Global LspAttach handler to load project settings and setup keymaps
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('lsp_project_settings', { clear = true }),
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
if not client then
return
end
-- Setup keymaps for this buffer
setup_keymaps(args.buf)
-- Load and send project-specific settings
local project_settings = load_project_lsp_settings(client.name, client.root_dir)
if project_settings.settings then
-- Merge with existing settings
client.settings = vim.tbl_deep_extend(
"force",
client.settings or {},
project_settings.settings
)
-- Send didChangeConfiguration to update the server
vim.schedule(function()
client.notify("workspace/didChangeConfiguration", { settings = client.settings })
end)
end
end,
})
-- Configure servers using vim.lsp.config (Neovim 0.11+ API)
local function configure(server_name, opts)
opts = opts or {}
@ -44,18 +71,6 @@ return {
-- Merge with defaults
local config = vim.tbl_deep_extend("force", {
capabilities = capabilities,
on_attach = on_attach,
on_new_config = function(new_config, root_dir)
-- Load project-specific settings based on actual root_dir
local project_settings = load_project_lsp_settings(server_name, root_dir)
if project_settings.settings then
new_config.settings = vim.tbl_deep_extend(
"force",
new_config.settings or {},
project_settings.settings
)
end
end,
}, opts)
-- Configure the server using the new API