Repository: phpactor/phpactor (php)

Issue:
Single line doc blocks not supported?
## Summary
Phpactor seems to not support single line comments that are supported by Phpdoc
[Tests are all targeted at multi line](https://github.com/phpactor/phpactor/tree/master/lib/DocblockParser/Tests/Unit/Printer/examples). Any plans to support this?

I would love to give it a go, if I can manage to get a development environment up and running for Phpactor


## Single line comment
Single line comment (no comment shown)
![image](https://github.com/phpactor/phpactor/assets/1260209/8969a5d7-f2b0-4cd5-8036-2d82398eec77)

Server log:
```
[DEBUG][2023-08-21 09:42:23] .../lua/vim/lsp.lua:1392	"LSP[phpactor]"	"client.request"	1	"textDocument/hover"	{  position = {    character = 20,    line = 11  },  textDocument = {    uri = "file:///home/dmitri/sites/meg-worktree/feature/tests/Unit/ATest.php"  }}	<function 1>	1
[DEBUG][2023-08-21 09:42:23] .../vim/lsp/rpc.lua:284	"rpc.send"	{  id = 2,  jsonrpc = "2.0",  method = "textDocument/hover",  params = {    position = {      character = 20,      line = 11    },    textDocument = {      uri = "file:///home/dmitri/sites/meg-worktree/feature/tests/Unit/ATest.php"    }  }}
[DEBUG][2023-08-21 09:42:24] .../vim/lsp/rpc.lua:387	"rpc.receive"	{  id = 2,  jsonrpc = "2.0",  result = {    contents = {      kind = "markdown",      value = "### ATest # singleLineComment\n\n```php\n<?php public function singleLineComment(): void\n```"    },    range = {      ["end"] = {        character = 37,        line = 11      },      start = {        character = 20,        line = 11      }    }  }}
```


## Multi line comment

Multi line comment (comment is shown)
![image](https://github.com/phpactor/phpactor/assets/1260209/76eaf622-05da-41d0-9558-fb25391a5739)


Server log:

```
[DEBUG][2023-08-21 09:43:59] .../lua/vim/lsp.lua:1392	"LSP[phpactor]"	"client.request"	1	"textDocument/hover"	{  position = {    character = 20,    line = 19  },  textDocument = {    uri = "file:///home/dmitri/sites/meg-worktree/feature/tests/Unit/ATest.php"  }}	<function 1>	1
[DEBUG][2023-08-21 09:43:59] .../vim/lsp/rpc.lua:284	"rpc.send"	{  id = 3,  jsonrpc = "2.0",  method = "textDocument/hover",  params = {    position = {      character = 20,      line = 19    },    textDocument = {      uri = "file:///home/dmitri/sites/meg-worktree/feature/tests/Unit/ATest.php"    }  }}
[DEBUG][2023-08-21 09:43:59] .../vim/lsp/rpc.lua:387	"rpc.receive"	{  id = 3,  jsonrpc = "2.0",  result = {    contents = {      kind = "markdown",      value = "### ATest # multipleLineComment\n\nThis is a\nmulti-line comment\n\n```php\n<?php public function multipleLineComment(): void\n```"    },    range = {      ["end"] = {        character = 39,        line = 19      },      start = {        character = 20,        line = 19      }    }  }}
```


## PHPDoc support

I double checked if this feature is supported by phpdoc

From [PhpDocumentor](https://docs.phpdoc.org/3.0/guide/guides/docblocks.html)

> A DocComment starts with a forward slash and two asterisks (/**), which is similar to how you start a multiline comment but with an additional asterisk, and ends with an asterisk and forward slash (*/). DocComments may be a single line in size but may also span multiple lines, in which case each line must start with an asterisk. It is customary, and recommended, to align the asterisks vertically when spanning multiple lines.
> 
> So, a single line DocComment looks like this:
>
> ```php
> /** This is a single line DocComment. */
> ```
>
> And a multiline DocComment looks like this:
>
> ```php
> /**
>  * This is a multi-line DocComment.
>  */
> ```




Pull request:
Gh 2345: Support single line comments
Use a simple parser in the plain docblock class, fixes #2345 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15108a9d..b6bf86a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,12 @@ Changelog
 
 Improvements:
 
-  - Do not promote parameters that are used in parent constructor #2119
+  - Do not promote parameters that are used in parent constructor #2119 @mamazu
+  - Improve detection of Xdebug @bart-jaskulsi #2347
+  - Improve plain docblock parsing #2345
+  - Generate `@param` tag for iterables #2343 @mamazu
+
+## 2023.08.06-1
 
 Bug fixes:
 
diff --git a/lib/WorseReflection/Core/DocBlock/PlainDocblock.php b/lib/WorseReflection/Core/DocBlock/PlainDocblock.php
index 93a7783f..2a08618e 100644
--- a/lib/WorseReflection/Core/DocBlock/PlainDocblock.php
+++ b/lib/WorseReflection/Core/DocBlock/PlainDocblock.php
@@ -12,10 +12,16 @@ use Phpactor\WorseReflection\Core\Type;
 use Phpactor\WorseReflection\Core\TypeFactory;
 use Phpactor\WorseReflection\Core\Deprecation;
 use Phpactor\WorseReflection\Core\TypeResolver;
-use function preg_replace;
 
 class PlainDocblock implements DocBlock
 {
+    private const LEADING = 3;
+    private const TEXT = 2;
+    private const EXTRA_ASTERIX = 1;
+    private const START = 0;
+    private const WHITESPACE = 4;
+    private const WS_OR_TERMINATE = 5;
+
     private string $raw;
 
     public function __construct(string $raw = '')
@@ -55,21 +61,63 @@ class PlainDocblock implements DocBlock
 
     public function formatted(): string
     {
-        $lines = [];
-        foreach (explode("\n", $this->raw) as $line) {
-            if (str_contains($line, '@')) {
-                continue;
-            }
-            if (str_contains($line, '/*')) {
-                continue;
+        $mode = self::START;
+        $buffer = '';
+        $text = '';
+        foreach (str_split(trim($this->raw)) as $char) {
+            $buffer .= $char;
+
+            switch ($mode) {
+                case self::START:
+                    if ($buffer === '/*') {
+                        $mode = self::EXTRA_ASTERIX;
+                        $buffer = '';
+                    }
+                    break;
+                case self::EXTRA_ASTERIX:
+                    if ($buffer === '*') {
+                        $buffer = '';
+                    }
+                    $mode = self::TEXT;
+                    break;
+                case self::TEXT:
+                    if ($char === "\n") {
+                        $text .= "\n";
+                        $mode = self::LEADING;
+                        break;
+                    }
+                    if ($char === '*') {
+                        $mode = self::WS_OR_TERMINATE;
+                        break;
+                    }
+                    $text .= $char;
+                    break;
+                case self::LEADING:
+                    if ($char === '*') {
+                        $mode = self::WS_OR_TERMINATE;
+                    }
+                    break;
+                case self::WHITESPACE:
+                    if ($char !== ' ') {
+                        $mode = self::TEXT;
+                        $text .= $char;
+                    }
+                    break;
+                case self::WS_OR_TERMINATE:
+                    if ($char === '/') {
+                        return trim($text);
+                    }
+
+                    if ($char !== ' ') {
+                        $text = trim($text, ' ') . $char;
+                        $mode = self::TEXT;
+                        break;
+                    }
+                    break;
             }
-            if (str_contains($line, '*/')) {
-                continue;
-            }
-            $line = trim(preg_replace('{\s+\*}', '', $line, 1));
-            $lines[] = $line;
         }
-        return trim(implode("\n", $lines));
+
+        return $text;
     }
 
     public function returnType(): Type


Test patch:
diff --git a/lib/WorseReflection/Tests/Unit/Core/DocBlock/PlainDocblockTest.php b/lib/WorseReflection/Tests/Unit/Core/DocBlock/PlainDocblockTest.php
index d9384da9..044332a4 100644
--- a/lib/WorseReflection/Tests/Unit/Core/DocBlock/PlainDocblockTest.php
+++ b/lib/WorseReflection/Tests/Unit/Core/DocBlock/PlainDocblockTest.php
@@ -33,6 +33,13 @@ class PlainDocblockTest extends TestCase
         )->formatted());
     }
 
+    public function testSingle(): void
+    {
+        self::assertEquals('hello world', $this->createDocblock(
+            '/** hello world */'
+        )->formatted());
+    }
+
     private function createDocblock(string $string): DocBlock
     {
         return new PlainDocblock($string);