Browse Source

Make @method alignable

Nat Zimmermann 7 years ago
parent
commit
2d8ab59b53
2 changed files with 177 additions and 9 deletions
  1. 26 8
      src/Fixer/Phpdoc/PhpdocAlignFixer.php
  2. 151 1
      tests/Fixer/Phpdoc/PhpdocAlignFixerTest.php

+ 26 - 8
src/Fixer/Phpdoc/PhpdocAlignFixer.php

@@ -43,6 +43,7 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurationDefin
         'throws',
         'type',
         'var',
+        'method',
     ];
 
     private static $tagsWithName = [
@@ -50,6 +51,10 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurationDefin
         'property',
     ];
 
+    private static $tagsWithMethodSignature = [
+        'method',
+    ];
+
     /**
      * {@inheritdoc}
      */
@@ -58,17 +63,20 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurationDefin
         parent::configure($configuration);
 
         $tagsWithNameToAlign = array_intersect($this->configuration['tags'], self::$tagsWithName);
-        $tagsWithoutNameToAlign = array_diff($this->configuration['tags'], $tagsWithNameToAlign);
+        $tagsWithMethodSignatureToAlign = array_intersect($this->configuration['tags'], self::$tagsWithMethodSignature);
+        $tagsWithoutNameToAlign = array_diff($this->configuration['tags'], $tagsWithNameToAlign, $tagsWithMethodSignatureToAlign);
 
         $indent = '(?P<indent>(?: {2}|\t)*)';
         // e.g. @param <hint> <$var>
         $tagsWithName = '(?P<tag>'.implode('|', $tagsWithNameToAlign).')\s+(?P<hint>[^$]+?)\s+(?P<var>(?:&|\.{3})?\$[^\s]+)';
         // e.g. @return <hint>
         $tagsWithoutName = '(?P<tag2>'.implode('|', $tagsWithoutNameToAlign).')\s+(?P<hint2>[^\s]+?)';
+        // e.g. @method <hint> <signature>
+        $tagsWithMethodSignature = '(?P<tag3>'.implode('|', $tagsWithMethodSignatureToAlign).')(\s+(?P<hint3>[^\s(]+)|)\s+(?P<signature>.+\))';
         // optional <desc>
         $desc = '(?:\s+(?P<desc>\V*))';
 
-        $this->regex = '/^'.$indent.' \* @(?:'.$tagsWithName.'|'.$tagsWithoutName.')'.$desc.'\s*$/u';
+        $this->regex = '/^'.$indent.' \* @(?:'.$tagsWithName.'|'.$tagsWithoutName.'|'.$tagsWithMethodSignature.')'.$desc.'\s*$/u';
         $this->regexCommentLine = '/^'.$indent.' \*(?! @)(?:\s+(?P<desc>\V+))(?<!\*\/)$/u';
     }
 
@@ -139,7 +147,8 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurationDefin
             ->setAllowedValues([
                 $generator->allowedValueIsSubsetOf(self::$alignableTags),
             ])
