Skip to content

feat: exclude globs and extra PHP extensions for indexing - #311

Open
syntlyx wants to merge 3 commits into
PHPantom-dev:mainfrom
syntlyx:feat/indexing-excludes
Open

feat: exclude globs and extra PHP extensions for indexing#311
syntlyx wants to merge 3 commits into
PHPantom-dev:mainfrom
syntlyx:feat/indexing-excludes

Conversation

@syntlyx

@syntlyx syntlyx commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Each commit is one logical change and carries its own description:

  1. fix: classify vendor files correctly under a symlinked workspace root
  2. feat: exclude globs and extra PHP extensions for indexing
  3. fix: index Drupal test directories so test base classes resolve

Summary

Ships the server side of backlog item X9 (docs/todo/indexing.md): user-configurable file filters for workspace discovery.

[indexing]
exclude = ["generated", "web/sites/default/files", "tests/"]
extensions = ["module", "install", "theme"]
  • exclude — patterns in gitignore syntax relative to the workspace root: a bare name matches at any depth, a pattern containing / anchors to the root, a trailing / restricts to directories, a leading ! re-includes. Honored by every workspace walker (fallback full scan, PSR-4/vendor scans, the Drupal web-root scan, preload, go-to-implementation) and by the file watcher, so events for excluded paths are dropped too. Excludes apply to background discovery only — files opened in the editor are always served.
  • extensions — extra file extensions (without the dot) treated as PHP source, e.g. Drupal's .module/.inc/.theme outside the auto-detected web root. Each gets its own didChangeWatchedFiles watcher so edits refresh the index.

Implementation notes

  • Patterns compile once into a new classmap_scanner::IndexFilters (backed by ignore::gitignore::GitignoreBuilder), cached on the Backend, invalidated on config reload — no glob compilation on any per-file path. Empty config is a single Option branch in the walkers, so the default configuration costs nothing.
  • GitignoreBuilder was chosen over ignore's Override: Override's whitelist-first semantics would invert the meaning of an exclude list containing ! re-includes.
  • Invalid patterns are skipped with a warning (same policy as [[diagnostics.ignore]]), never failing config load.
  • config-schema.json and docs/configuration.md updated; changelog entry added.
  • X9 in the backlog is rewritten to its remaining scope: forwarding the editor's files.exclude / files.associations from the editor extensions to the server (initializationOptions / workspace/configuration), which needs client-side work.

Bundled fixes (separate commits)

Drupal test base classes resolve. The Drupal web-root scanner skipped every directory named tests/Tests, but module tests extend base classes that live under core/tests/ (Drupal\Tests\UnitTestCase, Drupal\KernelTests\KernelTestBase, …), and the gitignore-aware workspace scan never reaches core/ either — so on a real Drupal project every module test reported Class 'Drupal\Tests\UnitTestCase' not found (very visible now that workspace-wide diagnostics are on by default). Test directories are indexed by default; the classmap's first-wins merge handles duplicate fixture FQNs, and [indexing] exclude replaces the hardcoded name filter for projects that want a smaller index.

Vendor classification under a symlinked workspace root. Pre-existing bug reproducible on clean main: on a workspace whose real path goes through a symlink (macOS /var/private/var, link-farm checkouts), vendor files were classified as project code — hover lost the package provenance badge, vendor providers were scanned as user code, and the analyse CLI walked the entire vendor tree. Scanned paths are canonicalized but the vendor dir was stored raw-only; add_vendor_dir now records the canonical form alongside the raw one (mirroring what the vendor URI prefixes already do), and analyse's skip list is canonicalized. Fixes 4 tests that fail on any macOS checkout of main.

Testing

  • Unit tests for glob semantics (anchoring, depth, dir-only, ! re-includes, invalid-pattern skip, extension normalization).
  • End-to-end walker tests: excluded dirs pruned in the workspace scan and the Drupal scan; .module files discovered via extensions.
  • Config parse test for the new keys.
  • cargo clippy -D warnings, cargo fmt, and the full test suite pass; both commits build and pass independently (bisect-safe).

Refs #48

Checklist

If applicable:

  • I have updated CHANGELOG.md
  • I have updated the documentation (README.md, docs/, examples/)
  • I have updated the config schema (config-schema.json)
  • I have added/updated tests to cover my changes
  • I fully understand the code that I am submitting (what it does,
    how it works, how it's organized), including any code drafted by an LLM.
  • For any prose generated by an LLM, I have proof-read and copy-edited with
    an eye towards deleting anything that is irrelevant, clarifying anything
    that is confusing, and adding details that are relevant. This includes,
    for example, commit descriptions, PR descriptions, and code comments.

syntlyx added 3 commits August 1, 2026 18:58
Scanned file paths are canonicalized while the vendor directory was
stored raw-only, so on a workspace whose real path goes through a
symlink (macOS /var -> /private/var, link-farm checkouts) vendor files
never prefix-matched the vendor directory. Hover lost the package
provenance badge, completion ranked vendor classes as project symbols,
and vendor service providers were scanned as if they were user code.
add_vendor_dir now records the canonical path alongside the raw one,
mirroring what the vendor URI prefixes already do.

The analyse CLI had the mirror-image bug: it canonicalized each walked
entry but compared against the raw vendor path, so analyse descended
into the whole vendor tree. The skip list is now canonicalized too.
Add `[indexing] exclude` and `[indexing] extensions` to
`.phpantom.toml`. Exclude patterns use gitignore semantics relative to
the workspace root and are honored by every workspace walker (the
fallback full scan, PSR-4 and vendor scans, the Drupal web-root scan,
the preload and go-to-implementation walkers) and by the file watcher,
so generated code and test fixtures stay out of the index. Extra
extensions let non-.php PHP source (e.g. Drupal's .module, .inc,
.theme) be discovered by background scans outside the Drupal-specific
directories, with matching file watchers so those files refresh the
index on change.

Patterns compile once into classmap_scanner::IndexFilters, cached on
the Backend and invalidated on config reload, so no glob compilation
happens per file. Gitignore matching (GitignoreBuilder) was chosen
over the ignore crate's Override, whose whitelist-first semantics
would invert an exclude list containing `!` re-includes.

This ships the server side of backlog item X9; the item now tracks the
remaining client-side work of forwarding the editor's files.exclude /
files.associations settings to the server.

Refs PHPantom-dev#48
The Drupal web-root scanner skipped every directory named tests/ or
Tests/ to avoid duplicate fixture classes. But module tests extend base
classes that live under core/tests/ (Drupal\Tests\UnitTestCase,
Drupal\KernelTests\KernelTestBase, ...) and reference test modules
under */tests/modules/, and the gitignore-aware workspace scan never
reaches core/ either, so every module test file reported
"Class 'Drupal\Tests\UnitTestCase' not found".

Index test directories by default: the classmap's first-wins merge
already handles duplicate fixture FQNs, and projects that want a
smaller index can trim it with the new [indexing] exclude setting
instead of a hardcoded name filter.

Refs PHPantom-dev#48
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 83.70370% with 44 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/indexing/init.rs 22.22% 14 Missing ⚠️
src/util.rs 0.00% 11 Missing ⚠️
src/classmap_scanner/filters.rs 93.96% 7 Missing ⚠️
src/indexing/scan.rs 50.00% 7 Missing ⚠️
src/definition/implementation.rs 0.00% 2 Missing ⚠️
src/analyse/run.rs 83.33% 1 Missing ⚠️
src/fix.rs 0.00% 1 Missing ⚠️
src/indexing/watch.rs 88.88% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants