nvim/.github/copilot-instructions.md

7.2 KiB

Neovim Config - AI Agent Instructions

Project Overview

Modern Lua-first Neovim configuration using lazy.nvim, currently in active migration from legacy Vimscript. Target: minimal, fast, tab-per-context workflow with Neo-tree, Telescope, LSP, and Copilot.

Critical: Read AGENTS.md first—it defines all coding rules, migration strategy, and what NOT to reintroduce.

Architecture & Files

~/.config/nvim/
├── init.lua              # Entry point: loads settings/keymaps/autocmds, bootstraps lazy.nvim
├── lua/
│   ├── settings.lua      # Non-plugin vim options (exrc, secure, spelllang, listchars)
│   ├── keymaps.lua       # Non-plugin keymaps
│   ├── autocmds.lua      # Non-plugin autocommands
│   ├── abbreviations.lua # Insert-mode abbreviations (typo corrections)
│   ├── netrw-config.lua  # Netrw configuration
│   └── plugins/          # One file per plugin/domain (lazy.nvim specs)
│       ├── lsp.lua       # LSP via vim.lsp.config + vim.lsp.enable (Neovim 0.11+)
│       ├── cmp.lua       # Completion: nvim-cmp + LuaSnip
│       └── mason*.lua    # LSP server installation
├── AGENTS.md             # PRIMARY: All rules, migration plan, do-not-reintroduce list
├── MIGRATION_PLAN.md     # Phase-by-phase checklist (source of truth for progress)
├── README.md             # Decision log and design rationale
└── legacy/               # Archived Vimscript (do not edit; reference only)

Critical Patterns

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/plugins/lsp.lua pattern:
-- Define config
vim.lsp.config(server_name, {
  capabilities = capabilities,
  -- 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,
})

Key decisions:

  • No neoconf (incompatible with Neovim 0.11+, issue #116)
  • Native exrc + secure for project config (see settings.lua)
  • 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:

return {
  lsp = {
    intelephense = {
      settings = {
        intelephense = {
          environment = {
            includePaths = { "/absolute/path/to/external/lib" }
          }
        }
      }
    }
  }
}

Security: vim.opt.secure = true prompts user before loading (one-time per file hash). Trusted files stored in ~/.local/state/nvim/trust.

3. Plugin Management

  • lazy.nvim: All plugins in lua/plugins/ as individual files returning spec tables
  • Mason: Installs LSP servers only; custom lspconfig setup retained
  • Plugin specs: Use opts = {} for defaults, config = function(_, opts) for setup
  • Approval policy: Do not install unlisted plugins without explicit user confirmation

4. Tab-Per-Context Workflow (Future Phase 4)

  • Each tab maintains own Neo-tree root
  • Splits stay within tabs
  • Neo-tree: sidebar toggle + floating view + preview (no buffer pollution)
  • Heavy PHP/HTML/JS/Markdown usage (WordPress plugin dev)

Conventions

Code Style

  • Lua APIs only: vim.opt, vim.keymap.set, vim.api.nvim_create_autocmd
  • No Vimscript: Avoid vim.cmd blocks unless strictly necessary
  • Keymaps: { silent = true, noremap = true } by default
  • Autocommands: Group via vim.api.nvim_create_augroup, narrow scope

File Organization

  • One plugin = one file in lua/plugins/
  • Keep plugin config self-contained in its spec
  • Settings/keymaps/autocmds: only non-plugin logic in respective files
  • No global state; return tables from modules

Migration Process

  1. Check MIGRATION_PLAN.md for current phase and priorities
  2. Before large changes: Update plan via CLI update_plan tool (if available)
  3. After any change: Immediately update MIGRATION_PLAN.md (check off items, add notes)
  4. After decisions: Update README.md decisions log
  5. Execute phases via subphases (N.x); one bullet = one implement-and-test step
  6. Archive legacy files to legacy/ instead of deleting

Do NOT Reintroduce (from AGENTS.md)

  • Custom Vimscript tabline/statusline/foldtext
  • Legacy autocommands (cursorline/column per window)
  • CoC or CoC-specific config
  • netrw settings (Neo-tree replaces it)
  • Providers for ruby/perl/node (disabled unless required)
  • Auto-reload vimrc-on-write templates
  • cursorcolumn and old folding logic (UFO will handle folding)
  • neoconf plugin (incompatible with Neovim 0.11+)

Developer Workflows

Testing Changes

# Quick syntax check
nvim --headless -c 'lua print("Config loads")' -c 'quitall'

# Check LSP status in test workspace
cd ~/.config/nvim/WORKSPACE_TEST
nvim site/external.php  # Then :LspInfo

# Validate goto-definition for external libs
# In external.php, cursor on Util::greetExternal, press gd

Validation Workspaces

  • WORKSPACE_TEST/: PHP project with .nvim.lua and .git
  • EXTERNAL_TEST/: External library referenced via includePaths
  • Test that gd resolves symbols in EXTERNAL_TEST from WORKSPACE_TEST

Current Migration Status (as of 2025-12-07)

  • Phase 1-2: Archive legacy, bootstrap lazy.nvim
  • Phase 3: Core editing & LSP complete
    • Settings, completion, LSP servers, Mason installed
    • Native exrc + vim.lsp.config migration done
    • Project-local .nvim.lua working
  • ⏸️ Phase 4+: Navigation (Neo-tree, Telescope), Treesitter, UX plugins pending

Integration Points

External Dependencies

  • Neovim 0.11+: Required for vim.lsp.config API
  • LSP Servers (via Mason): lua_ls, ts_ls, html, cssls, jsonls, bashls, marksman, intelephense
  • PHP: intelephense with single_file_support = false, uses environment.includePaths for external libs

Cross-Component Communication

  • LSP settings flow: .nvim.luaon_new_confignew_config.settingsworkspace/didChangeConfiguration
  • Completion: cmp-nvim-lsp provides capabilities → lspconfig → LSP servers

Common Issues

lua_ls Won't Start

Mason-installed lua_ls may fail with missing libbfd-2.38-system.so. Install via system package manager instead.

LSP Root Detection

If parent repo picked as root, create empty .git or .nvimroot marker in intended workspace root.

Duplicate gd Results (Fixed)

Was caused by settings loading at wrong time; fixed by using on_new_config with actual root_dir.

When Stuck

  1. Check MIGRATION_PLAN.md for known issues and current phase
  2. Review AGENTS.md for rules about what to avoid
  3. Read README.md decisions log for context on past choices
  4. Validate with test workspaces before modifying production config