Skip to content

Commit 762be3a

Browse files
calebdwAJenbo
authored andcommitted
feat(code-action): convert arrow function to closure
Add a refactor.rewrite code action that converts arrow functions to anonymous closures: `fn($x) => $x * 2` becomes `function($x) { return $x * 2; }`. Variables from the outer scope used in the expression are automatically detected and captured via a `use()` clause. `$this` is excluded since closures bind it automatically (unless static). Nested arrow functions are handled by extending the parameter exclusion set. Preserves: - static keyword - Return type hints - Parameter type hints Includes 10 unit tests covering: simple expressions, type hints, static arrows, single/multiple variable capture, $this exclusion, parameter exclusion, method calls with captures, and deduplication. Closes #148 Signed-off-by: Anders Jenbo <anders@jenbo.dk>
1 parent 399b2b1 commit 762be3a

4 files changed

Lines changed: 1007 additions & 1 deletion

File tree

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- **Static methods complete on instance access.** Member completion after `->` now offers a class's static methods alongside its instance methods, since PHP lets you call a static method through an instance (`$obj->make()`). Static properties remain excluded, as they are only reachable via `::`. Contributed by @calebdw in https://github.com/AJenbo/phpantom_lsp/pull/174.
1313
- **Array-callable navigation.** Method-name strings in array callables — `[Controller::class, 'method']` and `[$object, 'method']` — now resolve like a real member reference. This makes go-to-definition, find-references, and rename work on Laravel controller actions such as `Route::get('/', [IndexPageController::class, 'indexPage'])`.
1414
- **Array-callable method completion.** Typing inside the method-name string of an array callable (`[Controller::class, '|']`) now offers method name completions from the resolved class, including inherited and trait methods. Works with `Class::class` constants, `$this`, and typed variables. (thanks @calebdw)
15+
- **Convert arrow function to closure.** A new `refactor.rewrite` code action converts arrow functions to anonymous closures (`fn($x) => $x * 2` to `function($x) { return $x * 2; }`). Variables from the outer scope are automatically captured via a `use()` clause. Preserves `static` and return type hints. Contributed by @calebdw in https://github.com/PHPantom-dev/phpantom_lsp/pull/191.
1516
- **Magic methods complete when implemented.** Magic methods declared on a class (`__invoke`, `__toString`, `__call`, and the rest) are now offered in member completion, so explicit calls like `$x->__invoke()` autocomplete and support go-to-definition. They are sorted below the regular methods so they never appear at the top of the list.
1617
- **Staleness detection and auto-refresh.** The class index, function index, and constant index now stay fresh automatically. When PHP files are created or deleted outside the editor (e.g. `git checkout`, code generation), the indices update without a restart, and edits made outside the editor are reflected the next time the file is used. When `composer.json` or `composer.lock` changes (e.g. after `composer install`), vendor packages are rescanned automatically.
1718
- **`#[ArrayShape]` attribute support.** Functions and methods annotated with `#[ArrayShape(["key" => "type", ...])]` (used by ~84 phpstorm-stubs entries) now produce array shape key completions, hover type info, and correct type resolution. Affects commonly used functions like `parse_url`, `stat`, `pathinfo`, `gc_status`, `getimagesize`, and `session_get_cookie_params`.

examples/demo.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3585,6 +3585,30 @@ public function demo(): void
35853585
}
35863586
}
35873587

3588+
class ConvertToClosureDemo
3589+
{
3590+
public function demo(): void
3591+
{
3592+
// Try: place cursor on `fn` and use code action "Convert to closure"
3593+
$double = fn(int $x): int => $x * 2;
3594+
3595+
// Arrow with captured outer variable — converted closure gets use()
3596+
$base = 10;
3597+
$add = fn(int $x) => $x + $base;
3598+
3599+
// Static arrow function
3600+
$staticFn = static fn(string $s): string => strtoupper($s);
3601+
3602+
// Arrow as callback — trigger inside the arrow function
3603+
$result = array_map(fn(string $s) => strtoupper($s), ['a', 'b']);
3604+
3605+
// Multiple captured variables
3606+
$prefix = 'hello';
3607+
$suffix = 'world';
3608+
$greet = fn(string $sep) => $prefix . $sep . $suffix;
3609+
}
3610+
}
3611+
35883612
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
35893613
// ┃ SCAFFOLDING — Supporting definitions below this line. ┃
35903614

0 commit comments

Comments
 (0)