feat(indexing): add [indexing] follow-links to walk symlinked directories inside the workspace - #386
Open
liudashuang wants to merge 1 commit into
Open
Conversation
…ries Workspace walks now follow interior directory symlinks when `follow-links = true` is set in .phpantom.toml (off by default). A project that keeps its framework behind a symlink (e.g. `kdhelp -> ../kdhelp`) gets go-to-definition, hover, completion, Find References, and workspace diagnostics for the linked tree, with the symlink spelling preserved in every indexed path and returned URI. All four workspace walkers are switched together so the feature is never half-wired (classes indexed but references missed): - classmap_scanner::discovery::walk_roots (self-scan / PSR-4 / vendor / no-composer classmaps) - references::collect_php_files_gitignore (Find References, Rename, preload Phase 2, Laravel config trees) - analyse::discover_user_files (CLI analyze / fix) - util::collect_php_files (Go-to-implementation) Deliberately unchanged: the Drupal scanner, the Laravel migration walk (its comment refuses to follow links to avoid walking the whole project through a cycle), and composer::discover_subproject_roots. Both walker shapes are cycle-safe: the parallel path uses ignore's check_symlink_loop and skips Err entries, the serial walkers share the same loop detection and consume via flatten(), which drops the loop error instead of panicking. Each is covered by a test. Watcher blind spot documented: the client's watchers only cover the workspace root, so changes on disk inside a linked target (a git pull into the framework) do not trigger a re-index; reload the window after such changes. Closes PHPantom-dev#383
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #383
Problem
A directory symlink found inside the workspace root (e.g.
kdhelp -> ../kdhelp, pointing at a framework tree kept outside the repo) is never descended into: theignorecrate yields the symlink entry itself and stops, so classes, functions and constants under the linked tree are missing from the class index, Find References, workspace diagnostics, and everything else built on the walks. Go-to-definition, hover and completion all fail for those namespaces.Change
Add an opt-in
[indexing] follow-linkssetting (defaultfalse, matching the project's off-by-default posture for anything that widens the scan — the same stance asworkspace-externaland[diagnostics] workspace). When enabled, the workspace walks follow interior directory symlinks; the symlink spelling is preserved in every indexed path and returned URI, so the index, didOpen keys, and references all share one path representation.All four workspace walkers are switched together — a half-wired feature would index classes but still miss references:
classmap_scanner::discovery::walk_roots(self-scan / PSR-4 / vendor / no-composer classmaps)references::collect_php_files_gitignore(Find References, Rename, preload Phase 2, Laravel config trees)analyse::discover_user_files(CLIanalyze/fix)util::collect_php_files(Go-to-implementation Phase 5)The
[indexing]section is the natural home: its only existing key isstrategy, the walker behaviours are all scheduled byindexing::init, and the off-by-default style already has precedent in the same section's peers. The composer pipeline (PSR-4 + vendor walks viainstalled.json) also receives the config value, so a composer project with an interior symlink is not left half-broken.Deliberately not changed:
scan_drupal_directories— Drupal's gitignore-less web-root scanner stays independent.database_schema.rsmigration walk — its comment explicitly refuses to follow links to avoid walking the whole project through a cycle.composer::discover_subproject_roots— a no-op without a composer.json; following would let an external framework's own composer.json be mistaken for a monorepo subproject.Cycle safety
Both walker shapes are safe against symlink cycles:
walk_rootspath hasignore'scheck_symlink_loop(dev+inode handles), and the worker callback skipsErrentries (Error::Looplands there) without unwrapping.collect_php_files_gitignore,discover_user_files,collect_php_files) share the same loop detection inignore'sWalk::next(); they consume entries viaflatten(), which silently drops theErra cycle produces instead of panicking.Both are covered by tests:
walk_roots_follows_symlink_cycle_safely(parallel, self-cycle) andcollect_php_files_gitignore_follows_symlink_cycle_safely(serial, self-cycle).Watcher blind spot (documented, not fixed)
The client's watchers only cover the workspace root, so changes on disk inside a linked target (a
git pullinto the framework) do not trigger a re-index. A file open in the editor re-parses ondidOpen/didChange; everything else needs a window reload or server restart. This is documented in the CHANGELOG,docs/configuration.md, and the config doc comment.Multi-root workspaces
Not addressed, matching the server's existing single-root limitation:
server.rsreadsroot_urionly and ignoresworkspace_folders. Withfollow-linksenabled the behaviour is exactly the same as the single-root case.Windows CI note
CI has no Windows test job (
release.ymlbuilds Windows but doesn't runcargo test), so all new symlink tests use the#[cfg(unix)]/#[cfg(windows)]dual-branch pattern (seescan_directories_follows_a_symlinked_root, discovery_tests.rs). The author verified the tests on macOS; Windows runs are not exercised by CI.Performance measurement (CLI walk/analyze scope)
analyzewall time +8.7%, peak RSS +27.8%, error count 4 → 2 (no new errors).analyzeon the real workspace is in the "up to ~2×" range, but that is the CLI's heaviest one-shot path; interactive LSP use goes through incremental indexing (existing_urisfilter in preload Phase 2), where the +44% file count adds sub-second disk-walk cost.symbol_mapsis pure in-memory (Arc<RwLock<HashMap>>) with no persistence layer, so follow-links affects memory only, not cold-start disk I/O.Tests
14 new tests, all passing (
cargo +1.97.0 test --lib→ 5893 passed, 0 failed):follow-links = trueparses; absent defaults tofalseChecklist
[indexing]table)