Skip to content

Commit 09a220d

Browse files
committed
Implement Signature help
1 parent 36383e4 commit 09a220d

9 files changed

Lines changed: 1640 additions & 81 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
A fast, lightweight PHP language server written in Rust. Uses only a few MB of RAM regardless of project size and is fully responsive in milliseconds. No indexing phase, no background workers, no waiting.
44

55
> [!NOTE]
6-
> PHPantom is in active development. Completion and go-to-definition are solid and used daily. More LSP features (hover, signature help, find references) are on the roadmap.
6+
> PHPantom is in active development. Completion, go-to-definition, and signature help are solid and used daily. More LSP features (hover, find references) are on the roadmap.
77
88
## Features
99

@@ -22,8 +22,8 @@ PHPantom focuses on completion and go-to-definition and aims to do them really w
2222
| Array shape inference ||||| 🚧 |
2323
| Object shape completion ||||| 🚧 |
2424
| Generator body types ||| 🚧 |||
25-
| Hover | |||||
26-
| Signature help | |||||
25+
| Hover | 🚧 |||||
26+
| Signature help | |||||
2727
| Find references ||||||
2828
| Diagnostics ||||||
2929
| Rename / refactoring || 💰 ||||

docs/ARCHITECTURE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ src/
6767
│ ├── catch_completion.rs # Smart exception type completion inside catch() clauses
6868
│ ├── type_hint_completion.rs # Type completion in parameter lists, return types, properties
6969
│ └── use_edit.rs # Use-statement insertion helpers
70+
├── signature_help.rs # Signature help: parameter hints inside function/method call parens
7071
├── hover/
7172
│ └── mod.rs # Hover handler: symbol-map dispatch, type/signature/docblock formatting
7273
├── definition/
@@ -82,6 +83,7 @@ tests/
8283
├── completion_*.rs # Completion integration tests (by feature area)
8384
├── definition_*.rs # Go-to-definition integration tests
8485
├── hover.rs # Hover integration tests
86+
├── signature_help.rs # Signature help integration tests
8587
├── implementation.rs # Go-to-implementation integration tests
8688
├── docblock_*.rs # Docblock parsing and type tests
8789
├── parser.rs # PHP parser / AST extraction tests

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- **Signature help.** Parameter hints now appear automatically when typing inside function and method call parentheses. The active parameter is highlighted and updates as you type commas. Supported call forms: standalone functions, instance methods (`$var->method(`), static methods (`Class::method(`), constructors (`new Class(`), `self::`, `static::`, and `parent::` calls. Works with inherited methods, trait methods, built-in (stub) functions, and cross-file PSR-4 resolution. Variadic parameters stay highlighted for extra arguments. Incomplete code is handled via automatic content patching (same strategy as named-argument completion).
1213
- **Hover.** Hovering over any symbol now shows its type, signature, and docblock description in a Markdown popup. Supported symbols include variables (with inferred types), methods (with full parameter list and return type), properties, class constants, class/interface/trait/enum references (with FQN, parent, interfaces, and docblock summary), function calls, and `$this`/`self`/`static`/`parent` keywords. Works across files via the same resolution pipeline that powers completion and go-to-definition. Deprecated members are marked with `@deprecated`.
1314
- **Closure parameter inference.** When a closure or arrow function is passed to a method or function whose parameter is typed as `callable(T): R` or `Closure(T): R`, untyped closure parameters are now inferred from the callable signature. For example, `$users->map(fn($u) => $u->name)` infers `$u` as `User` when `map` declares `@param callable(TValue): TMapValue` and generic substitution has resolved `TValue` to `User`. Works with instance methods, static methods, null-safe calls, standalone functions, `$this->method()` receivers, and closures at any argument position. Explicit type hints on closure parameters always take precedence. Template parameters inside callable signatures (e.g. `callable(TValue): void`) are substituted during generic resolution so the inferred types are concrete.
1415
- **Factory support.** `User::factory()->create()` and `->make()` now resolve to the model class. When a model uses the `HasFactory` trait with an explicit `@use HasFactory<UserFactory>` annotation, the generics system handles resolution. When the annotation is absent, the naming convention is used as a fallback: `App\Models\User` maps to `Database\Factories\UserFactory`, and subdirectories are preserved (`App\Models\Admin\SuperUser` maps to `Database\Factories\Admin\SuperUserFactory`). Factory chain methods that return `static` (e.g. `count()`, `state()`) continue the chain on the factory, while `create()` and `make()` return the model. The convention also works in reverse: a factory class extending `Factory` without `@extends Factory<Model>` resolves `TModel` from its own class name. Both directions are implemented as fallbacks that defer to explicit generics when present.

