From 770c9ae01f4d6b45924e4d43828e1114055233b5 Mon Sep 17 00:00:00 2001 From: ray Date: Sun, 7 Dec 2025 18:33:51 +0000 Subject: [PATCH] update --- .github/copilot-instructions.md | 168 ++++++++++++++++++++++++++++++++ MIGRATION_PLAN.md | 28 +++++- after/ftplugin/php.lua | 62 ++++++++++++ lua/settings.lua | 3 + 4 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 after/ftplugin/php.lua diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..9ac3fe3 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,168 @@ +# 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 +│ └── 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()`. + +```lua +-- lua/plugins/lsp.lua pattern: +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 + 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 + +### 2. Project-Local Configuration +**Format**: `.nvim.lua` at project root, returns: +```lua +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 +```bash +# 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.lua` → `on_new_config` → `new_config.settings` → `workspace/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 diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md index 04e2a33..2c7dbb0 100644 --- a/MIGRATION_PLAN.md +++ b/MIGRATION_PLAN.md @@ -108,13 +108,32 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date. ## Phase 4 — Navigation ## Phase 4.1 — Confirm scope and priorities -- [ ] Confirm scope and priorities for this phase +- [x] Confirm scope and priorities for this phase +- [x] Decision: Skip Neo-tree in favor of netrw for navigation/visual context +- [x] Decision: Use netrw for project structure visualization and file preview +- [x] Decision: Telescope as primary "find file" tool +- [x] Decision: Add Oil.nvim for file manipulation (handles buffer sync on rename/move/delete) +- [x] Note: Oil.nvim is for evaluation; alternative is mini.files if too heavy -## Phase 4.2 — Neo-tree setup -- [ ] Add `neo-tree.nvim` with per-tab roots, sidebar toggle, floating view, and preview keymap (no buffer pollution) +## Phase 4.2 — Configure netrw +- [ ] Configure netrw with tree view, preview split, and basic settings (no banner, sensible defaults) +- [ ] Add simple keymaps for opening netrw (e.g., in split, tab, or current window) + +## Phase 4.2.1 — PHP gf enhancement +- [x] Add PHP `includeexpr` for intelligent `gf` behavior (handles `__DIR__`, `__FILE__`, `dirname(__FILE__)` patterns) +- [x] Created `after/ftplugin/php.lua` with path resolution for WordPress/PHP patterns +- [x] Tested and validated with test-gf.php ## Phase 4.3 — Telescope setup -- [ ] Add `telescope.nvim` (+ optional `telescope-fzf-native.nvim`) and basic pickers +- [ ] Add `telescope.nvim` + `telescope-fzf-native.nvim` for fuzzy finding +- [ ] Configure basic pickers: `find_files`, `live_grep`, `buffers`, `help_tags`, `oldfiles` +- [ ] Add sensible default keymaps + +## Phase 4.4 — Oil.nvim for file manipulation +- [ ] Add `stevearc/oil.nvim` for filesystem operations (rename, create, delete, copy, move) +- [ ] Configure to handle buffer name sync on file operations +- [ ] Keep minimal - use only when shell operations would cause buffer issues +- [ ] Note: Evaluate practicality; can switch to `mini.files` if preferred ## Phase 5 — Treesitter @@ -231,3 +250,4 @@ Decisions & Changes: - **Project config format**: `.nvim.lua` files return `{ lsp = { [server_name] = { settings = {...} } } }`. - **Security model**: `secure` mode prompts user to trust `.nvim.lua` files before execution (one-time per file hash). - **Settings loading**: `on_new_config` hook loads project settings from actual `root_dir` (not cwd), ensuring correct behavior across different workspace structures. +- **Phase 4 Navigation Strategy**: Skipped Neo-tree in favor of built-in netrw for visual context and project structure browsing. Telescope for fuzzy finding. Oil.nvim for file manipulation (evaluation phase; alternative is mini.files). Rationale: User needs visual context and preview, not per-tab roots or complex tree features. File manipulation primarily done in shell, but Oil.nvim handles buffer sync issues when renaming/moving files. diff --git a/after/ftplugin/php.lua b/after/ftplugin/php.lua new file mode 100644 index 0000000..cb80682 --- /dev/null +++ b/after/ftplugin/php.lua @@ -0,0 +1,62 @@ +-- PHP-specific settings and enhancements +-- Loaded automatically for PHP files + +-- Enable includeexpr for intelligent gf (goto file) behavior +-- Handles common WordPress/PHP path patterns like: +-- require_once __DIR__ . '/../path/to/file.php'; +-- include __FILE__ . '/relative/path.php'; +vim.opt_local.includeexpr = "v:lua.resolve_php_gf_path(v:fname)" + +-- Global function to resolve PHP file paths for gf command +function _G.resolve_php_gf_path(fname) + local line = vim.api.nvim_get_current_line() + local current_dir = vim.fn.expand('%:p:h') + local current_file = vim.fn.expand('%:p') + + -- Pattern 1: __DIR__ . '/path' or __DIR__ . '/../path' + -- Most common in modern PHP/WordPress + local path = line:match("__DIR__%s*%.%s*['\"]([^'\"]+)['\"]") + if path then + path = current_dir .. path + return vim.fn.simplify(path) + end + + -- Pattern 2: __FILE__ . '/path' + -- Less common but appears in legacy WordPress code + -- __FILE__ resolves to the current file's full path, so we use its directory + path = line:match("__FILE__%s*%.%s*['\"]([^'\"]+)['\"]") + if path then + -- __FILE__ is the file itself, but when concatenated with a path, + -- it's typically used like __DIR__, so we use the directory + path = current_dir .. path + return vim.fn.simplify(path) + end + + -- Pattern 3: dirname(__FILE__) . '/path' + -- Old-style equivalent to __DIR__ + path = line:match("dirname%s*%(%s*__FILE__%s*%)%s*%.%s*['\"]([^'\"]+)['\"]") + if path then + path = current_dir .. path + return vim.fn.simplify(path) + end + + -- Pattern 4: Simple quoted path in require/include statements + -- Fallback for basic relative paths + path = line:match("require[_%w]*%s*['\"]([^'\"]+)['\"]") + if path then + -- Try relative to current file's directory + path = current_dir .. '/' .. path + return vim.fn.simplify(path) + end + + path = line:match("include[_%w]*%s*['\"]([^'\"]+)['\"]") + if path then + -- Try relative to current file's directory + path = current_dir .. '/' .. path + return vim.fn.simplify(path) + end + + -- Fallback: return as-is (normal gf behavior) + -- This allows standard gf to work for simple filenames + return fname +end diff --git a/lua/settings.lua b/lua/settings.lua index 07dd898..a06da0b 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -27,3 +27,6 @@ vim.opt.listchars = { space = '·', } +-- Enable line numbers and relative line numbers +vim.opt.number = true +vim.opt.relativenumber = true \ No newline at end of file