Repository: phpactor/phpactor (php)

Issue:
Initialised properties don't appear in LSP document symbols
Given this class:

```php
<?php

namespace App;

class Foo
{
    private string $uninitialised;

    private string $initialised = '';
}
```

.. I see these document symbols (using [Trouble](https://github.com/folke/trouble.nvim) in Neovim):

```
  Document Symbols  2 
   └╴  src/App/Foo.php  2 
     └╴  Foo class Foo [5, 7]
       └╴  uninitialised private string $uninitialised; [7, 20]
```

Note that the property `$initialised` doesn't appear in the list of document symbols.


Pull request description:
Fixing the document symbol provider
Closes #2677 

If the property is initialized then the node is an assignment expression and just a simple variable. It's weird that the shape of the tree changes if the property is initialized. But :shrug: 

Patch:
diff --git a/lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php b/lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php
index 2d64f0f6..4484c301 100644
--- a/lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php
+++ b/lib/Extension/LanguageServerSymbolProvider/Adapter/TolerantDocumentSymbolProvider.php
@@ -11,6 +11,8 @@ use Microsoft\PhpParser\Node\DelimitedList\ConstElementList;
 use Microsoft\PhpParser\Node\DelimitedList\ExpressionList;
 use Microsoft\PhpParser\Node\EnumCaseDeclaration;
 use Microsoft\PhpParser\Node\EnumMembers;
+use Microsoft\PhpParser\Node\Expression;
+use Microsoft\PhpParser\Node\Expression\AssignmentExpression;
 use Microsoft\PhpParser\Node\Expression\Variable;
 use Microsoft\PhpParser\Node\InterfaceMembers;
 use Microsoft\PhpParser\Node\MethodDeclaration;
@@ -141,21 +143,13 @@ class TolerantDocumentSymbolProvider implements DocumentSymbolProvider
         }
 
         if ($node instanceof Variable) {
-            if ($node->getFirstAncestor(PropertyDeclaration::class)) {
-                return new DocumentSymbol(
-                    (string)$node->getName(),
-                    SymbolKind::PROPERTY,
-                    new Range(
-                        PositionConverter::intByteOffsetToPosition($node->parent->getStartPosition(), $source),
-                        PositionConverter::intByteOffsetToPosition($node->parent->getEndPosition(), $source)
-                    ),
-                    new Range(
-                        PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source),
-                        PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source)
-                    ),
-                    children: []
-                );
-            }
+            return $this->resolvePropertyVariable($node, $source);
+        }
+
+        if ($node instanceof AssignmentExpression) {
+            /** @var Expression $left */
+            $left = $node->leftOperand;
+            return $this->resolvePropertyVariable($left, $source);
         }
 
         if ($node instanceof ConstElement) {
@@ -209,6 +203,30 @@ class TolerantDocumentSymbolProvider implements DocumentSymbolProvider
         return null;
     }
 
+
+    private function resolvePropertyVariable(Node $node, string $source): ?DocumentSymbol
+    {
+        if (!$node instanceof Variable) {
+            return null;
+        }
+        if (!$node->getFirstAncestor(PropertyDeclaration::class)) {
+            return null;
+        }
+        return new DocumentSymbol(
+            (string)$node->getName(),
+            SymbolKind::PROPERTY,
+            new Range(
+                PositionConverter::intByteOffsetToPosition($node->parent->getStartPosition(), $source),
+                PositionConverter::intByteOffsetToPosition($node->parent->getEndPosition(), $source)
+            ),
+            new Range(
+                PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $source),
+                PositionConverter::intByteOffsetToPosition($node->getEndPosition(), $source)
+            ),
+            children: []
+        );
+    }
+
     private function memberNodes(Node $node): Generator
     {
         return $node->getDescendantNodes(function (Node $node) {


Test patch:
diff --git a/lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php b/lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php
index 32db8259..e904594b 100644
--- a/lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php
+++ b/lib/Extension/LanguageServerSymbolProvider/Tests/Unit/Adapter/TolerantDocumentSymbolProviderTest.php
@@ -116,6 +116,27 @@ class TolerantDocumentSymbolProviderTest extends TestCase
             ]
         ];
 
+        yield 'class property with value' => [
+            '<?php class Foo { private $bar = "Hallo"; }',
+            [
+                DocumentSymbol::fromArray([
+                    'name' => 'Foo',
+                    'kind' => SymbolKind::CLASS_,
+                    'range' => $this->dummyRange(),
+                    'selectionRange' => $this->dummyRange(),
+                    'children' => [
+                        DocumentSymbol::fromArray([
+                            'name' => 'bar',
+                            'kind' => SymbolKind::PROPERTY,
+                            'range' => $this->dummyRange(),
+                            'selectionRange' => $this->dummyRange(),
+                            'children' => [],
+                        ]),
+                    ]
+                ])
+            ]
+        ];
+
         yield 'class property' => [
             '<?php class Foo { private $bar; }',
             [