docs/todo.md

Lines changed: 59 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,13 @@ Each item carries two ratings:
1414

1515
---
1616

17-
<!-- ============================================================ -->
18-
<!-- TIER 0 — CRITICAL IMPACT -->
19-
<!-- ============================================================ -->
20-
21-
## Critical Impact
22-
23-
### 1. Signature Help (`textDocument/signatureHelp`)
24-
**Impact: Critical · Effort: Medium**
25-
26-
No parameter hints shown while typing function/method arguments. Named arg
27-
completion partially fills this role, but proper signature help is more
28-
ergonomic. This is a baseline expectation for any modern language server —
29-
users rely on it constantly when calling unfamiliar APIs.
30-
31-
---
32-
3317
<!-- ============================================================ -->
3418
<!-- TIER 1 — HIGH IMPACT -->
3519
<!-- ============================================================ -->
3620

3721
## High Impact
3822

39-
### 2. `in_array` strict-mode type narrowing
23+
### 1. `in_array` strict-mode type narrowing
4024
**Impact: High · Effort: Low**
4125

4226
When `in_array($needle, $haystack, true)` is used as a condition,
@@ -58,7 +42,7 @@ See `InArrayFunctionTypeSpecifyingExtension` in PHPStan.
5842

5943
---
6044

61-
### 3. Pipe operator (PHP 8.5)
45+
### 2. Pipe operator (PHP 8.5)
6246
**Impact: High · Effort: Low**
6347

6448
PHP 8.5 introduced the pipe operator (`|>`):
@@ -84,7 +68,7 @@ callable syntax (`htmlspecialchars(...)`), reuse the existing
8468

8569
---
8670

87-
### 4. Function-level `@template` generic resolution
71+
### 3. Function-level `@template` generic resolution
8872
**Impact: High · Effort: Medium**
8973

9074
`MethodInfo` has `template_params` and `template_bindings` fields that
@@ -185,7 +169,7 @@ manifestation of this gap.
185169

186170
---
187171

188-
### 5. Parse and resolve `($param is T ? A : B)` return types
172+
### 4. Parse and resolve `($param is T ? A : B)` return types
189173
**Impact: High · Effort: Medium**
190174

191175
PHPStan's stubs use conditional return type syntax in docblocks:
@@ -224,7 +208,7 @@ extend the existing `ConditionalReturnType` infrastructure.
224208

225209
---
226210

227-
### 6. Warn when composer.json is missing or classmap is not optimized
211+
### 5. Warn when composer.json is missing or classmap is not optimized
228212
**Impact: High · Effort: Medium**
229213

