Skip to content

Improved closure parameters #209

@UFOMelkor

Description

@UFOMelkor

I don't know whether this is out of scope for this plugin, but it is AFAIK currently the only one detecting the type of untyped closure parameters. I add a little script further below as an example.

To show what I am talking about at first, let me show a little screenshot.
image
The plugin automatically detects that the argument of the closure must be of type WordBuilder, provides autocompletion and allows Go To Declaration or Usages.

IMHO, four things are missing to complete this feature.

  1. Support Find usage. As you can see, only the usages of ok() are found but not for withLetter()
    image
  2. Support renaming. Currently, this refactoring is not provided on the autocompleted methods.
  3. Do not get distracted by outer variables. While autocompletion works in this case
    image
    it does not work in this one, because the name of the outer variable is the same as the argument one.
    image
  4. Detect closure arguments within closure arguments. While ->withWord() is detected, withLetter() is not.
    image

Without deeper knowledge, I would guess most of this would be possible with a TypeProvider.

<?php

declare(strict_types=1);

class WordBuilder
{
    private string $letters = "";

    public function withLetter(string $letter): self
    {
        $this->letters .= $letter;
        return $this;
    }

    public function ok(): string
    {
        return $this->letters;
    }
}

class SentenceBuilder
{
    /** @psalm-var list<string> */
    private array $words = [];

    /**
     * @psalm-param callable(WordBuilder): WordBuilder $mod
     */
    public function withWord(callable $mod): self
    {
        $this->words[] = $mod(new WordBuilder())->ok();
        return $this;
    }

    public function ok(): string
    {
        return implode(' ', $this->words) . '.';
    }
}

class Test
{

    public function test_with_and_without_nested_builders(): void
    {
        echo $this->givenAWord(fn($word) => $word->withLetter("T")->withLetter("e")->withLetter("s")->withLetter("t")) . "\n";
        echo $this->givenASentence(
            fn($aSentence) => $aSentence
                ->withWord(fn($word) => $word->withLetter("T")->withLetter("h")->withLetter("i")->withLetter("s"))
                ->withWord(fn($word) => $word->withLetter("i")->withLetter("s"))
                ->withWord(fn($word) => $word->withLetter("a"))
                ->withWord(fn($word) => $word->withLetter("T")->withLetter("e")->withLetter("s")->withLetter("t"))) . "\n";
    }

    public function test_with_equal_named_variable(): void
    {
        $word = $this->givenAWord(fn($word) => $word->withLetter("T")->withLetter("e")->withLetter("s")->withLetter("t")) . "\n";
        echo $word;
    }

    /**
     * @psalm-param callable(SentenceBuilder): SentenceBuilder $mod
     */
    private function givenASentence(callable $mod): string
    {
        return $mod(new SentenceBuilder())->ok();
    }

    /**
     * @psalm-param callable(WordBuilder): WordBuilder $mod
     */
    private function givenAWord(callable $mod): string
    {
        return $mod(new WordBuilder())->ok();
    }
}

(new Test())->test_with_and_without_nested_builders();
(new Test())->test_with_equal_named_variable();

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: help wantedBacklogtype: improvementThe issue is about some basic functionality delivered earlier to make it closer to perfect

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions