|
| 1 | +# PHPantom — Remaining Work |
| 2 | + |
| 3 | +> Last updated: 2026-02-20 |
| 4 | +
|
| 5 | +--- |
| 6 | + |
| 7 | +## Go-to-Implementation Gaps |
| 8 | + |
| 9 | +### 5a. Transitive interface inheritance |
| 10 | +**Priority: Medium** |
| 11 | + |
| 12 | +If `InterfaceB extends InterfaceA` and `ClassC implements InterfaceB`, |
| 13 | +go-to-implementation on `InterfaceA` will not find `ClassC`. The |
| 14 | +transitive check in `class_implements_or_extends` walks `parent_class` |
| 15 | +chains but does not walk interface-extends chains. |
| 16 | + |
| 17 | +**Fix:** in `class_implements_or_extends`, when checking a class's |
| 18 | +`interfaces` list, load each interface via `class_loader` and recursively |
| 19 | +check whether it extends the target interface (with a depth bound). |
| 20 | + |
| 21 | +### 5b. Short-name collisions in `find_implementors` |
| 22 | +**Priority: Low** |
| 23 | + |
| 24 | +`class_implements_or_extends` matches interfaces by both short name and |
| 25 | +FQN (`iface_short == target_short || iface == target_fqn`). Two |
| 26 | +interfaces in different namespaces with the same short name (e.g. |
| 27 | +`App\Logger` and `Vendor\Logger`) could produce false positives. |
| 28 | +Similarly, `seen_names` in `find_implementors` deduplicates by short |
| 29 | +name, so two classes with the same short name in different namespaces |
| 30 | +could shadow each other. |
| 31 | + |
| 32 | +**Fix:** always compare fully-qualified names by resolving both sides |
| 33 | +before comparison. |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## Missing LSP Features |
| 38 | + |
| 39 | +### 6. Hover (`textDocument/hover`) |
| 40 | +**Priority: High** |
| 41 | + |
| 42 | +No hover support at all. Users can't see inferred types, docblock descriptions, |
| 43 | +or method signatures by hovering. Most of the infrastructure already exists |
| 44 | +(type resolution, class loading, docblocks) — wiring it into a hover handler |
| 45 | +would be relatively straightforward and high-impact. |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +### 7. Signature Help (`textDocument/signatureHelp`) |
| 50 | +**Priority: Medium** |
| 51 | + |
| 52 | +No parameter hints shown while typing function/method arguments. Named arg |
| 53 | +completion partially fills this role, but proper signature help is more |
| 54 | +ergonomic. |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +### 8. Document Symbols (`textDocument/documentSymbol`) |
| 59 | +**Priority: Medium** |
| 60 | + |
| 61 | +No outline view. Editors can't show a file's class/method/property structure. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +### 9. Find References (`textDocument/references`) |
| 66 | +**Priority: Medium** |
| 67 | + |
| 68 | +Can't find all usages of a symbol. |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +### 10. Rename (`textDocument/rename`) |
| 73 | +**Priority: Low** |
| 74 | + |
| 75 | +No rename refactoring support. |
| 76 | + |
| 77 | +--- |
| 78 | + |
| 79 | +### 11. Workspace Symbols (`workspace/symbol`) |
| 80 | +**Priority: Low** |
| 81 | + |
| 82 | +Can't search for classes/functions across the project. |
| 83 | + |
| 84 | +--- |
| 85 | + |
| 86 | +### 12. Diagnostics |
| 87 | +**Priority: Low** (large scope) |
| 88 | + |
| 89 | +No error reporting (undefined methods, type mismatches, etc.). |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +### 13. Code Actions |
| 94 | +**Priority: Low** |
| 95 | + |
| 96 | +No quick fixes or refactoring suggestions. No `codeActionProvider` in |
| 97 | +`ServerCapabilities`, no `textDocument/codeAction` handler, and no |
| 98 | +`WorkspaceEdit` generation infrastructure beyond trivial `TextEdit`s for |
| 99 | +use-statement insertion. |
| 100 | + |
| 101 | +#### 13a. Extract Function refactoring |
| 102 | + |
| 103 | +Select a range of statements inside a method/function and extract them into a |
| 104 | +new function. The LSP would need to: |
| 105 | + |
| 106 | +1. **Scope analysis** — determine which variables are read in the selection but |
| 107 | + defined before it (→ parameters) and which are written in the selection but |
| 108 | + read after it (→ return values). |
| 109 | +2. **Statement boundary validation** — reject selections that split an |
| 110 | + expression or cross control-flow boundaries in invalid ways. |
| 111 | +3. **Type annotation** — use variable type resolution to generate parameter and |
| 112 | + return type hints on the new function. |
| 113 | +4. **Code generation** — produce a `WorkspaceEdit` that replaces the selection |
| 114 | + with a call and inserts the new function definition nearby. |
| 115 | + |
| 116 | +**Prerequisites (build these first):** |
| 117 | + |
| 118 | +| Feature | What it contributes | |
| 119 | +|---|---| |
| 120 | +| Hover (§6) | "Resolve type at arbitrary position" — needed to type params | |
| 121 | +| Document Symbols (§8) | AST range → symbol mapping — needed to find enclosing function and valid insertion points | |
| 122 | +| Find References (§9) | Variable usage tracking across a scope — the same "which variables are used where" analysis | |
| 123 | +| Simple code actions (add use stmt, implement interface) | Builds the code action + `WorkspaceEdit` plumbing | |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## Performance / UX Ideas |
| 128 | + |
| 129 | +### 14. Partial result streaming via `$/progress` |
| 130 | +**Priority: Medium** (cross-cutting optimisation) |
| 131 | + |
| 132 | +The LSP spec (3.17) allows requests that return arrays — such as |
| 133 | +`textDocument/implementation`, `textDocument/references`, |
| 134 | +`workspace/symbol`, and even `textDocument/completion` — to stream |
| 135 | +incremental batches of results via `$/progress` notifications when both |
| 136 | +sides negotiate a `partialResultToken`. The final RPC response then |
| 137 | +carries `null` (all items were already sent through progress). |
| 138 | + |
| 139 | +This would let PHPantom deliver the *first* useful results almost |
| 140 | +instantly instead of blocking until every source has been scanned. |
| 141 | + |
| 142 | +#### Streaming between existing phases |
| 143 | + |
| 144 | +`find_implementors` already runs five sequential phases (see |
| 145 | +`docs/ARCHITECTURE.md` § Go-to-Implementation): |
| 146 | + |
| 147 | +1. **Phase 1 — ast_map** (already-parsed classes in memory) — essentially |
| 148 | + free. Flush results immediately. |
| 149 | +2. **Phase 2 — class_index** (FQN → URI entries not yet in ast_map) — |
| 150 | + loads individual files. Flush after each batch. |
| 151 | +3. **Phase 3 — classmap files** (Composer classmap, user + vendor mixed) |
| 152 | + — iterates unique file paths, applies string pre-filter, parses |
| 153 | + matches. This is the widest phase and the best candidate for |
| 154 | + within-phase streaming (see below). |
| 155 | +4. **Phase 4 — embedded stubs** (string pre-filter → lazy parse) — flush |
| 156 | + after stubs are checked. |
| 157 | +5. **Phase 5 — PSR-4 directory walk** (user code only, catches files not |
| 158 | + in the classmap) — disk I/O + parse per file, good candidate for |
| 159 | + per-file streaming. |
| 160 | + |
| 161 | +Each phase boundary is a natural point to flush a `$/progress` batch, |
| 162 | +so the editor starts populating the results list while heavier phases |
| 163 | +are still running. |
| 164 | + |
| 165 | +#### Prioritising user code within Phase 3 |
| 166 | + |
| 167 | +Phase 3 iterates the Composer classmap, which contains both user and |
| 168 | +vendor entries. Currently they are processed in arbitrary order. A |
| 169 | +simple optimisation: partition classmap file paths into user paths |
| 170 | +(under PSR-4 roots from `composer.json` `autoload` / `autoload-dev`) |
| 171 | +and vendor paths (everything else, typically under `vendor/`), then |
| 172 | +process user paths first. This way the results most relevant to the |
| 173 | +developer arrive before vendor matches, even within a single phase. |
| 174 | + |
| 175 | +#### Granularity options |
| 176 | + |
| 177 | +- **Per-phase batches** (simplest) — one `$/progress` notification at |
| 178 | + each of the five phase boundaries listed above. |
| 179 | +- **Per-file streaming** — within Phases 3 and 5, emit results as each |
| 180 | + file is parsed from disk instead of waiting for the entire phase to |
| 181 | + finish. Phase 3 can iterate hundreds of classmap files and Phase 5 |
| 182 | + recursively walks PSR-4 directories, so per-file flushing would |
| 183 | + significantly improve perceived latency for large projects. |
| 184 | +- **Adaptive batching** — collect results for a short window (e.g. 50 ms) |
| 185 | + then flush, balancing notification overhead against latency. |
| 186 | + |
| 187 | +#### Applicable requests |
| 188 | + |
| 189 | +| Request | Benefit | |
| 190 | +|---|---| |
| 191 | +| `textDocument/implementation` | Already scans five phases; each phase's matches can be streamed | |
| 192 | +| `textDocument/references` (§9) | Will need full-project scanning; streaming is essential | |
| 193 | +| `workspace/symbol` (§11) | Searches every known class/function; early batches feel instant | |
| 194 | +| `textDocument/completion` | Less critical (usually fast), but long chains through vendor code could benefit | |
| 195 | + |
| 196 | +#### Implementation sketch |
| 197 | + |
| 198 | +1. Check whether the client sent a `partialResultToken` in the request |
| 199 | + params. |
| 200 | +2. If yes, create a `$/progress` sender. After each scan phase (or |
| 201 | + per-file, depending on granularity), send a |
| 202 | + `ProgressParams { token, value: [items...] }` notification. |
| 203 | +3. Return `null` as the final response. |
| 204 | +4. If no token was provided, fall back to the current behaviour: collect |
| 205 | + everything, return once. |
0 commit comments