230214
PHPantom relies on Composer artifacts (`vendor/composer/autoload_classmap.php`,
@@ -299,7 +283,7 @@ For the non-optimized classmap case, offer action buttons:
299283

300284
---
301285

302-
### 7. Find References (`textDocument/references`)
286+
### 6. Find References (`textDocument/references`)
303287
**Impact: High · Effort: Medium-High**
304288

305289
Can't find all usages of a symbol. The precomputed `SymbolMap` (built
@@ -323,7 +307,7 @@ variable within its scope" without re-parsing.
323307

324308
## Medium-High Impact
325309

326-
### 8. File system watching for vendor and project changes
310+
### 7. File system watching for vendor and project changes
327311
**Impact: Medium-High · Effort: Medium**
328312

329313
PHPantom loads Composer artifacts (classmap, PSR-4 mappings, autoload
@@ -381,7 +365,7 @@ supports glob patterns like `**/vendor/composer/autoload_*.php`.
381365

382366
## Medium Impact
383367

384-
### 9. No reverse jump: implementation → interface method declaration
368+
### 8. No reverse jump: implementation → interface method declaration
385369
**Impact: Medium · Effort: Low**
386370

387371
Go-to-implementation lets you jump from an interface method to its concrete
@@ -398,7 +382,7 @@ target.
398382

399383
---
400384

401-
### 10. `BackedEnum::from()` / `::tryFrom()` return type refinement
385+
### 9. `BackedEnum::from()` / `::tryFrom()` return type refinement
402386
**Impact: Medium · Effort: Low**
403387

404388
When calling `MyEnum::from($value)` or `MyEnum::tryFrom($value)`,
@@ -415,14 +399,14 @@ See `BackedEnumFromMethodDynamicReturnTypeExtension` in PHPStan.
415399

416400
---
417401

418-
### 11. Document Symbols (`textDocument/documentSymbol`)
402+
### 10. Document Symbols (`textDocument/documentSymbol`)
419403
**Impact: Medium · Effort: Low**
420404

421405
No outline view. Editors can't show a file's class/method/property structure.
422406

423407
---
424408

425-
### 12. Workspace Symbols (`workspace/symbol`)
409+
### 11. Workspace Symbols (`workspace/symbol`)
426410
**Impact: Medium · Effort: Low-Medium**
427411

428412
Can't search for classes/functions across the project. The `ast_map`
@@ -434,7 +418,7 @@ LSP `Location`s.
434418

435419
---
436420

437-
### 13. No go-to-definition for built-in (stub) functions and constants
421+
### 12. No go-to-definition for built-in (stub) functions and constants
438422
**Impact: Medium · Effort: Medium**
439423

440424
Clicking on a built-in function name like `array_map`, `strlen`, or
@@ -456,7 +440,7 @@ limitation.
456440

457441
---
458442

459-
### 14. Property hooks (PHP 8.4)
443+
### 13. Property hooks (PHP 8.4)
460444
**Impact: Medium · Effort: Medium**
461445

462446
PHP 8.4 introduced property hooks (`get` / `set`):
@@ -491,7 +475,7 @@ scopes, and parse the set-visibility modifier into a new
491475

492476
---
493477

494-
### 15. Narrow types of `&$var` parameters after function calls
478+
### 14. Narrow types of `&$var` parameters after function calls
495479
**Impact: Medium · Effort: Medium**
496480

497481
When a function takes a parameter by reference, the variable's type
@@ -520,7 +504,7 @@ extension) or use a built-in map for known functions.
520504

521505
---
522506

523-
### 16. SPL iterator generic stubs
507+
### 15. SPL iterator generic stubs
524508
**Impact: Medium · Effort: Medium**
525509

526510
PHPStan's `iterable.stub` provides full `@template TKey` /
@@ -541,7 +525,7 @@ certainly missing these generic annotations. We should either:
541525

542526
---
543527

544-
### 17. Partial result streaming via `$/progress`
528+
### 16. Partial result streaming via `$/progress`
545529
**Impact: Medium · Effort: Medium-High**
546530

547531
The LSP spec (3.17) allows requests that return arrays — such as
@@ -621,7 +605,7 @@ developer arrive before vendor matches, even within a single phase.
621605

622606
---
623607

624-
### 18. Rename (`textDocument/rename`)
608+
### 17. Rename (`textDocument/rename`)
625609
**Impact: Medium · Effort: Medium-High**
626610

627611
No rename refactoring support. Rename builds on find-references (§8) —
@@ -636,7 +620,7 @@ position without text scanning.
636620

637621
---
638622

639-
### 19. Array functions needing new code paths
623+
### 18. Array functions needing new code paths
640624
**Impact: Medium · Effort: High**
641625

642626
These functions have return type semantics that don't fit into either
@@ -675,7 +659,7 @@ These functions have return type semantics that don't fit into either
675659

676660
## Low-Medium Impact
677661

678-
### 20. Asymmetric visibility (PHP 8.4)
662+
### 19. Asymmetric visibility (PHP 8.4)
679663
**Impact: Low-Medium · Effort: Low**
680664

681665
Separate from property hooks, PHP 8.4 allows asymmetric visibility on
@@ -705,7 +689,7 @@ is just to store the value; context-aware filtering can follow later.
705689

706690
---
707691

708-
### 21. `str_contains` / `str_starts_with` / `str_ends_with` → non-empty-string narrowing
692+
### 20. `str_contains` / `str_starts_with` / `str_ends_with` → non-empty-string narrowing
709693
**Impact: Low-Medium · Effort: Low**
710694

711695
When `str_contains($haystack, $needle)` appears in a condition and
@@ -722,7 +706,7 @@ See `StrContainingTypeSpecifyingExtension` in PHPStan.
722706

723707
---
724708

725-
### 22. `count` / `sizeof` comparison → non-empty-array narrowing
709+
### 21. `count` / `sizeof` comparison → non-empty-array narrowing
726710
**Impact: Low-Medium · Effort: Low**
727711

728712
`if (count($arr) > 0)` or `if (count($arr) >= 1)` narrows `$arr` to
@@ -740,7 +724,7 @@ branches in `TypeSpecifier::specifyTypesInCondition`.
740724

741725
## Low Impact
742726

743-
### 23. Short-name collisions in `find_implementors`
727+
### 22. Short-name collisions in `find_implementors`
744728
**Impact: Low · Effort: Low**
745729

746730
`class_implements_or_extends` matches interfaces by both short name and
@@ -756,7 +740,7 @@ before comparison.
756740

757741
---
758742

759-
### 24. Fiber type resolution
743+
### 23. Fiber type resolution
760744
**Impact: Low · Effort: Low**
761745

762746
`Generator<TKey, TValue, TSend, TReturn>` has dedicated support for
@@ -771,7 +755,7 @@ Generator extraction in `docblock/types.rs`.
771755

772756
---
773757

774-
### 25. Non-empty-string propagation through string functions
758+
### 24. Non-empty-string propagation through string functions
775759
**Impact: Low · Effort: Low**
776760

777761
PHPStan tracks `non-empty-string` through string-manipulating
@@ -789,7 +773,7 @@ See `NonEmptyStringFunctionsReturnTypeExtension` in PHPStan.
789773

790774
---
791775

792-
### 26. `Closure::bind()` / `Closure::fromCallable()` return type preservation
776+
### 25. `Closure::bind()` / `Closure::fromCallable()` return type preservation
793777
**Impact: Low · Effort: Low-Medium**
794778

795779
PHPStan preserves the closure's type through `Closure::bind()` and
@@ -802,7 +786,7 @@ See `ClosureBindDynamicReturnTypeExtension` and
802786

803787
---
804788

805-
### 27. Remove deprecated text-search fallbacks
789+
### 26. Remove deprecated text-search fallbacks
806790
**Impact: Low · Effort: Medium**
807791

808792
The go-to-definition subsystem now uses the precomputed `SymbolMap` as
@@ -834,7 +818,7 @@ would let that deprecated function be removed entirely.
834818

835819
---
836820

837-
### 28. Non-array functions with dynamic return types
821+
### 27. Non-array functions with dynamic return types
838822
**Impact: Low · Effort: High**
839823

840824
PHPStan also provides dynamic return type extensions for many non-array
@@ -865,22 +849,22 @@ return types (less impactful for class-based completion).
865849

866850
---
867851

868-
### 29. Diagnostics
852+
### 28. Diagnostics
869853
**Impact: Low (large scope) · Effort: Very High**
870854

871855
No error reporting (undefined methods, type mismatches, etc.).
872856

873857
---
874858

875-
### 30. Code Actions
859+
### 29. Code Actions
876860
**Impact: Low · Effort: Very High**
877861

878862
No quick fixes or refactoring suggestions. No `codeActionProvider` in
879863
`ServerCapabilities`, no `textDocument/codeAction` handler, and no
880864
`WorkspaceEdit` generation infrastructure beyond trivial `TextEdit`s for
881865
use-statement insertion.
882866

883-
#### 30a. Extract Function refactoring
867+
#### 29a. Extract Function refactoring
884868

885869
Select a range of statements inside a method/function and extract them into a
886870
new function. The LSP would need to:
@@ -914,33 +898,32 @@ new function. The LSP would need to:
914898

915899
| # | Item | Impact | Effort |
916900
|---|---|---|---|
917-
| 1 | Signature Help | **Critical** | Medium |
918-
| 2 | `in_array` strict-mode type narrowing | High | Low |
919-
| 3 | Pipe operator (PHP 8.5) | High | Low |
920-
| 4 | Function-level `@template` generic resolution | High | Medium |
921-
| 5 | Conditional return type syntax | High | Medium |
922-
| 6 | Composer environment warnings | High | Medium |
923-
| 7 | Find References | High | Medium-High |
924-
| 8 | File system watching | Medium-High | Medium |
925-
| 9 | Reverse jump: impl → interface | Medium | Low |
926-
| 10 | `BackedEnum::from()` refinement | Medium | Low |
927-
| 11 | Document Symbols | Medium | Low |
928-
| 12 | Workspace Symbols | Medium | Low-Medium |
929-
| 13 | Built-in stub go-to-definition | Medium | Medium |
930-
| 14 | Property hooks (PHP 8.4) | Medium | Medium |
931-
| 15 | Parameter out types (by-reference) | Medium | Medium |
932-
| 16 | SPL iterator generic stubs | Medium | Medium |
933-
| 17 | Partial result streaming | Medium | Medium-High |
934-
| 18 | Rename | Medium | Medium-High |
935-
| 19 | Array functions (new code paths) | Medium | High |
936-
| 20 | Asymmetric visibility (PHP 8.4) | Low-Medium | Low |
937-
| 21 | `str_contains` / `str_starts_with` narrowing | Low-Medium | Low |
938-
| 22 | `count` / `sizeof` → non-empty-array | Low-Medium | Low |
939-
| 23 | Short-name collisions | Low | Low |
940-
| 24 | Fiber type resolution | Low | Low |
941-
| 25 | Non-empty-string propagation | Low | Low |
942-
| 26 | `Closure::bind()` preservation | Low | Low-Medium |
943-
| 27 | Remove deprecated text-search fallbacks | Low | Medium |
944-
| 28 | Non-array dynamic return types | Low | High |
945-
| 29 | Diagnostics | Low | Very High |
946-
| 30 | Code Actions / Extract Function | Low | Very High |
901+
| 1 | `in_array` strict-mode type narrowing | High | Low |
902+
| 2 | Pipe operator (PHP 8.5) | High | Low |
903+
| 3 | Function-level `@template` generic resolution | High | Medium |
904+
| 4 | Conditional return type syntax | High | Medium |
905+
| 5 | Composer environment warnings | High | Medium |
906+
| 6 | Find References | High | Medium-High |
907+
| 7 | File system watching | Medium-High | Medium |
908+
| 8 | Reverse jump: impl → interface | Medium | Low |
909+
| 9 | `BackedEnum::from()` refinement | Medium | Low |
910+
| 10 | Document Symbols | Medium | Low |
911+
| 11 | Workspace Symbols | Medium | Low-Medium |
912+
| 12 | Built-in stub go-to-definition | Medium | Medium |
913+
| 13 | Property hooks (PHP 8.4) | Medium | Medium |
914+
| 14 | Parameter out types (by-reference) | Medium | Medium |
915+
| 15 | SPL iterator generic stubs | Medium | Medium |
916+
| 16 | Partial result streaming | Medium | Medium-High |
917+
| 17 | Rename | Medium | Medium-High |
918+
| 18 | Array functions (new code paths) | Medium | High |
919+
| 19 | Asymmetric visibility (PHP 8.4) | Low-Medium | Low |
920+
| 20 | `str_contains` / `str_starts_with` narrowing | Low-Medium | Low |
921+
| 21 | `count` / `sizeof` → non-empty-array | Low-Medium | Low |
922+
| 22 | Short-name collisions | Low | Low |
923+
| 23 | Fiber type resolution | Low | Low |
924+
| 24 | Non-empty-string propagation | Low | Low |
925+
| 25 | `Closure::bind()` preservation | Low | Low-Medium |
926+
| 26 | Remove deprecated text-search fallbacks | Low | Medium |
927+
| 27 | Non-array dynamic return types | Low | High |
928+
| 28 | Diagnostics | Low | Very High |
929+
| 29 | Code Actions / Extract Function | Low | Very High |

0 commit comments

Comments
 (0)