Skip to content

Commit 8a49775

Browse files
committed
Update changelog and performance todo
1 parent 1594259 commit 8a49775

3 files changed

Lines changed: 84 additions & 4 deletions

File tree

docs/CHANGELOG.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- **Laravel custom Eloquent builder support.** Models using the `#[UseEloquentBuilder]` attribute now have their custom builder's methods forwarded as static methods on the model. `query()`, `newQuery()`, and `newModelQuery()` return the custom builder type with correct generic model substitution. Contributed by @MingJen in https://github.com/AJenbo/phpantom_lsp/pull/118.
13+
1014
### Fixed
1115

1216
- **`throw new` completion no longer offers non-instantiable types.** Interfaces, abstract classes, traits, and enums are now filtered out, matching the behavior of `new` completion. The `throw new` path also now filters to Throwable descendants only.
1317
- **Unified class name completion architecture.** `throw new` and `catch()` completion now use the same `build_class_name_completions` pipeline as `new`, `extends`, `implements`, etc. `throw new` uses a `ThrowNew` context (instantiable + Throwable) and `catch()`/`@throws` uses a `Catch` context (class or interface + Throwable). This gives both contexts the same affinity scoring, FQN shortening via use-map, namespace segment drill-down, deprecation flags, and consistent filtering. The separate `build_catch_class_name_completions` function has been removed.
1418
- **Consolidated class completion passes.** The previous 5-pass architecture (use-map, same-namespace, fqn_uri_index, fqn_uri_index duplicate, stub_index) has been simplified to 2 passes (fqn_uri_index + stub_index) with an inline `classify` closure that determines tier (`'0'` use-imported, `'1'` same/sub-namespace, `'2'` everything else) per candidate. The redundant pass 4 (identical to pass 3) is eliminated, and tier assignment is now based on proximity checks rather than which data source produced the item.
1519

20+
### Changed
21+
22+
- **Improved LSP responsiveness.** File parsing (`update_ast`) and diagnostics now run in background tasks, preventing interactive requests (completion, hover) from being blocked by full-file parses during typing. Contributed by @MingJen in https://github.com/AJenbo/phpantom_lsp/pull/118.
23+
- **Member completion caching.** Unfiltered member lists are cached per-target to speed up subsequent completions during keyword entry. Contributed by @MingJen in https://github.com/AJenbo/phpantom_lsp/pull/118.
24+
- **Laravel startup performance.** Common Laravel builder classes are warmed in the background at startup to eliminate the first-access penalty on Eloquent completions. Contributed by @MingJen in https://github.com/AJenbo/phpantom_lsp/pull/118.
25+
1626
## [0.8.0] - 2026-05-14
1727

1828
### Added
1929

20-
- **Laravel custom Eloquent builder support.** Models using the `#[UseEloquentBuilder]` attribute now have their custom builder's methods forwarded as static methods on the model. `query()`, `newQuery()`, and `newModelQuery()` return the custom builder type with correct generic model substitution.
2130
- **Blade template support.** Completion, hover, go-to-definition, diagnostics, semantic tokens, and inlay hints work inside `.blade.php` files. Contributed by @MingJen in https://github.com/AJenbo/phpantom_lsp/pull/100.
2231
- **Blade keyword highlighting.** Blade directives, echo delimiters, PHP keywords, cast types, comments, and PHPDoc tags inside `.blade.php` files now receive semantic tokens for proper syntax coloring.
2332
- **Blade view directive navigation.** Go-to-definition works on view names inside Blade directives (`@include`, `@extends`, `@includeIf`, `@includeWhen`, `@includeUnless`, `@includeFirst`, `@component`, `@each`), jumping to the referenced template file.
@@ -53,9 +62,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5362

5463
### Changed
5564

56-
- **Improved LSP responsiveness.** File parsing (`update_ast`) and diagnostics now run in background tasks, preventing interactive requests (completion, hover) from being blocked by full-file parses during typing.
57-
- **Member completion caching.** Unfiltered member lists are cached per-target to speed up subsequent completions during keyword entry.
58-
- **Laravel startup performance.** Common Laravel builder classes are warmed in the background at startup to eliminate the first-access penalty on Eloquent completions.
5965
- **Find References performance and freshness.** Project-wide Find References now avoids more unnecessary file work while still returning references through aliased class and function imports, and it refreshes newly added workspace PHP files on later searches. Contributed by @MingJen in https://github.com/AJenbo/phpantom_lsp/pull/116.
6066
- **Incremental text sync.** The server now uses incremental document sync, receiving only changed ranges from the editor instead of the full file content on every keystroke.
6167
- **LSP responsiveness.** Hover, go-to-definition, signature help, code actions, rename, and other handlers now run on background threads. Slow requests no longer block other requests or cancellations.