-            // By default, all tags apart from @property will be aligned for backwards compatibility
+            // By default, all tags apart from @property and @method will be aligned for backwards compatibility
+            // @TODO 3.0 Align all available tags by default
             ->setDefault([
                 'param',
                 'return',
@@ -215,13 +224,16 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurationDefin
                         continue;
                     }
 
+                    $extraIndent = 2;
+
+                    if (in_array($currTag, self::$tagsWithName, true) || in_array($currTag, self::$tagsWithMethodSignature, true)) {
+                        $extraIndent = 3;
+                    }
+
                     $line =
                         $item['indent']
                         .' *  '
-                        .str_repeat(
-                            ' ',
-                            $tagMax + $hintMax + $varMax + (in_array($currTag, self::$tagsWithName, true) ? 3 : 2)
-                        )
+                        .str_repeat(' ', $tagMax + $hintMax + $varMax + $extraIndent)
                         .$item['desc']
                         .$lineEnding;
 
@@ -242,7 +254,7 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurationDefin
 
                 if (!empty($item['var'])) {
                     $line .=
-                        str_repeat(' ', $hintMax - strlen($item['hint']) + 1)
+                        str_repeat(' ', ($hintMax ?: -1) - strlen($item['hint']) + 1)
                         .$item['var']
                         .(
                             !empty($item['desc'])
@@ -278,6 +290,12 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurationDefin
                 $matches['var'] = '';
             }
 
+            if (!empty($matches['tag3'])) {
+                $matches['tag'] = $matches['tag3'];
+                $matches['hint'] = $matches['hint3'];
+                $matches['var'] = $matches['signature'];
+            }
+
             return $matches;
         }
 

+ 151 - 1
tests/Fixer/Phpdoc/PhpdocAlignFixerTest.php

@@ -55,7 +55,7 @@ EOF;
 
     public function testFixMultiLineDesc()
     {
-        $this->fixer->configure(['tags' => ['param', 'property']]);
+        $this->fixer->configure(['tags' => ['param', 'property', 'method']]);
 
         $expected = <<<'EOF'
 <?php
@@ -70,6 +70,8 @@ EOF;
      * @param    mixed           &$reference A parameter passed by reference
      * @property mixed           $foo        A foo
      *                                       See constants
+     * @method   static          baz($bop)   A method that does a thing
+     *                                       It does it well
      */
 
 EOF;
@@ -87,6 +89,8 @@ EOF;
      * @param  mixed    &$reference     A parameter passed by reference
      * @property   mixed   $foo     A foo
      *                               See constants
+     * @method static   baz($bop)   A method that does a thing
+     *                          It does it well
      */
 
 EOF;
@@ -628,6 +632,151 @@ EOF;
         $this->doTest($expected, $input);
     }
 
+    public function testDoesNotAlignMethodByDefault()
+    {
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @param  int       $foobar Description
+     * @return int
+     * @throws Exception
+     * @var    FooBar
+     * @type   BarFoo
+     * @method     string    foo(string $bar)   Hello World
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @param    int   $foobar   Description
+     * @return  int
+     * @throws Exception
+     * @var       FooBar
+     * @type      BarFoo
+     * @method     string    foo(string $bar)   Hello World
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
+    public function testAlignsMethod()
+    {
+        $this->fixer->configure(['tags' => ['param', 'method', 'return', 'throws', 'type', 'var']]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @param  int       $foobar                                        Description
+     * @return int
+     * @throws Exception
+     * @var    FooBar
+     * @type   BarFoo
+     * @method int       foo(string $bar, string ...$things, int &$baz) Description
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @param    int   $foobar     Description
+     * @return  int
+     * @throws Exception
+     * @var       FooBar
+     * @type      BarFoo
+     * @method        int    foo(string $bar, string ...$things, int &$baz)   Description
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
+    public function testAlignsMethodWithoutParameters()
+    {
+        $this->fixer->configure(['tags' => ['method', 'property']]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @property string $foo  Desc
+     * @method   int    foo() Description
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @property    string   $foo     Desc
+     * @method int      foo()          Description
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
+    public function testDoesNotFormatMethod()
+    {
+        $this->fixer->configure(['tags' => ['method']]);
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @method int foo( string  $bar ) Description
+     */
+EOF;
+
+        $this->doTest($input);
+    }
+
+    public function testAlignsMethodWithoutReturnType()
+    {
+        $this->fixer->configure(['tags' => ['method', 'property']]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @property string $foo  Desc
+     * @method   int    foo() Description
+     * @method          bar() Descrip
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @property    string   $foo     Desc
+     * @method int      foo()          Description
+     * @method    bar()   Descrip
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
+    public function testAlignsMethodsWithoutReturnType()
+    {
+        $this->fixer->configure(['tags' => ['method']]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @method fooBaz()         Description
+     * @method bar(string $foo) Descrip
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @method         fooBaz()  Description
+     * @method    bar(string $foo)   Descrip
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
     public function testDoesNotAlignWithEmptyConfig()
     {
         $this->fixer->configure(['tags' => []]);
@@ -641,6 +790,7 @@ EOF;
      * @var       FooBar
      * @type      BarFoo
      * @property     string    $foo   Hello World
+     * @method    int    bar() Description
      */
 EOF;