docs/todo.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ unlikely to move the needle for most users.
163163
| | **[Performance](todo/performance.md) / [Eager Resolution](todo/eager-resolution.md)** | | |
164164
| ER5 | [Mago-style separated metadata](todo/eager-resolution.md#er5--mago-style-separated-metadata) | High | High |
165165
| P14 | [Eager docblock parsing into structured fields](todo/performance.md#p14-eager-docblock-parsing-into-structured-fields) | Medium | Medium |
166+
| P9 | [`resolved_class_cache` generic-arg specialisation](todo/performance.md#p9-resolved_class_cache-generic-arg-specialisation) | Medium | Medium |
166167
| P11 | [Uncached base-resolution in `build_scope_methods_for_builder`](todo/performance.md#p11-uncached-base-resolution-in-build_scope_methods_for_builder) | Low-Medium | Low |
167168
| P3 | Parallel pre-filter in `find_implementors` | Low-Medium | Medium |
168169
| P4 | `memmem` for block comment terminator search | Low | Low |
@@ -171,6 +172,7 @@ unlikely to move the needle for most users.
171172
| P7 | `diag_pending_uris` uses `Vec::contains` for dedup | Low | Low |
172173
| P8 | `find_class_in_uri_classes_index` linear fallback scan | Low | Low |
173174
| P12 | [`find_or_load_function` Phase 1.75 serial bottleneck](todo/performance.md#p12-find_or_load_function-phase-175-serial-bottleneck) | Low | Low |
175+
| P17 | [`mago-names` resolution on the parse hot path](todo/performance.md#p17-mago-names-resolution-on-the-parse-hot-path) | Medium | Low |
174176
| P18 | [Subtype result caching](todo/performance.md#p18-subtype-result-caching) (per-request HashMap for hierarchy walks) | Medium | Low |
175177
| P19 | [Arena reuse on the parse hot path](todo/performance.md#p19-arena-reuse-on-the-parse-hot-path) (thread-local `Bump::reset()` instead of `Bump::new()`) | Medium | Low |
176178
| P20 | [Content-hash gated resolution cache persistence](todo/performance.md#p20-content-hash-gated-resolution-cache-persistence) | Medium | Medium |

docs/todo/performance.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,36 @@ window.
190190

191191
---
192192

193+
## P9. `resolved_class_cache` generic-arg specialisation
194+
195+
**Impact: Medium · Effort: Medium**
196+
197+
The resolved-class cache is keyed by `(FQN, Vec<String>)`. Every
198+
distinct generic instantiation of the same class (e.g.
199+
`Builder<User>`, `Builder<Order>`, `Builder<Product>`) triggers a
200+
full `resolve_class_fully` call, even though the base resolution
201+
(inheritance merging, trait merging, virtual member injection) is
202+
identical. Only the final generic substitution differs.
203+
204+
In a Laravel codebase with hundreds of Eloquent models, this means
205+
`Builder` is fully resolved hundreds of times, once per model.
206+
207+
### Fix
208+
209+
Cache the base-resolved class (before generic substitution)
210+
separately, keyed by FQN alone. When a generic instantiation is
211+
requested, look up the base-resolved class and apply
212+
`apply_substitution` on top. The substitution step is cheap
213+
(tree walk) compared to the full resolution (inheritance walking,
214+
trait merging, virtual member providers).
215+
216+
This requires splitting `resolve_class_fully` into two stages:
217+
base resolution (cached by FQN) and generic specialisation (cached
218+
by `(FQN, Vec<String>)` as today, but with a much cheaper miss
219+
path).
220+
221+
---
222+
193223
## P11. Uncached base-resolution in `build_scope_methods_for_builder`
194224

195225
**Impact: Low-Medium · Effort: Low**
@@ -476,6 +506,48 @@ essentially free for both eager and deferred indexing paths.
476506

477507
---
478508

509+
## P17. `mago-names` resolution on the parse hot path
510+
511+
**Impact: Medium · Effort: Low**
512+
513+
The `mago-names` name resolver runs synchronously inside
514+
`update_ast_inner`, adding a full AST walk plus an owned `HashMap`
515+
copy on every `didChange` event. Measured regression from `6a0737a`
516+
("Migrate to use mago-names"):
517+
518+
| Benchmark | Before | After | Δ |
519+
| ---------------- | ------ | ----- | ---- |
520+
| with_narrowing | 12 ms | 15 ms | +25% |
521+
| 5_methods_chain | 8 ms | 10 ms | +25% |
522+
| carbon_class | 250 ms | 340 ms | +36% |
523+
| large_file | 150 ms | 210 ms | +40% |
524+
525+
The resolved names are currently consumed only by diagnostics (which
526+
run asynchronously) and `FileContext::resolve_name_at()`. Nothing on
527+
the completion hot path requires this data to be computed eagerly.
528+
529+
### Fix
530+
531+
Defer name resolution out of `update_ast_inner`. Options:
532+
533+
- **Lazy resolution:** compute `OwnedResolvedNames` on first access
534+
per file version, invalidate on the next `update_ast`. Moves the
535+
cost off the typing hot path entirely.
536+
- **Diagnostic-worker resolution:** run the resolver in the
537+
diagnostic worker clone of `Backend`, since diagnostics are the
538+
primary consumer.
539+
540+
### When to implement
541+
542+
Low priority. The `mago-names` migration is complete, but the
543+
`use_map` is still used by several consumers. Further refactoring
544+
(migrating more consumers to byte-offset lookups, eventually
545+
removing `use_map`) will change the access patterns. Optimizing
546+
now would likely be reworked. Revisit once `use_map` usage is
547+
significantly reduced.
548+
549+
---
550+
479551
## P18. Subtype result caching
480552

481553
**Impact: Medium · Effort: Low**

0 commit comments

Comments
 (0)