From f2a94a0da3fe2a3c49a9c247dd9e1e83e857c51e Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 20 Jan 2021 20:36:18 +0000
Subject: [PATCH 01/63] Start again
---
lib/DefaultValue.php | 40 -------
lib/Docblock.php | 42 -------
lib/DocblockException.php | 9 --
lib/DocblockFactory.php | 143 -----------------------
lib/DocblockType.php | 82 -------------
lib/DocblockTypes.php | 42 -------
lib/InheritTag.php | 11 --
lib/Method/Parameter.php | 46 --------
lib/Parser.php | 62 ----------
lib/Parser/MethodParser.php | 79 -------------
lib/Parser/ParameterParser.php | 81 -------------
lib/Parser/TypesParser.php | 53 ---------
lib/Tag.php | 8 --
lib/Tag/AbstractTag.php | 0
lib/Tag/DeprecatedTag.php | 28 -----
lib/Tag/MethodTag.php | 66 -----------
lib/Tag/MixinTag.php | 29 -----
lib/Tag/ParamTag.php | 11 --
lib/Tag/PropertyTag.php | 37 ------
lib/Tag/ReturnTag.php | 29 -----
lib/Tag/VarTag.php | 40 -------
lib/Tags.php | 46 --------
tests/DocblockFactoryTest.php | 111 ------------------
tests/DocblockTest.php | 44 -------
tests/DocblockTypeTest.php | 18 ---
tests/Parser/MethodParserTest.php | 169 ---------------------------
tests/Parser/ParameterParserTest.php | 59 ----------
tests/Parser/TypesParserTest.php | 54 ---------
tests/ParserTest.php | 140 ----------------------
tests/Tag/MethodTagTest.php | 17 ---
tests/Tag/PropertyTagTest.php | 17 ---
tests/Tag/VarTagTest.php | 19 ---
tests/TagsTest.php | 31 -----
33 files changed, 1663 deletions(-)
delete mode 100644 lib/DefaultValue.php
delete mode 100644 lib/Docblock.php
delete mode 100644 lib/DocblockException.php
delete mode 100644 lib/DocblockFactory.php
delete mode 100644 lib/DocblockType.php
delete mode 100644 lib/DocblockTypes.php
delete mode 100644 lib/InheritTag.php
delete mode 100644 lib/Method/Parameter.php
delete mode 100644 lib/Parser.php
delete mode 100644 lib/Parser/MethodParser.php
delete mode 100644 lib/Parser/ParameterParser.php
delete mode 100644 lib/Parser/TypesParser.php
delete mode 100644 lib/Tag.php
delete mode 100644 lib/Tag/AbstractTag.php
delete mode 100644 lib/Tag/DeprecatedTag.php
delete mode 100644 lib/Tag/MethodTag.php
delete mode 100644 lib/Tag/MixinTag.php
delete mode 100644 lib/Tag/ParamTag.php
delete mode 100644 lib/Tag/PropertyTag.php
delete mode 100644 lib/Tag/ReturnTag.php
delete mode 100644 lib/Tag/VarTag.php
delete mode 100644 lib/Tags.php
delete mode 100644 tests/DocblockFactoryTest.php
delete mode 100644 tests/DocblockTest.php
delete mode 100755 tests/DocblockTypeTest.php
delete mode 100644 tests/Parser/MethodParserTest.php
delete mode 100644 tests/Parser/ParameterParserTest.php
delete mode 100644 tests/Parser/TypesParserTest.php
delete mode 100644 tests/ParserTest.php
delete mode 100644 tests/Tag/MethodTagTest.php
delete mode 100644 tests/Tag/PropertyTagTest.php
delete mode 100644 tests/Tag/VarTagTest.php
delete mode 100644 tests/TagsTest.php
diff --git a/lib/DefaultValue.php b/lib/DefaultValue.php
deleted file mode 100644
index 6e05838f..00000000
--- a/lib/DefaultValue.php
+++ /dev/null
@@ -1,40 +0,0 @@
-value = $value;
- $this->none = $none;
- }
-
- public function isDefined(): bool
- {
- return $this->value !== null;
- }
-
- public static function none()
- {
- return new self(null, true);
- }
-
- public static function ofValue($value)
- {
- return new self($value);
- }
-
- public function value()
- {
- return $this->value;
- }
-}
diff --git a/lib/Docblock.php b/lib/Docblock.php
deleted file mode 100644
index d2a47267..00000000
--- a/lib/Docblock.php
+++ /dev/null
@@ -1,42 +0,0 @@
-tags = $tags;
- $this->prose = $prose;
- }
-
- public static function fromTags(array $tags)
- {
- return new self(Tags::fromArray($tags));
- }
-
- public static function fromProseAndTags(string $prose, array $tags)
- {
- return new self(Tags::fromArray($tags), $prose);
- }
-
- public function tags(): Tags
- {
- return $this->tags;
- }
-
- public function prose(): string
- {
- return $this->prose;
- }
-}
diff --git a/lib/DocblockException.php b/lib/DocblockException.php
deleted file mode 100644
index 521f50e7..00000000
--- a/lib/DocblockException.php
+++ /dev/null
@@ -1,9 +0,0 @@
-parser = $parser ?: new Parser();
- $this->typesParser = $typesParser ?: new TypesParser();
- $this->methodParser = $methodParser ?: new MethodParser($this->typesParser);
- }
-
- public function create(string $docblock): Docblock
- {
- $tags = [];
- list($prose, $tagData) = $this->parser->parse($docblock);
- foreach ($tagData as $tagName => $metadatas) {
- foreach ($metadatas as $metadata) {
- switch (strtolower(trim($tagName))) {
- case 'var':
- $tags[] = $this->createVarTag($metadata);
- break;
- case 'param':
- $tags[] = $this->createParamTag($metadata);
- break;
- case 'method':
- $tags[] = $this->createMethodTag($metadata);
- break;
- case 'mixin':
- $tags[] = $this->createMixinTag($metadata);
- break;
- case 'property':
- $tags[] = $this->createPropertyTag($metadata);
- break;
- case 'deprecated':
- $tags[] = $this->createDeprecatedTag($metadata);
- break;
- case 'return':
- $tags[] = $this->createReturnTag($metadata);
- break;
- case 'inheritdoc':
- $tags[] = new InheritTag();
- }
- }
- }
-
- return Docblock::fromProseAndTags(implode(PHP_EOL, $prose), array_filter(
- $tags,
- function (Tag $tag) {
- return $tag !== null;
- }
- ));
- }
-
- private function createVarTag(array $metadata): VarTag
- {
- if (null === $types = array_shift($metadata)) {
- $types = '';
- }
-
- $varName = array_shift($metadata);
-
- return new VarTag($this->typesParser->parseTypes($types), $varName);
- }
-
- private function createParamTag(array $metadata): ParamTag
- {
- if (null === $types = array_shift($metadata)) {
- $types = '';
- }
-
- $varName = array_shift($metadata);
-
- return new ParamTag($this->typesParser->parseTypes($types), $varName);
- }
-
- private function createMethodTag(array $metadata): MethodTag
- {
- return $this->methodParser->parseMethod($metadata);
- }
-
- private function createReturnTag(array $metadata): ReturnTag
- {
- if (null === $types = array_shift($metadata)) {
- $types = '';
- }
-
- $methodName = array_shift($metadata);
-
- return new ReturnTag($this->typesParser->parseTypes($types));
- }
-
- private function createPropertyTag($metadata)
- {
- if (null === $types = array_shift($metadata)) {
- $types = '';
- }
-
- $propertyName = array_shift($metadata);
-
- return new PropertyTag($this->typesParser->parseTypes($types), ltrim($propertyName, '$'));
- }
-
- private function createDeprecatedTag(array $metadata): DeprecatedTag
- {
- return new DeprecatedTag(implode(' ', $metadata));
- }
-
- private function createMixinTag(array $metadata): ?MixinTag
- {
- $fqn = array_shift($metadata);
- if (null === $fqn) {
- return null;
- }
- return new MixinTag($fqn);
- }
-}
diff --git a/lib/DocblockType.php b/lib/DocblockType.php
deleted file mode 100644
index 30e867f0..00000000
--- a/lib/DocblockType.php
+++ /dev/null
@@ -1,82 +0,0 @@
-type = $type;
- $this->iteratedType = $iteratedType;
- }
-
- public static function collectionOf(string $type, string $iteratedType): DocblockType
- {
- return new self($type, $iteratedType);
- }
-
- public static function of(string $type): DocblockType
- {
- return new self($type);
- }
-
- public static function fullyQualifiedNameOf(string $string): self
- {
- $type = static::of($string);
- $type->isFullyQualified = true;
-
- return $type;
- }
-
- public static function arrayOf(string $type): DocblockType
- {
- return new self('array', $type);
- }
-
- /**
- * @return string|null
- */
- public function iteratedType()
- {
- return $this->iteratedType;
- }
-
- public function isArray(): bool
- {
- return $this->type === 'array';
- }
-
- public function isCollection(): bool
- {
- return $this->iteratedType && $this->type !== 'array';
- }
-
- public function __toString()
- {
- if ($this->isFullyQualified) {
- return '\\' . $this->type;
- }
-
- return $this->type;
- }
-
- public function isFullyQualified(): bool
- {
- return $this->isFullyQualified;
- }
-}
diff --git a/lib/DocblockTypes.php b/lib/DocblockTypes.php
deleted file mode 100644
index 343c7abc..00000000
--- a/lib/DocblockTypes.php
+++ /dev/null
@@ -1,42 +0,0 @@
-add($item);
- }
- }
-
- public static function empty(): DocblockTypes
- {
- return new self([]);
- }
-
- public static function fromStringTypes($types)
- {
- return new self(array_map(function (string $type) {
- return DocblockType::of($type);
- }, $types));
- }
-
- public static function fromDocblockTypes(array $docblocktypes): DocblockTypes
- {
- return new self($docblocktypes);
- }
-
- public function getIterator()
- {
- return new \ArrayIterator($this->docblocktypes);
- }
-
- private function add(DocblockType $item)
- {
- $this->docblocktypes[] = $item;
- }
-}
diff --git a/lib/InheritTag.php b/lib/InheritTag.php
deleted file mode 100644
index 652ac6aa..00000000
--- a/lib/InheritTag.php
+++ /dev/null
@@ -1,11 +0,0 @@
-name = $name;
- $this->types = $types ?: DocblockTypes::empty();
- $this->defaultValue = $defaultValue ?: DefaultValue::none();
- }
-
- public function name(): string
- {
- return $this->name;
- }
-
- public function types(): DocblockTypes
- {
- return $this->types;
- }
-
- public function defaultValue(): DefaultValue
- {
- return $this->defaultValue;
- }
-}
diff --git a/lib/Parser.php b/lib/Parser.php
deleted file mode 100644
index 64c94885..00000000
--- a/lib/Parser.php
+++ /dev/null
@@ -1,62 +0,0 @@
-?\[\\\\\]|\w\s]+)?}';
-
- public function parse($docblock): array
- {
- $lines = explode(PHP_EOL, $docblock);
- $tags = [];
- $prose = [];
-
- foreach ($lines as $line) {
- if (0 === preg_match(self::TAG, $line, $matches)) {
- if (null !== $line = $this->extractProse($line)) {
- $prose[] = $line;
- }
- continue;
- }
-
- $tagName = $matches[1];
-
- if (!isset($tags[$tagName])) {
- $tags[$tagName] = [];
- }
-
- $metadata = array_values(array_filter(explode(' ', trim($matches[2] ?? ''))));
- $tags[$tagName][] = $metadata;
- }
-
- return [$prose, $tags ];
- }
-
- private function extractProse(string $line)
- {
- $line = trim($line);
-
- if (empty($line)) {
- return;
- }
-
- if ($line == '/**') {
- return;
- }
-
- if ($line == '*') {
- return '';
- }
-
- if (substr($line, 0, 2) == '* ') {
- $line = substr($line, 2);
- }
-
- if (substr($line, 0, 2) == '*/') {
- return;
- }
-
- return $line;
- }
-}
diff --git a/lib/Parser/MethodParser.php b/lib/Parser/MethodParser.php
deleted file mode 100644
index ea0fb5fc..00000000
--- a/lib/Parser/MethodParser.php
+++ /dev/null
@@ -1,79 +0,0 @@
-typesParser = $typesParser ?: new TypesParser();
- $this->parameterParser = $parameterParser ?: new ParameterParser($typesParser);
- }
-
- public function parseMethod(array $parts): MethodTag
- {
- $method = implode(' ', $parts);
-
- list($static, $types, $methodName, $parameters) = $this->methodInfo($method, $parts);
-
- return new MethodTag(
- $this->typesParser->parseTypes($types),
- $methodName,
- $parameters,
- $static
- );
- }
-
- private function methodInfo(string $method, array $parts): array
- {
- if (empty($method)) {
- return [ false , '', $method, [] ];
- }
-
- if (substr($method, -1) !== ')') {
- $method .= '()';
- }
-
- if (preg_match('{(static)?\s*([\w\\\]+)?\s+(\w*?)\s*\((.*)\)}', $method, $parts)) {
- $static = $parts[1];
- $types = $parts[2];
- $methodName = $parts[3];
- $paramString = $parts[4];
-
- return [
- $static === 'static',
- $types,
- $methodName,
- $this->parseParameters($paramString),
- ];
- }
-
- return [ false, '', $method, [] ];
- }
-
- private function parseParameters(string $paramString): array
- {
- $parameters = array_map(function (string $param) {
- return trim($param);
- }, explode(', ', $paramString));
-
- $parameters = array_filter(array_map(function (string $paramString) {
- return $this->parameterParser->parse($paramString);
- }, $parameters));
-
- return $parameters;
- }
-}
diff --git a/lib/Parser/ParameterParser.php b/lib/Parser/ParameterParser.php
deleted file mode 100644
index f7157b71..00000000
--- a/lib/Parser/ParameterParser.php
+++ /dev/null
@@ -1,81 +0,0 @@
-typesParser = $typesParser ?: new TypesParser();
- }
-
- /**
- * @return Parameter|null
- */
- public function parse(string $parameterString)
- {
- list($parameterName, $types, $defaultValue) = $this->extractParts($parameterString);
-
- if (!$parameterName) {
- return null;
- }
-
- return new Parameter($parameterName, $types, $defaultValue);
- }
-
- private function extractParts(string $parameterString)
- {
- $parts = array_map('trim', explode(' ', $parameterString));
-
- $types = DocblockTypes::empty();
- $parameterName = null;
- $defaultValue = null;
-
- foreach ($parts as $index => $part) {
- if (substr($part, 0, 1) === '$') {
- $parameterName = substr($part, 1);
- continue;
- }
-
- if (substr($part, 0, 3) === '...') {
- $parameterName = substr($part, 4);
- continue;
- }
-
- if ($index === 0) {
- $types = $this->typesParser->parseTypes($part);
- continue;
- }
-
- if ($part === '=' && isset($parts[$index + 1])) {
- $defaultValue = $this->parseDefaultValue($parts[$index + 1]);
- break;
- }
- }
-
- return [$parameterName, $types, $defaultValue];
- }
-
- private function parseDefaultValue(string $defaultValueString): DefaultValue
- {
- if (is_numeric($defaultValueString)) {
- // hack to cast to either a float or an int
- return DefaultValue::ofValue($defaultValueString + 0);
- }
-
- if (in_array(substr($defaultValueString, 0, 1), ['"', '\''])) {
- return DefaultValue::ofValue(trim($defaultValueString, '"\''));
- }
-
- return DefaultValue::none();
- }
-}
diff --git a/lib/Parser/TypesParser.php b/lib/Parser/TypesParser.php
deleted file mode 100644
index bc7cdd61..00000000
--- a/lib/Parser/TypesParser.php
+++ /dev/null
@@ -1,53 +0,0 @@
-$}', $type, $matches)) {
- $type = $matches[1];
- $collectionType = trim($matches[2], self::MASK_WHITESPACE_AND_IGNORED_PREFIX);
- $docblockTypes[] = DocblockType::collectionOf($type, $collectionType);
- continue;
- }
-
- if (substr($type, -2) == '[]') {
- $type = trim(substr($type, 0, -2), self::MASK_WHITESPACE_AND_IGNORED_PREFIX);
- $docblockTypes[] = DocblockType::arrayOf($type);
- continue;
- }
-
- if (substr($type, 0, 1) === '\\') {
- $docblockTypes[] = DocblockType::fullyQualifiedNameOf(substr($type, 1));
- continue;
- }
-
- $docblockTypes[] = DocblockType::of($type);
- }
-
- return DocblockTypes::fromDocblockTypes($docblockTypes);
- }
-}
diff --git a/lib/Tag.php b/lib/Tag.php
deleted file mode 100644
index 729ffd54..00000000
--- a/lib/Tag.php
+++ /dev/null
@@ -1,8 +0,0 @@
-message = $message;
- }
-
- public function name()
- {
- return 'deprecated';
- }
-
- public function message(): ?string
- {
- return $this->message;
- }
-}
diff --git a/lib/Tag/MethodTag.php b/lib/Tag/MethodTag.php
deleted file mode 100644
index 8656f3ba..00000000
--- a/lib/Tag/MethodTag.php
+++ /dev/null
@@ -1,66 +0,0 @@
-types = $types;
- $this->methodName = $methodName;
- $this->parameters = $parameters;
- $this->isStatic = $isStatic;
- }
-
- public function name()
- {
- return 'method';
- }
-
- public function types(): DocblockTypes
- {
- return $this->types;
- }
-
- public function methodName()
- {
- return $this->methodName;
- }
-
- public function parameters(): array
- {
- return $this->parameters;
- }
-
- public function isStatic(): bool
- {
- return $this->isStatic;
- }
-}
diff --git a/lib/Tag/MixinTag.php b/lib/Tag/MixinTag.php
deleted file mode 100644
index 5247759b..00000000
--- a/lib/Tag/MixinTag.php
+++ /dev/null
@@ -1,29 +0,0 @@
-fqn = $fqn;
- }
-
- public function fqn(): string
- {
- return $this->fqn;
- }
-
- public function name(): string
- {
- return 'mixin';
- }
-}
-
diff --git a/lib/Tag/ParamTag.php b/lib/Tag/ParamTag.php
deleted file mode 100644
index e18de696..00000000
--- a/lib/Tag/ParamTag.php
+++ /dev/null
@@ -1,11 +0,0 @@
-types = $types;
- $this->propertyName = $propertyName;
- }
-
- public function name()
- {
- return 'property';
- }
-
- public function propertyName()
- {
- return $this->propertyName;
- }
-
- public function types(): DocblockTypes
- {
- return $this->types;
- }
-}
diff --git a/lib/Tag/ReturnTag.php b/lib/Tag/ReturnTag.php
deleted file mode 100644
index 680cc62b..00000000
--- a/lib/Tag/ReturnTag.php
+++ /dev/null
@@ -1,29 +0,0 @@
-types = $types;
- }
-
- public function name()
- {
- return 'return';
- }
-
- public function types(): DocblockTypes
- {
- return $this->types;
- }
-}
diff --git a/lib/Tag/VarTag.php b/lib/Tag/VarTag.php
deleted file mode 100644
index c41a96db..00000000
--- a/lib/Tag/VarTag.php
+++ /dev/null
@@ -1,40 +0,0 @@
-types = $types;
- $this->varName = $varName;
- }
-
- public function types(): DocblockTypes
- {
- return $this->types;
- }
-
- public function varName()
- {
- return $this->varName;
- }
-}
diff --git a/lib/Tags.php b/lib/Tags.php
deleted file mode 100644
index e9535838..00000000
--- a/lib/Tags.php
+++ /dev/null
@@ -1,46 +0,0 @@
-add($item);
- }
- }
-
- public static function fromArray(array $tags): Tags
- {
- return new self($tags);
- }
-
- public function getIterator()
- {
- return new \ArrayIterator($this->tags);
- }
-
- public function byName(string $name): Tags
- {
- $name = strtolower($name);
- return new self(array_filter($this->tags, function (Tag $tag) use ($name) {
- return $name == strtolower($tag->name());
- }));
- }
-
- /**
- * {@inheritDoc}
- */
- public function count()
- {
- return count($this->tags);
- }
-
- private function add(Tag $item)
- {
- $this->tags[] = $item;
- }
-}
diff --git a/tests/DocblockFactoryTest.php b/tests/DocblockFactoryTest.php
deleted file mode 100644
index c893836c..00000000
--- a/tests/DocblockFactoryTest.php
+++ /dev/null
@@ -1,111 +0,0 @@
-create($docblock);
- $this->assertEquals($expected->tags(), $docblock->tags());
- }
-
- public function provideCreate()
- {
- return [
- 'no tags' => [
- '/** */',
- Docblock::fromTags([]),
- ],
- 'var single type' => [
- '** @var Foobar */',
- Docblock::fromTags([ new VarTag(DocblockTypes::fromStringTypes([ 'Foobar' ])), ]),
- ],
- 'var multiple types' => [
- '/** @var Foobar|string|null */',
- Docblock::fromTags([ new VarTag(DocblockTypes::fromStringTypes([ 'Foobar', 'string', 'null' ])) ]),
- ],
- 'var union types' => [
- '/** @var Foobar&string */',
- Docblock::fromTags([ new VarTag(DocblockTypes::fromStringTypes([ 'Foobar', 'string' ])) ]),
- ],
- 'param single type' => [
- '/** @param Foobar $foobar */',
- Docblock::fromTags([ new ParamTag(DocblockTypes::fromStringTypes([ 'Foobar' ]), '$foobar') ]),
- ],
- 'method single type' => [
- '/** @method Foobar foobar() */',
- Docblock::fromTags([ new MethodTag(DocblockTypes::fromStringTypes([ 'Foobar' ]), 'foobar') ]),
- ],
- 'property' => [
- '/** @property string $foo */',
- Docblock::fromTags([ new PropertyTag(DocblockTypes::fromStringTypes(['string']), 'foo') ]),
- ],
-
- 'property no type' => [
- '/** @property Foo */',
- Docblock::fromTags([ new PropertyTag(DocblockTypes::fromStringTypes(['Foo']), '') ]),
- ],
-
- 'property with nothing' => [
- '/** @property */',
- Docblock::fromTags([ new PropertyTag(DocblockTypes::empty(), '') ]),
- ],
-
- 'return single type' => [
- '/** @return Foobar foobar() */',
- Docblock::fromTags([ new ReturnTag(DocblockTypes::fromStringTypes([ 'Foobar' ])) ]),
- ],
- 'return single nullable type' => [
- '/** @return ?Foobar foobar() */',
- Docblock::fromTags([ new ReturnTag(DocblockTypes::fromStringTypes([ 'Foobar' ])) ]),
- ],
- 'inheritdoc' => [
- '/** {@inheritDoc} */',
- Docblock::fromTags([ new InheritTag() ]),
- ],
-
- 'var no type' => [
- '/** @var */',
- Docblock::fromTags([ new VarTag(DocblockTypes::empty()) ]),
- ],
- 'param no type' => [
- '/** @param */',
- Docblock::fromTags([ new ParamTag(DocblockTypes::empty(), '') ]),
- ],
- 'method no type' => [
- '/** @method */',
- Docblock::fromTags([ new MethodTag(DocblockTypes::empty(), '') ]),
- ],
- 'return no type' => [
- '/** @return */',
- Docblock::fromTags([ new ReturnTag(DocblockTypes::empty()) ]),
- ],
- 'deprecated' => [
- '/** @deprecated This is deprecated */',
- Docblock::fromTags([ new DeprecatedTag('This is deprecated') ]),
- ],
- 'mixin' => [
- '/** @mixin Foobar\\Barfoo */',
- Docblock::fromTags([ new MixinTag('Foobar\\Barfoo') ]),
- ],
- ];
- }
-}
diff --git a/tests/DocblockTest.php b/tests/DocblockTest.php
deleted file mode 100644
index 83a888ea..00000000
--- a/tests/DocblockTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-tag1 = $this->prophesize(Tag::class);
- }
-
- public function testFromTags()
- {
- $docblock = Docblock::fromTags([
- $this->tag1->reveal()
- ]);
-
- $this->assertEquals(Tags::fromArray([$this->tag1->reveal()]), $docblock->tags());
- }
-
- public function testFromProseAndTags()
- {
- $docblock = Docblock::fromProseAndTags(
- 'Hello this is prose',
- [
- $this->tag1->reveal()
- ]
- );
-
- $this->assertEquals(Tags::fromArray([$this->tag1->reveal()]), $docblock->tags());
- $this->assertEquals('Hello this is prose', $docblock->prose());
- }
-}
diff --git a/tests/DocblockTypeTest.php b/tests/DocblockTypeTest.php
deleted file mode 100755
index e46f7112..00000000
--- a/tests/DocblockTypeTest.php
+++ /dev/null
@@ -1,18 +0,0 @@
-assertTrue($type->isCollection());
- $this->assertFalse($type->isArray());
- $this->assertEquals('Foobar', $type->__toString());
- $this->assertEquals('Item', $type->iteratedType());
- }
-}
diff --git a/tests/Parser/MethodParserTest.php b/tests/Parser/MethodParserTest.php
deleted file mode 100644
index bafc44ce..00000000
--- a/tests/Parser/MethodParserTest.php
+++ /dev/null
@@ -1,169 +0,0 @@
-assertEquals($expected, $parser->parseMethod($parts));
- }
-
- public function provideCreate()
- {
- return [
- 'no parts' => [
- [ ],
- new MethodTag(DocblockTypes::empty(), ''),
- ],
- //'type only parts' => [
- // [ 'Foobar' ],
- // new MethodTag(DocblockTypes::fromStringTypes(['Foobar']), ''),
- //],
- 'no parenthesis' => [
- [ 'Foobar', 'foobar' ],
- new MethodTag(DocblockTypes::fromStringTypes([ 'Foobar' ]), 'foobar'),
- ],
- 'single type' => [
- [ 'Foobar', 'foobar()' ],
- new MethodTag(DocblockTypes::fromStringTypes([ 'Foobar' ]), 'foobar'),
- ],
- 'with parameters' => [
- [ 'Foobar', 'foobar(Foobar $foobar, string $foo, $bar)' ],
- new MethodTag(
- DocblockTypes::fromStringTypes([ 'Foobar' ]),
- 'foobar',
- [
- new Parameter('foobar', DocblockTypes::fromDocblockTypes([ DocblockType::of('Foobar') ])),
- new Parameter('foo', DocblockTypes::fromDocblockTypes([ DocblockType::of('string') ])),
- new Parameter('bar', DocblockTypes::fromDocblockTypes([ ])),
- ]
- ),
- ],
- 'with parameters and default value' => [
- [ 'Foobar', 'foobar(string $foo = "hello", $bar)' ],
- new MethodTag(
- DocblockTypes::fromStringTypes([ 'Foobar' ]),
- 'foobar',
- [
- new Parameter(
- 'foo',
- DocblockTypes::fromDocblockTypes([ DocblockType::of('string') ]),
- DefaultValue::ofValue('hello')
- ),
- new Parameter('bar', DocblockTypes::fromDocblockTypes([ ])),
- ]
- ),
- ],
- 'static method' => [
- [ 'static', 'Foobar', 'foobar()' ],
- new MethodTag(
- DocblockTypes::fromStringTypes([ 'Foobar' ]),
- 'foobar',
- [],
- true
- ),
- ],
- 'phpspec be constructed with' => [
- [ 'void', 'foobar(...$arguments)' ],
- new MethodTag(
- DocblockTypes::fromStringTypes(['void']),
- 'foobar',
- [
- new Parameter(
- 'arguments',
- DocblockTypes::fromDocblockTypes([])
- ),
- ]
- ),
- ],
- 'phpspec be constructed through' => [
- [ 'void', 'beConstructedThrough($factoryMethod, array $constructorArguments = array())' ],
- new MethodTag(
- DocblockTypes::fromStringTypes(['void']),
- 'beConstructedThrough',
- [
- new Parameter(
- 'factoryMethod',
- DocblockTypes::fromDocblockTypes([])
- ),
- new Parameter(
- 'constructorArguments',
- DocblockTypes::fromDocblockTypes([ DocblockType::of('array') ]),
- DefaultValue::none()
- ),
- ]
- ),
- ],
- 'phpspec should have count' => [
- [ 'void', 'shouldHaveCount($count)' ],
- new MethodTag(
- DocblockTypes::fromStringTypes(['void']),
- 'shouldHaveCount',
- [
- new Parameter(
- 'count',
- DocblockTypes::empty()
- ),
- ]
- ),
-
- ],
- 'laravel db' => [
- [ 'static', '\\Illuminate\\Database\\Query\\Builder', 'table(string $table)' ],
- new MethodTag(
- DocblockTypes::fromDocblockTypes([DocblockType::fullyQualifiedNameOf('Illuminate\\Database\\Query\\Builder')]),
- 'table',
- [
- new Parameter(
- 'table',
- DocblockTypes::fromStringTypes(['string'])
- ),
- ],
- true
- ),
-
- ],
- 'laravel route' => [
- [
- 'static',
- '\\Illuminate\\Routing\\Route',
- 'get(string $uri, \\Closure|array|string|callable|null $action = null)'
- ],
- new MethodTag(
- DocblockTypes::fromDocblockTypes([DocblockType::fullyQualifiedNameOf('Illuminate\\Routing\\Route')]),
- 'get',
- [
- new Parameter(
- 'uri',
- DocblockTypes::fromStringTypes(['string'])
- ),
- new Parameter(
- 'action',
- DocblockTypes::fromDocblockTypes([
- DocblockType::fullyQualifiedNameOf('Closure'),
- DocblockType::of('array'),
- DocblockType::of('string'),
- DocblockType::of('callable'),
- DocblockType::of('null'),
- ])
- ),
- ],
- true
- )
- ],
- ];
- }
-}
diff --git a/tests/Parser/ParameterParserTest.php b/tests/Parser/ParameterParserTest.php
deleted file mode 100644
index 18f8c9e5..00000000
--- a/tests/Parser/ParameterParserTest.php
+++ /dev/null
@@ -1,59 +0,0 @@
-assertEquals($expected, $parser->parse($paramString));
- }
-
- public function provideCreate()
- {
- return [
- 'no parts' => [
- '',
- null
- ],
- 'lone variable' => [
- '$foobar',
- new Parameter('foobar'),
- ],
- 'typed variable' => [
- 'Foobar $foobar',
- new Parameter('foobar', DocblockTypes::fromStringTypes(['Foobar'])),
- ],
- 'typed variable with string default' => [
- 'Foobar $foobar = "foobar"',
- new Parameter('foobar', DocblockTypes::fromStringTypes(['Foobar']), DefaultValue::ofValue('foobar')),
- ],
- 'typed variable with single quoted default string' => [
- 'Foobar $foobar = \'foobar\'',
- new Parameter('foobar', DocblockTypes::fromStringTypes(['Foobar']), DefaultValue::ofValue('foobar')),
- ],
- 'typed variable with float' => [
- 'Foobar $foobar = 1.234',
- new Parameter('foobar', DocblockTypes::fromStringTypes(['Foobar']), DefaultValue::ofValue(1.234)),
- ],
- 'typed variable with int' => [
- 'Foobar $foobar = 1234',
- new Parameter('foobar', DocblockTypes::fromStringTypes(['Foobar']), DefaultValue::ofValue(1234)),
- ],
- 'typed variable with static call (not supported)' => [
- 'Foobar $foobar = Barfoo::FOOBAR',
- new Parameter('foobar', DocblockTypes::fromStringTypes(['Foobar'])),
- ],
- ];
- }
-}
diff --git a/tests/Parser/TypesParserTest.php b/tests/Parser/TypesParserTest.php
deleted file mode 100644
index 669371c0..00000000
--- a/tests/Parser/TypesParserTest.php
+++ /dev/null
@@ -1,54 +0,0 @@
-assertEquals($expected, $parser->parseTypes($types));
- }
-
- public function provideParseTypes()
- {
- return [
- [
- 'Foobar',
- DocblockTypes::fromStringTypes(['Foobar']),
- ],
- [
- 'Foobar[]',
- DocblockTypes::fromDocblockTypes([ DocblockType::arrayOf('Foobar') ]),
- ],
- [
- 'Foobar- ',
- DocblockTypes::fromDocblockTypes([ DocblockType::collectionOf('Foobar', 'Item') ]),
- ],
- [
- '\Foobar\Foobar',
- DocblockTypes::fromDocblockTypes([ DocblockType::fullyQualifiedNameOf('Foobar\Foobar') ]),
- ],
- [
- '?Foobar',
- DocblockTypes::fromStringTypes(['Foobar']),
- ],
- [
- '?Foobar[]',
- DocblockTypes::fromDocblockTypes([ DocblockType::arrayOf('Foobar') ]),
- ],
- [
- 'Foobar',
- DocblockTypes::fromDocblockTypes([ DocblockType::collectionOf('Foobar', 'Item') ]),
- ],
- ];
- }
-}
diff --git a/tests/ParserTest.php b/tests/ParserTest.php
deleted file mode 100644
index 7a911617..00000000
--- a/tests/ParserTest.php
+++ /dev/null
@@ -1,140 +0,0 @@
-parse($docblock);
- $this->assertEquals($expected, $tags);
- }
-
- public function provideParseTags()
- {
- yield [
- '/** @var Foobar */',
- [ 'var' => [ [ 'Foobar' ] ] ],
- ];
-
- yield [
- '/** @var Foobar[] */',
- [ 'var' => [ [ 'Foobar[]' ] ] ],
- ];
-
- yield 'for collection' => [
- '/** @var Foobar
- */',
- [ 'var' => [ [ 'Foobar
- ' ] ] ],
- ];
-
- yield [
- '/** @var Foobar $foobar */',
- [ 'var' => [ [ 'Foobar', '$foobar' ] ] ],
- ];
-
- yield 'named var with irregular spacing' => [
- '/** @var Foobar $foobar */',
- [ 'var' => [ [ 'Foobar', '$foobar' ] ] ],
- ];
-
- yield [
- <<<'EOT'
-/**
- * @var Foobar $foobar
- * @var Barfoo $barfoo
- **/
-EOT
- ,
- ['var' => [
- [ 'Foobar', '$foobar' ],
- [ 'Barfoo', '$barfoo' ]
- ]],
- ];
-
- yield [
- <<<'EOT'
-/**
- * @var Foobar $foobar Hello this is description
- **/
-EOT
- ,
- ['var' => [
- [ 'Foobar', '$foobar', 'Hello', 'this', 'is', 'description' ],
- ]],
- ];
-
- yield 'method with fully qualified type' => [
- <<<'EOT'
-/**
- * @method \Foobar\Barfoo foobar()
- **/
-EOT
- ,
- ['method' => [
- [ '\Foobar\Barfoo', 'foobar()' ],
- ]],
- ];
-
- yield 'method with parameters' => [
- '/** @method \Barfoo foobar($foobar, string $foo) */',
- [ 'method' => [ [
- '\Barfoo',
- 'foobar($foobar,', 'string', '$foo)'
- ] ] ],
- ];
-
- yield 'method with array type' => [
- '/** @method Foobar[] */',
- [ 'method' => [ [ 'Foobar[]' ] ] ],
- ];
-
- yield 'phpspec should have count' => [
- '* @method void shouldHaveCount($count)',
- [ 'method' => [ [ 'void', 'shouldHaveCount($count)' ] ] ],
- ];
-
- yield 'argument with default value' => [
- '* @method void shouldHaveCount($count = 5)',
- [ 'method' => [ [
- 'void',
- 'shouldHaveCount($count', '=', '5)'
- ] ] ],
- ];
- }
-
- /**
- * @dataProvider provideParseProse
- */
- public function testParseProse($docblock, $expected)
- {
- $parser = new Parser();
- list($prose, $tags) = $parser->parse($docblock);
- $this->assertEquals($expected, $prose);
- }
-
- public function provideParseProse()
- {
- return [
- [
- <<<'EOT'
-/**
- * Hello
- *
- * This is a description
- *
- * @return Foo
- */
-EOT
- ,
- [ 'Hello', '', 'This is a description', '' ],
- ],
- ];
- }
-}
diff --git a/tests/Tag/MethodTagTest.php b/tests/Tag/MethodTagTest.php
deleted file mode 100644
index 2f663016..00000000
--- a/tests/Tag/MethodTagTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-assertEquals('foobar', $tag->methodName());
- $this->assertEquals(DocblockTypes::fromStringTypes([ 'Foobar' ]), $tag->types());
- }
-}
diff --git a/tests/Tag/PropertyTagTest.php b/tests/Tag/PropertyTagTest.php
deleted file mode 100644
index c4f41faa..00000000
--- a/tests/Tag/PropertyTagTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-assertEquals('foobar', $tag->propertyName());
- $this->assertEquals(DocblockTypes::fromStringTypes([ 'Foobar' ]), $tag->types());
- }
-}
diff --git a/tests/Tag/VarTagTest.php b/tests/Tag/VarTagTest.php
deleted file mode 100644
index c8e92620..00000000
--- a/tests/Tag/VarTagTest.php
+++ /dev/null
@@ -1,19 +0,0 @@
-assertEquals(DocblockTypes::fromStringTypes(['Foobar']), $tag->types());
-
- $tag = new VarTag(DocblockTypes::fromStringTypes([ 'Foobar' ]), '$foobar');
- $this->assertEquals('$foobar', $tag->varName());
- }
-}
diff --git a/tests/TagsTest.php b/tests/TagsTest.php
deleted file mode 100644
index 38cc1ee7..00000000
--- a/tests/TagsTest.php
+++ /dev/null
@@ -1,31 +0,0 @@
-assertEquals($expected, $original->byName('foobar'));
- }
-}
From a816265de653dff806a523b69d5788bf3a635487 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 20 Jan 2021 20:36:25 +0000
Subject: [PATCH 02/63] Initial
---
composer.json | 3 +-
lib/Lexer.php | 103 +++++++++++++++++++++++++++++++++++++++
lib/Token.php | 50 +++++++++++++++++++
tests/Unit/LexerTest.php | 68 ++++++++++++++++++++++++++
4 files changed, 223 insertions(+), 1 deletion(-)
create mode 100644 lib/Lexer.php
create mode 100644 lib/Token.php
create mode 100644 tests/Unit/LexerTest.php
diff --git a/composer.json b/composer.json
index eda3af40..7405546d 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,8 @@
"friendsofphp/php-cs-fixer": "^2.17",
"phpstan/phpstan": "~0.12.0",
"phpunit/phpunit": "^9.0",
- "phpspec/prophecy-phpunit": "^2.0"
+ "phpspec/prophecy-phpunit": "^2.0",
+ "symfony/var-dumper": "^5.2"
},
"extra": {
"branch-alias": {
diff --git a/lib/Lexer.php b/lib/Lexer.php
new file mode 100644
index 00000000..92c0c2d0
--- /dev/null
+++ b/lib/Lexer.php
@@ -0,0 +1,103 @@
+',
+
+ // variable name
+ '\$[a-zA-Z0-9_\x80-\xff]+',
+
+ // name
+ '[^a-zA-Z0-9_\x80-\xff]+',
+ ];
+
+ /**
+ * @var string[]
+ */
+ private $ignorePatterns = [
+ '\s+'
+ ];
+
+ /**
+ * @return Token[]
+ */
+ public function lex(string $docblock): array
+ {
+ $pattern = sprintf(
+ '{(%s)|%s}',
+ implode(')|(', $this->patterns),
+ implode('|', $this->ignorePatterns)
+ );
+ $chunks = (array)preg_split(
+ $pattern,
+ $docblock,
+ null,
+ PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE
+ );
+
+ $tokens = [];
+ foreach ($chunks as $chunk) {
+ [ $value, $offset ] = $chunk;
+ $tokens[] = new Token($offset, $this->resolveType($value), $value);
+ }
+
+ return $tokens;
+ }
+
+ private function resolveType($value): string
+ {
+ if (0 === strpos($value, '$')) {
+ return Token::T_VARIABLE;
+ }
+
+ if (0 === strpos($value, '@')) {
+ return Token::T_TAG;
+ }
+
+ if (trim($value) === '') {
+ return Token::T_WHITESPACE;
+ }
+
+ if (trim($value) === '') {
+ return Token::T_WHITESPACE;
+ }
+
+ if (ctype_alpha($value) || $value === '_') {
+ return Token::T_LABEL;
+ }
+
+ if ($value === ']') {
+ return Token::T_BRACKET_SQUARE_CLOSE;
+ }
+
+ if ($value === '[') {
+ return Token::T_BRACKET_SQUARE_OPEN;
+ }
+
+ if ($value === '<') {
+ return Token::T_BRACKET_ANGLE_OPEN;
+ }
+
+ if ($value === '>') {
+ return Token::T_BRACKET_ANGLE_CLOSE;
+ }
+
+ return Token::T_UNKNOWN;
+ }
+
+}
diff --git a/lib/Token.php b/lib/Token.php
new file mode 100644
index 00000000..29d31dab
--- /dev/null
+++ b/lib/Token.php
@@ -0,0 +1,50 @@
+byteOffset = $byteOffset;
+ $this->type = $type;
+ $this->value = $value;
+ }
+
+ public function byteOffset(): int
+ {
+ return $this->byteOffset;
+ }
+
+ public function value(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
new file mode 100644
index 00000000..986c9cbd
--- /dev/null
+++ b/tests/Unit/LexerTest.php
@@ -0,0 +1,68 @@
+ $expectedTokens
+ */
+ public function testLex(string $lex, array $expectedTokens): void
+ {
+ $tokens = (new Lexer())->lex($lex);
+
+ self::assertCount(count($expectedTokens), $tokens, 'Expected number of tokens');
+
+ foreach ($tokens as $index => $token) {
+ [$type, $value] = $expectedTokens[$index];
+ $expectedToken = new Token($token->byteOffset(), $type, $value);
+ self::assertEquals($expectedToken, $token);
+ }
+ }
+
+ /**
+ * @return Generator
+ */
+ public function provideLex(): Generator
+ {
+ yield [ '', [] ];
+ yield [
+ 'Foobar',
+ [
+ [Token::T_LABEL, "Foobar"],
+ ]
+ ];
+ yield [
+ 'Foobar[]',
+ [
+ [Token::T_LABEL, "Foobar"],
+ [Token::T_BRACKET_SQUARE_OPEN, "["],
+ [Token::T_BRACKET_SQUARE_CLOSE, "]"],
+ ]
+ ];
+ yield [
+ 'Foobar',
+ [
+ [Token::T_LABEL, "Foobar"],
+ [Token::T_BRACKET_ANGLE_OPEN, "<"],
+ [Token::T_LABEL, "Barfoo"],
+ [Token::T_BRACKET_ANGLE_CLOSE, ">"],
+ ]
+ ];
+ yield [
+ 'Foobar',
+ [
+ [Token::T_LABEL, "Foobar"],
+ [Token::T_BRACKET_ANGLE_OPEN, "<"],
+ [Token::T_LABEL, "Barfoo"],
+ [Token::T_BRACKET_ANGLE_CLOSE, ">"],
+ ]
+ ];
+ }
+}
From 7c803edad1185606ec2f249357c4b3f5b8651477 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 20 Jan 2021 20:53:03 +0000
Subject: [PATCH 03/63] Progress
---
lib/Lexer.php | 35 +++++++++++++++++++----------------
lib/Token.php | 4 ++++
tests/Unit/LexerTest.php | 32 ++++++++++++++++++++++++++++++++
3 files changed, 55 insertions(+), 16 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 92c0c2d0..cfb40b4f 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -4,26 +4,17 @@
class Lexer
{
- private $position = 0;
-
/**
* @var string[]
*/
private $patterns = [
- // param tag
- '@[\w]+',
-
- // param tag
- '\s',
-
- // brackets
- '\[', '\]', '<', '>',
-
- // variable name
- '\$[a-zA-Z0-9_\x80-\xff]+',
-
- // name
- '[^a-zA-Z0-9_\x80-\xff]+',
+ '/\*+\s*\*?', // border
+ '@[\w]+', //tag
+ '\s+', // whitespace
+ ',', // comma
+ '\{', '\}', '\[', '\]', '<', '>', // brackets
+ '\$[a-zA-Z0-9_\x80-\xff]+', // variable
+ '[^a-zA-Z0-9_\x80-\xff]+', // label
];
/**
@@ -97,6 +88,18 @@ private function resolveType($value): string
return Token::T_BRACKET_ANGLE_CLOSE;
}
+ if ($value === '{') {
+ return Token::T_BRACKET_CURLY_OPEN;
+ }
+
+ if ($value === '}') {
+ return Token::T_BRACKET_CURLY_CLOSE;
+ }
+
+ if ($value === ',') {
+ return Token::T_COMMA;
+ }
+
return Token::T_UNKNOWN;
}
diff --git a/lib/Token.php b/lib/Token.php
index 29d31dab..e3fbb362 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -4,17 +4,21 @@
class Token
{
+ public const T_PHPDOC_BORDER= 'PHPDOC_BORDER';
public const T_BORDER = 'BORDER';
public const T_TEXT = 'TEXT';
public const T_VARIABLE = 'VARIABLE';
public const T_UNKNOWN = 'UNKNOWN';
public const T_TAG = 'TAG';
+ public const T_COMMA = 'COMMA';
public const T_LABEL = 'LABEL';
public const T_WHITESPACE = 'WHITESPACE';
public const T_BRACKET_SQUARE_OPEN = 'BRACKET_SQUARE_OPEN';
public const T_BRACKET_SQUARE_CLOSE = 'BRACKET_SQUARE_CLOSE';
public const T_BRACKET_ANGLE_OPEN = 'BRACKET_ANGLE_OPEN';
public const T_BRACKET_ANGLE_CLOSE = 'BRACKET_ANGLE_CLOSE';
+ public const T_BRACKET_CURLY_OPEN = 'BRACKET_CURLY_OPEN';
+ public const T_BRACKET_CURLY_CLOSE = 'BRACKET_CURLY_CLOSE';
/**
* @var int
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 986c9cbd..0d4ffc33 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -32,6 +32,26 @@ public function testLex(string $lex, array $expectedTokens): void
public function provideLex(): Generator
{
yield [ '', [] ];
+ yield [
+ <<<'EOT'
+/**
+ * Hello this is
+ */
+EOT
+
+ ,[
+ [Token::T_UNKNOWN, "/**\n *"],
+ [Token::T_WHITESPACE, " "],
+ [Token::T_LABEL, "Hello"],
+ [Token::T_WHITESPACE, " "],
+ [Token::T_LABEL, "this"],
+ [Token::T_WHITESPACE, " "],
+ [Token::T_LABEL, "is"],
+ [Token::T_WHITESPACE, "\n "],
+ [Token::T_UNKNOWN, "*/"],
+ ]
+ ];
+
yield [
'Foobar',
[
@@ -64,5 +84,17 @@ public function provideLex(): Generator
[Token::T_BRACKET_ANGLE_CLOSE, ">"],
]
];
+ yield [
+ 'Foobar{Barfoo, Foobar}',
+ [
+ [Token::T_LABEL, "Foobar"],
+ [Token::T_BRACKET_CURLY_OPEN, "{"],
+ [Token::T_LABEL, "Barfoo"],
+ [Token::T_COMMA, ","],
+ [Token::T_WHITESPACE, " "],
+ [Token::T_LABEL, "Foobar"],
+ [Token::T_BRACKET_CURLY_CLOSE, "}"],
+ ]
+ ];
}
}
From 817a5ee97b82c5781acca42ded1ef6d2f0d78706 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 20 Jan 2021 21:24:12 +0000
Subject: [PATCH 04/63] open, close and leading phpdoc
---
lib/Lexer.php | 25 ++++++++++++++++++++++---
lib/Parser.php | 0
lib/Token.php | 3 +++
tests/Unit/LexerTest.php | 8 ++++++--
4 files changed, 31 insertions(+), 5 deletions(-)
create mode 100644 lib/Parser.php
diff --git a/lib/Lexer.php b/lib/Lexer.php
index cfb40b4f..7b35d635 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -8,7 +8,7 @@ class Lexer
* @var string[]
*/
private $patterns = [
- '/\*+\s*\*?', // border
+ '^/\*+\s*\*?', // start tag
'@[\w]+', //tag
'\s+', // whitespace
',', // comma
@@ -42,16 +42,35 @@ public function lex(string $docblock): array
);
$tokens = [];
+ $prevChunk = [null,null];
foreach ($chunks as $chunk) {
[ $value, $offset ] = $chunk;
- $tokens[] = new Token($offset, $this->resolveType($value), $value);
+ [ $prevValue, $_ ] = $prevChunk;
+ $tokens[] = new Token(
+ $offset,
+ $this->resolveType($value, $prevValue),
+ $value
+ );
+ $prevChunk = $chunk;
}
return $tokens;
}
- private function resolveType($value): string
+ private function resolveType(string $value, ?string $prevValue): string
{
+ if (false !== strpos($value, '/*')) {
+ return Token::T_PHPDOC_OPEN;
+ }
+
+ if (false !== strpos($value, '*/')) {
+ return Token::T_PHPDOC_CLOSE;
+ }
+
+ if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
+ return Token::T_PHPDOC_LEADING;
+ }
+
if (0 === strpos($value, '$')) {
return Token::T_VARIABLE;
}
diff --git a/lib/Parser.php b/lib/Parser.php
new file mode 100644
index 00000000..e69de29b
diff --git a/lib/Token.php b/lib/Token.php
index e3fbb362..8784e07e 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -5,6 +5,9 @@
class Token
{
public const T_PHPDOC_BORDER= 'PHPDOC_BORDER';
+ public const T_PHPDOC_OPEN = 'PHPDOC_OPEN';
+ public const T_PHPDOC_LEADING = 'PHPDOC_LEADING';
+ public const T_PHPDOC_CLOSE = 'PHPDOC_CLOSE';
public const T_BORDER = 'BORDER';
public const T_TEXT = 'TEXT';
public const T_VARIABLE = 'VARIABLE';
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 0d4ffc33..9f43b205 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -36,11 +36,12 @@ public function provideLex(): Generator
<<<'EOT'
/**
* Hello this is
+ * Multi
*/
EOT
,[
- [Token::T_UNKNOWN, "/**\n *"],
+ [Token::T_PHPDOC_OPEN, "/**\n *"],
[Token::T_WHITESPACE, " "],
[Token::T_LABEL, "Hello"],
[Token::T_WHITESPACE, " "],
@@ -48,7 +49,10 @@ public function provideLex(): Generator
[Token::T_WHITESPACE, " "],
[Token::T_LABEL, "is"],
[Token::T_WHITESPACE, "\n "],
- [Token::T_UNKNOWN, "*/"],
+ [Token::T_PHPDOC_LEADING, "* "],
+ [Token::T_LABEL, "Multi"],
+ [Token::T_WHITESPACE, "\n "],
+ [Token::T_PHPDOC_CLOSE, "*/"],
]
];
From afe8a54e2bde397851c6d3af25b3796d804db2b6 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 20 Jan 2021 21:30:13 +0000
Subject: [PATCH 05/63] Return collection
---
lib/Lexer.php | 7 ++-----
lib/Token.php | 2 +-
tests/Unit/LexerTest.php | 2 +-
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 7b35d635..cdc693f8 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -24,10 +24,7 @@ class Lexer
'\s+'
];
- /**
- * @return Token[]
- */
- public function lex(string $docblock): array
+ public function lex(string $docblock): Tokens
{
$pattern = sprintf(
'{(%s)|%s}',
@@ -54,7 +51,7 @@ public function lex(string $docblock): array
$prevChunk = $chunk;
}
- return $tokens;
+ return new Tokens($tokens);
}
private function resolveType(string $value, ?string $prevValue): string
diff --git a/lib/Token.php b/lib/Token.php
index 8784e07e..e40e19f7 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -2,7 +2,7 @@
namespace Phpactor\Docblock;
-class Token
+final class Token
{
public const T_PHPDOC_BORDER= 'PHPDOC_BORDER';
public const T_PHPDOC_OPEN = 'PHPDOC_OPEN';
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 9f43b205..624b6e5f 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -15,7 +15,7 @@ class LexerTest extends TestCase
*/
public function testLex(string $lex, array $expectedTokens): void
{
- $tokens = (new Lexer())->lex($lex);
+ $tokens = (new Lexer())->lex($lex)->toArray();
self::assertCount(count($expectedTokens), $tokens, 'Expected number of tokens');
From 1e9b7042335d9fe6c6c54470745932b9a0f8a3ec Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 20 Jan 2021 23:15:39 +0000
Subject: [PATCH 06/63] Parser test
---
lib/Ast/Docblock.php | 14 +++++++
lib/Ast/Element.php | 7 ++++
lib/Ast/NameNode.php | 18 +++++++++
lib/Ast/Node.php | 11 ++++++
lib/Ast/ParamNode.php | 21 ++++++++++
lib/Ast/TypeNode.php | 7 ++++
lib/Ast/VariableNode.php | 19 +++++++++
lib/Parser.php | 65 ++++++++++++++++++++++++++++++
lib/Token.php | 9 ++++-
lib/Tokens.php | 83 +++++++++++++++++++++++++++++++++++++++
tests/Unit/ParserTest.php | 56 ++++++++++++++++++++++++++
11 files changed, 309 insertions(+), 1 deletion(-)
create mode 100644 lib/Ast/Docblock.php
create mode 100644 lib/Ast/Element.php
create mode 100644 lib/Ast/NameNode.php
create mode 100644 lib/Ast/Node.php
create mode 100644 lib/Ast/ParamNode.php
create mode 100644 lib/Ast/TypeNode.php
create mode 100644 lib/Ast/VariableNode.php
create mode 100644 lib/Tokens.php
create mode 100644 tests/Unit/ParserTest.php
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
new file mode 100644
index 00000000..61bf1f37
--- /dev/null
+++ b/lib/Ast/Docblock.php
@@ -0,0 +1,14 @@
+children = $children;
+ }
+}
diff --git a/lib/Ast/Element.php b/lib/Ast/Element.php
new file mode 100644
index 00000000..94ed2a77
--- /dev/null
+++ b/lib/Ast/Element.php
@@ -0,0 +1,7 @@
+name = $name;
+ }
+}
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
new file mode 100644
index 00000000..1501db8e
--- /dev/null
+++ b/lib/Ast/Node.php
@@ -0,0 +1,11 @@
+type = $type;
+ $this->variable = $variable;
+ }
+}
diff --git a/lib/Ast/TypeNode.php b/lib/Ast/TypeNode.php
new file mode 100644
index 00000000..195602c1
--- /dev/null
+++ b/lib/Ast/TypeNode.php
@@ -0,0 +1,7 @@
+name = $name;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index e69de29b..cabeb595 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -0,0 +1,65 @@
+current()];
+ while ($token = $tokens->next()) {
+ assert($token instanceof Token);
+ if ($token->type() === Token::T_TAG) {
+ $children[] = $this->parseTag($token, $tokens);
+ continue;
+ }
+ $children[] = $token;
+ }
+
+ return new Docblock($children);
+ }
+
+ private function parseTag(Token $token, Tokens $tokens)
+ {
+ if ($token->value() === '@param') {
+ return $this->parseParam($tokens);
+ }
+ }
+
+ private function parseParam(Tokens $tokens): ParamNode
+ {
+ $type = $this->parseType($tokens->skip(Token::T_WHITESPACE));
+ $variable = $this->parseVariable($tokens->skip(Token::T_WHITESPACE));
+
+ return new ParamNode($type, $variable);
+ }
+
+ private function parseType(Tokens $tokens): ?TypeNode
+ {
+ if (!$tokens->isType(Token::T_LABEL)) {
+ return null;
+ }
+
+ $type = $tokens->current();
+
+ return new NameNode($type);
+ }
+
+ private function parseVariable(Tokens $tokens): ?VariableNode
+ {
+ if (!$tokens->isType(Token::T_VARIABLE)) {
+ return null;
+ }
+
+ $name = $tokens->current();
+
+ return new VariableNode($name);
+ }
+}
diff --git a/lib/Token.php b/lib/Token.php
index e40e19f7..91d15b4f 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -2,7 +2,9 @@
namespace Phpactor\Docblock;
-final class Token
+use Phpactor\Docblock\Ast\Element;
+
+final class Token implements Element
{
public const T_PHPDOC_BORDER= 'PHPDOC_BORDER';
public const T_PHPDOC_OPEN = 'PHPDOC_OPEN';
@@ -54,4 +56,9 @@ public function value(): string
{
return $this->value;
}
+
+ public function type(): string
+ {
+ return $this->type;
+ }
}
diff --git a/lib/Tokens.php b/lib/Tokens.php
new file mode 100644
index 00000000..10a91d0a
--- /dev/null
+++ b/lib/Tokens.php
@@ -0,0 +1,83 @@
+tokens = $tokens;
+ }
+
+ /**
+ * @return Token[]
+ */
+ public function toArray(): array
+ {
+ return $this->tokens;
+ }
+
+ /**
+ * @return ArrayIterator
+ */
+ public function getIterator(): ArrayIterator
+ {
+ return new ArrayIterator($this->tokens);
+ }
+
+ public function next(): ?Token
+ {
+ if (!isset($this->tokens[$this->position + 1])) {
+ return null;
+ }
+
+ return $this->tokens[++$this->position];
+ }
+
+ public function current(): Token
+ {
+ if (!isset($this->tokens[$this->position])) {
+ throw new RuntimeException(sprintf(
+ 'No token at current position "%s"',
+ $this->position
+ ));
+ }
+
+ return $this->tokens[$this->position];
+ }
+
+ /**
+ * Skip until all tokens of the given type
+ */
+ public function skip(string $type): self
+ {
+ while (null !== $current = $this->next()) {
+ if ($current->type() === $type) {
+ continue;
+ }
+
+ return $this;
+ }
+
+ return $this;
+ }
+
+ public function isType(string $type): bool
+ {
+ return $this->current()->type() === $type;
+ }
+}
diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php
new file mode 100644
index 00000000..ad6466f1
--- /dev/null
+++ b/tests/Unit/ParserTest.php
@@ -0,0 +1,56 @@
+parse((new Lexer())->lex($text));
+ dump($node);
+ self::assertEquals($expected, $node);
+ }
+
+ /**
+ * @return Generator
+ */
+ public function provideParse(): Generator
+ {
+ yield [
+ '/** Hello */',
+ new Docblock([
+ new Token(0, Token::T_PHPDOC_OPEN, '/** '),
+ new Token(4, Token::T_LABEL, 'Hello'),
+ new Token(9, Token::T_WHITESPACE, ' '),
+ new Token(10, Token::T_PHPDOC_CLOSE, '*/'),
+ ])
+ ];
+
+ yield [
+ '/** @param Foobar $foobar */',
+ new Docblock([
+ new Token(0, Token::T_PHPDOC_OPEN, '/** '),
+ new ParamNode(
+ new NameNode(new Token(11, Token::T_LABEL, 'Foobar')),
+ new VariableNode(new Token(18, Token::T_VARIABLE, '$foobar'))
+ ),
+ new Token(25, Token::T_WHITESPACE, ' '),
+ new Token(26, Token::T_PHPDOC_CLOSE, '*/'),
+ ])
+ ];
+ }
+}
From efa1e2a83c2409cd411dd7308566b591e2e173bf Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 20 Jan 2021 23:22:29 +0000
Subject: [PATCH 07/63] Updated README
---
README.md | 23 ++++-------------------
1 file changed, 4 insertions(+), 19 deletions(-)
diff --git a/README.md b/README.md
index 19a8b093..21267499 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,11 @@
Docblock Parser
===============
-Sub-standard docblock parser.
+PHP Docblock parser with the following aims:
-```php
-$docblock = (new DocblockFactory())->create('/** @var Foobar */');
-$vars = $docblock->tags()->byName('var');
-
-foreach ($vars as $var) {
- $var->type();
- $var->varName();
-}
-```
-
-Why?
-----
-
-There is already a [standards-compliant
-library](https://github.com/phpDocumentor/ReflectionDocBlock) for
-PHP-Documentor, however it is coupled to the PHPDocumentor type reflection
-library. This library only cares about parsing docblocks badly for
-[Phpactor](https://github.com/phpactor/phpactor).
+- Fast: has to be fast enough for IDE analysis.
+- Isomorphic: can be transformed back to the original text representation.
+- Positional: the positions of nodes are captured.
Contributing
------------
From 6c5ea45260e3933f6e8e7019944bd064b0796d98 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Thu, 21 Jan 2021 22:50:59 +0000
Subject: [PATCH 08/63] Parse lists
---
lib/Ast/Docblock.php | 13 +++
lib/Ast/Node.php | 8 +-
lib/Ast/ParamNode.php | 19 +++-
lib/Ast/TagNode.php | 7 ++
lib/Ast/Type/ClassNode.php | 24 +++++
lib/Ast/Type/ListNode.php | 35 +++++++
lib/Ast/Type/ScalarNode.php | 24 +++++
lib/Ast/TypeNode.php | 4 +-
lib/Ast/{NameNode.php => UnknownTag.php} | 2 +-
lib/Ast/VariableNode.php | 5 +
lib/Lexer.php | 8 +-
lib/Parser.php | 34 ++++++-
lib/Printer.php | 10 ++
lib/Printer/TestPrinter.php | 112 +++++++++++++++++++++++
lib/Token.php | 6 ++
lib/Tokens.php | 9 ++
tests/Printer/PrinterTest.php | 50 ++++++++++
tests/Printer/examples/list1.test | 9 ++
tests/Printer/examples/param1.test | 7 ++
tests/Printer/examples/param2.test | 9 ++
tests/Printer/examples/param3.test | 19 ++++
tests/Printer/examples/text1.test | 11 +++
tests/Unit/ParserTest.php | 4 +-
23 files changed, 413 insertions(+), 16 deletions(-)
create mode 100644 lib/Ast/TagNode.php
create mode 100644 lib/Ast/Type/ClassNode.php
create mode 100644 lib/Ast/Type/ListNode.php
create mode 100644 lib/Ast/Type/ScalarNode.php
rename lib/Ast/{NameNode.php => UnknownTag.php} (86%)
create mode 100644 lib/Printer.php
create mode 100644 lib/Printer/TestPrinter.php
create mode 100644 tests/Printer/PrinterTest.php
create mode 100644 tests/Printer/examples/list1.test
create mode 100644 tests/Printer/examples/param1.test
create mode 100644 tests/Printer/examples/param2.test
create mode 100644 tests/Printer/examples/param3.test
create mode 100644 tests/Printer/examples/text1.test
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
index 61bf1f37..1aec2f21 100644
--- a/lib/Ast/Docblock.php
+++ b/lib/Ast/Docblock.php
@@ -4,6 +4,11 @@
class Docblock extends Node
{
+ /**
+ * @var Element[]
+ */
+ private $children = [];
+
/**
* @param Element[] $children
*/
@@ -11,4 +16,12 @@ public function __construct(array $children)
{
$this->children = $children;
}
+
+ /**
+ * @return Element[]
+ */
+ public function children(): array
+ {
+ return $this->children;
+ }
}
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index 1501db8e..8ecbfb98 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -4,8 +4,8 @@
class Node implements Element
{
- /**
- * @var Element[]
- */
- protected $children = [];
+ public function shortName(): string
+ {
+ return substr(get_class($this), strrpos(get_class($this), '\\') + 1);
+ }
}
diff --git a/lib/Ast/ParamNode.php b/lib/Ast/ParamNode.php
index db3d280b..e2bac69e 100644
--- a/lib/Ast/ParamNode.php
+++ b/lib/Ast/ParamNode.php
@@ -2,20 +2,31 @@
namespace Phpactor\Docblock\Ast;
-class ParamNode extends Node
+class ParamNode extends TagNode
{
/**
- * @var TypeNode
+ * @var ?TypeNode
*/
private $type;
+
/**
- * @var VariableNode
+ * @var ?VariableNode
*/
private $variable;
- public function __construct(TypeNode $type, VariableNode $variable)
+ public function __construct(?TypeNode $type, ?VariableNode $variable)
{
$this->type = $type;
$this->variable = $variable;
}
+
+ public function type(): ?TypeNode
+ {
+ return $this->type;
+ }
+
+ public function variable(): ?VariableNode
+ {
+ return $this->variable;
+ }
}
diff --git a/lib/Ast/TagNode.php b/lib/Ast/TagNode.php
new file mode 100644
index 00000000..93ebc1c8
--- /dev/null
+++ b/lib/Ast/TagNode.php
@@ -0,0 +1,7 @@
+name = $name;
+ }
+
+ public function name(): Token
+ {
+ return $this->name;
+ }
+}
diff --git a/lib/Ast/Type/ListNode.php b/lib/Ast/Type/ListNode.php
new file mode 100644
index 00000000..aef076f2
--- /dev/null
+++ b/lib/Ast/Type/ListNode.php
@@ -0,0 +1,35 @@
+type = $type;
+ $this->listChars = $listChars;
+ }
+
+ public function type(): TypeNode
+ {
+ return $this->type;
+ }
+
+ public function listChars(): Token
+ {
+ return $this->listChars;
+ }
+}
diff --git a/lib/Ast/Type/ScalarNode.php b/lib/Ast/Type/ScalarNode.php
new file mode 100644
index 00000000..f7e41397
--- /dev/null
+++ b/lib/Ast/Type/ScalarNode.php
@@ -0,0 +1,24 @@
+name = $name;
+ }
+
+ public function name(): Token
+ {
+ return $this->name;
+ }
+}
diff --git a/lib/Ast/TypeNode.php b/lib/Ast/TypeNode.php
index 195602c1..2ca7a693 100644
--- a/lib/Ast/TypeNode.php
+++ b/lib/Ast/TypeNode.php
@@ -2,6 +2,8 @@
namespace Phpactor\Docblock\Ast;
-class TypeNode extends Node
+use Phpactor\Docblock\Token;
+
+abstract class TypeNode extends Node
{
}
diff --git a/lib/Ast/NameNode.php b/lib/Ast/UnknownTag.php
similarity index 86%
rename from lib/Ast/NameNode.php
rename to lib/Ast/UnknownTag.php
index 3ef61d93..3679740c 100644
--- a/lib/Ast/NameNode.php
+++ b/lib/Ast/UnknownTag.php
@@ -4,7 +4,7 @@
use Phpactor\Docblock\Token;
-class NameNode extends TypeNode
+class UnknownTag extends TagNode
{
/**
* @var Token
diff --git a/lib/Ast/VariableNode.php b/lib/Ast/VariableNode.php
index 1178dac6..6bcca641 100644
--- a/lib/Ast/VariableNode.php
+++ b/lib/Ast/VariableNode.php
@@ -16,4 +16,9 @@ public function __construct(Token $name)
{
$this->name = $name;
}
+
+ public function name(): Token
+ {
+ return $this->name;
+ }
}
diff --git a/lib/Lexer.php b/lib/Lexer.php
index cdc693f8..a6d65ffb 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -9,7 +9,9 @@ class Lexer
*/
private $patterns = [
'^/\*+\s*\*?', // start tag
- '@[\w]+', //tag
+ '\s*\*', // border
+ '\[\]', //tag
+ '@\w+', //tag
'\s+', // whitespace
',', // comma
'\{', '\}', '\[', '\]', '<', '>', // brackets
@@ -116,6 +118,10 @@ private function resolveType(string $value, ?string $prevValue): string
return Token::T_COMMA;
}
+ if ($value === '[]') {
+ return Token::T_LIST;
+ }
+
return Token::T_UNKNOWN;
}
diff --git a/lib/Parser.php b/lib/Parser.php
index cabeb595..a5d932fe 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -3,17 +3,26 @@
namespace Phpactor\Docblock;
use Phpactor\Docblock\Ast\Docblock;
-use Phpactor\Docblock\Ast\NameNode;
+use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Ast\ParamNode;
+use Phpactor\Docblock\Ast\TagNode;
use Phpactor\Docblock\Ast\TypeNode;
+use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\Type\ScalarNode;
+use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\VariableNode;
final class Parser
{
+ private const SCALAR_TYPES = [
+ 'int', 'float', 'bool', 'string'
+ ];
+
public function parse(Tokens $tokens): Node
{
$children = [$tokens->current()];
+
while ($token = $tokens->next()) {
assert($token instanceof Token);
if ($token->type() === Token::T_TAG) {
@@ -26,11 +35,13 @@ public function parse(Tokens $tokens): Node
return new Docblock($children);
}
- private function parseTag(Token $token, Tokens $tokens)
+ private function parseTag(Token $token, Tokens $tokens): TagNode
{
if ($token->value() === '@param') {
return $this->parseParam($tokens);
}
+
+ return new UnknownTag($token);
}
private function parseParam(Tokens $tokens): ParamNode
@@ -43,13 +54,30 @@ private function parseParam(Tokens $tokens): ParamNode
private function parseType(Tokens $tokens): ?TypeNode
{
+ $isList = false;
+
if (!$tokens->isType(Token::T_LABEL)) {
return null;
}
+
$type = $tokens->current();
- return new NameNode($type);
+ if ($tokens->peek()->type() === Token::T_LIST) {
+ $tokens->next();
+ return new ListNode($this->createTypeFromToken($type), $tokens->current());
+ }
+
+ return $this->createTypeFromToken($type);
+ }
+
+ private function createTypeFromToken(Token $type): TypeNode
+ {
+ if (in_array($type->value(), self::SCALAR_TYPES)) {
+ return new ScalarNode($type);
+ }
+
+ return new ClassNode($type);
}
private function parseVariable(Tokens $tokens): ?VariableNode
diff --git a/lib/Printer.php b/lib/Printer.php
new file mode 100644
index 00000000..f7459b3e
--- /dev/null
+++ b/lib/Printer.php
@@ -0,0 +1,10 @@
+out = [];
+
+ $this->render($node);
+
+ return implode("", $this->out);
+ }
+
+ private function render(?Element $node): void
+ {
+ if (null === $node) {
+ $this->out[] = '#missing#';
+ return;
+ }
+
+ if ($node instanceof Docblock) {
+ $this->renderDocblock($node);
+ return;
+ }
+
+ if ($node instanceof Token) {
+ $this->out[] = $node->value();
+ return;
+ }
+
+ if ($node instanceof ParamNode) {
+ $this->renderParam($node);
+ return;
+ }
+
+ if ($node instanceof ListNode) {
+ $this->renderListNode($node);
+ return;
+ }
+
+ if ($node instanceof TypeNode) {
+ $this->renderTypeNode($node);
+ return;
+ }
+
+ if ($node instanceof VariableNode) {
+ $this->renderVariableNode($node);
+ return;
+ }
+
+ throw new RuntimeException(sprintf(
+ 'Do not know how to render "%s"',
+ get_class($node)
+ ));
+ }
+
+ private function renderDocblock(Docblock $node): void
+ {
+ foreach ($node->children() as $child) {
+ $this->render($child);
+ }
+ }
+
+ private function renderParam(ParamNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ $this->out[] = ',';
+ $this->render($node->variable());
+ $this->out[] = ')';
+ }
+
+ private function renderTypeNode(TypeNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->name());
+ $this->out[] = ')';
+ }
+
+ private function renderVariableNode(VariableNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->name());
+ $this->out[] = ')';
+ }
+
+ private function renderListNode(ListNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ $this->out[] = ')';
+ }
+}
diff --git a/lib/Token.php b/lib/Token.php
index 91d15b4f..5350327b 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -16,6 +16,7 @@ final class Token implements Element
public const T_UNKNOWN = 'UNKNOWN';
public const T_TAG = 'TAG';
public const T_COMMA = 'COMMA';
+ public const T_LIST = '[]';
public const T_LABEL = 'LABEL';
public const T_WHITESPACE = 'WHITESPACE';
public const T_BRACKET_SQUARE_OPEN = 'BRACKET_SQUARE_OPEN';
@@ -61,4 +62,9 @@ public function type(): string
{
return $this->type;
}
+
+ public function __toString(): string
+ {
+ return $this->value;
+ }
}
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 10a91d0a..69bb0326 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -39,6 +39,15 @@ public function getIterator(): ArrayIterator
return new ArrayIterator($this->tokens);
}
+ public function peek(): ?Token
+ {
+ if (!isset($this->tokens[$this->position + 1])) {
+ return null;
+ }
+
+ return $this->tokens[$this->position + 1];
+ }
+
public function next(): ?Token
{
if (!isset($this->tokens[$this->position + 1])) {
diff --git a/tests/Printer/PrinterTest.php b/tests/Printer/PrinterTest.php
new file mode 100644
index 00000000..3d0ca500
--- /dev/null
+++ b/tests/Printer/PrinterTest.php
@@ -0,0 +1,50 @@
+parse((new Lexer())->lex($parts[0]));
+ $rendered = (new TestPrinter())->print($node);
+
+ /**
+ * @phpstan-ignore-next-line
+ */
+ if (!isset($parts[1]) || $update) {
+ file_put_contents($path, implode("---\n", [$parts[0], $rendered]));
+ $this->markTestSkipped('Generated output');
+ return;
+ }
+
+ self::assertEquals(ltrim($parts[1]), $rendered);
+ }
+
+ /**
+ * @return Generator
+ */
+ public function provideExamples(): Generator
+ {
+ foreach ((array)glob(__DIR__ . '/examples/*.test') as $path) {
+ yield [
+ $path
+ ];
+ }
+ }
+}
diff --git a/tests/Printer/examples/list1.test b/tests/Printer/examples/list1.test
new file mode 100644
index 00000000..94d5be11
--- /dev/null
+++ b/tests/Printer/examples/list1.test
@@ -0,0 +1,9 @@
+/**
+ * @param Foobar[] $foobar
+ * @param string[] $strings
+ */
+---
+/**
+ * ParamNode(ListNode(ClassNode(Foobar)),VariableNode($foobar))
+ * ParamNode(ListNode(ScalarNode(string)),VariableNode($strings))
+ */
diff --git a/tests/Printer/examples/param1.test b/tests/Printer/examples/param1.test
new file mode 100644
index 00000000..ee64e32a
--- /dev/null
+++ b/tests/Printer/examples/param1.test
@@ -0,0 +1,7 @@
+/**
+ * @param Foobar $foobar
+ */
+---
+/**
+ * ParamNode(ClassNode(Foobar),VariableNode($foobar))
+ */
diff --git a/tests/Printer/examples/param2.test b/tests/Printer/examples/param2.test
new file mode 100644
index 00000000..2eb6c34d
--- /dev/null
+++ b/tests/Printer/examples/param2.test
@@ -0,0 +1,9 @@
+/**
+ * @param Foobar $foobar
+ * @param string $barfoo
+ */
+---
+/**
+ * ParamNode(ClassNode(Foobar),VariableNode($foobar))
+ * ParamNode(ScalarNode(string),VariableNode($barfoo))
+ */
diff --git a/tests/Printer/examples/param3.test b/tests/Printer/examples/param3.test
new file mode 100644
index 00000000..eb7e6809
--- /dev/null
+++ b/tests/Printer/examples/param3.test
@@ -0,0 +1,19 @@
+/**
+ * Hello World
+ *
+ * @param Foobar $foobar
+ *
+ * @param string $barfoo
+ *
+ * @param bool $bool
+ */
+---
+/**
+ * Hello World
+ *
+ * ParamNode(ClassNode(Foobar),VariableNode($foobar))
+ *
+ * ParamNode(ScalarNode(string),VariableNode($barfoo))
+ *
+ * ParamNode(ScalarNode(bool),VariableNode($bool))
+ */
diff --git a/tests/Printer/examples/text1.test b/tests/Printer/examples/text1.test
new file mode 100644
index 00000000..bf18af6d
--- /dev/null
+++ b/tests/Printer/examples/text1.test
@@ -0,0 +1,11 @@
+/**
+ * Hello World
+ *
+ * This is some text
+ */
+---
+/**
+ * Hello World
+ *
+ * This is some text
+ */
diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php
index ad6466f1..b9661e6c 100644
--- a/tests/Unit/ParserTest.php
+++ b/tests/Unit/ParserTest.php
@@ -5,7 +5,7 @@
use Generator;
use PHPUnit\Framework\TestCase;
use Phpactor\Docblock\Ast\Docblock;
-use Phpactor\Docblock\Ast\NameNode;
+use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Ast\ParamNode;
use Phpactor\Docblock\Ast\VariableNode;
@@ -45,7 +45,7 @@ public function provideParse(): Generator
new Docblock([
new Token(0, Token::T_PHPDOC_OPEN, '/** '),
new ParamNode(
- new NameNode(new Token(11, Token::T_LABEL, 'Foobar')),
+ new ClassNode(new Token(11, Token::T_LABEL, 'Foobar')),
new VariableNode(new Token(18, Token::T_VARIABLE, '$foobar'))
),
new Token(25, Token::T_WHITESPACE, ' '),
From 11cb6c1e53b1a4430a17ae8edc005f7982016e5d Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Fri, 22 Jan 2021 00:07:59 +0000
Subject: [PATCH 09/63] Printer tests OK, docblock start / end broken
---
lib/Ast/Type/GenericNode.php | 58 ++++++++++++++++++++++++++++
lib/Ast/TypeList.php | 42 ++++++++++++++++++++
lib/Lexer.php | 1 +
lib/Parser.php | 40 ++++++++++++++++++-
lib/Printer/TestPrinter.php | 26 +++++++++++++
lib/Token.php | 2 +-
lib/Tokens.php | 4 ++
tests/Printer/examples/generic1.test | 7 ++++
tests/Printer/examples/generic2.test | 7 ++++
tests/Printer/examples/generic3.test | 7 ++++
tests/Printer/examples/generic4.test | 7 ++++
tests/Unit/ParserTest.php | 25 +++++-------
12 files changed, 209 insertions(+), 17 deletions(-)
create mode 100644 lib/Ast/Type/GenericNode.php
create mode 100644 lib/Ast/TypeList.php
create mode 100644 tests/Printer/examples/generic1.test
create mode 100644 tests/Printer/examples/generic2.test
create mode 100644 tests/Printer/examples/generic3.test
create mode 100644 tests/Printer/examples/generic4.test
diff --git a/lib/Ast/Type/GenericNode.php b/lib/Ast/Type/GenericNode.php
new file mode 100644
index 00000000..66fd2bf2
--- /dev/null
+++ b/lib/Ast/Type/GenericNode.php
@@ -0,0 +1,58 @@
+open = $open;
+ $this->close = $close;
+ $this->parameters = $parameters;
+ $this->type = $type;
+ }
+
+ public function close(): Token
+ {
+ return $this->close;
+ }
+
+ public function open(): Token
+ {
+ return $this->open;
+ }
+
+ public function parameters(): TypeList
+ {
+ return $this->parameters;
+ }
+
+ public function type(): TypeNode
+ {
+ return $this->type;
+ }
+}
diff --git a/lib/Ast/TypeList.php b/lib/Ast/TypeList.php
new file mode 100644
index 00000000..811f4f40
--- /dev/null
+++ b/lib/Ast/TypeList.php
@@ -0,0 +1,42 @@
+
+ */
+class TypeList implements IteratorAggregate, Countable
+{
+ /**
+ * @var TypeNode[]
+ */
+ private $typeList;
+
+ /**
+ * @param TypeNode[] $typeList
+ */
+ public function __construct(array $typeList)
+ {
+ $this->typeList = $typeList;
+ }
+
+ /**
+ * @return ArrayIterator
+ */
+ public function getIterator(): ArrayIterator
+ {
+ return new ArrayIterator($this->typeList);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function count()
+ {
+ return count($this->typeList);
+ }
+}
diff --git a/lib/Lexer.php b/lib/Lexer.php
index a6d65ffb..f23f91cc 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -9,6 +9,7 @@ class Lexer
*/
private $patterns = [
'^/\*+\s*\*?', // start tag
+ '\*/', // close tag
'\s*\*', // border
'\[\]', //tag
'@\w+', //tag
diff --git a/lib/Parser.php b/lib/Parser.php
index a5d932fe..4f751df6 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -3,11 +3,13 @@
namespace Phpactor\Docblock;
use Phpactor\Docblock\Ast\Docblock;
+use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Ast\ParamNode;
use Phpactor\Docblock\Ast\TagNode;
use Phpactor\Docblock\Ast\TypeNode;
+use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
use Phpactor\Docblock\Ast\Type\ScalarNode;
use Phpactor\Docblock\Ast\UnknownTag;
@@ -46,7 +48,8 @@ private function parseTag(Token $token, Tokens $tokens): TagNode
private function parseParam(Tokens $tokens): ParamNode
{
- $type = $this->parseType($tokens->skip(Token::T_WHITESPACE));
+ $tokens->next();
+ $type = $this->parseType($tokens);
$variable = $this->parseVariable($tokens->skip(Token::T_WHITESPACE));
return new ParamNode($type, $variable);
@@ -54,6 +57,7 @@ private function parseParam(Tokens $tokens): ParamNode
private function parseType(Tokens $tokens): ?TypeNode
{
+ $tokens->skip(Token::T_WHITESPACE);
$isList = false;
if (!$tokens->isType(Token::T_LABEL)) {
@@ -64,10 +68,30 @@ private function parseType(Tokens $tokens): ?TypeNode
$type = $tokens->current();
if ($tokens->peek()->type() === Token::T_LIST) {
+ $tokens->next();
$tokens->next();
return new ListNode($this->createTypeFromToken($type), $tokens->current());
}
+ if ($tokens->peek()->type() === Token::T_BRACKET_ANGLE_OPEN) {
+ $open = $tokens->next();
+ $tokens->next();
+ $typeList = $this->parseTypeList($tokens);
+
+ if (!$tokens->isType(Token::T_BRACKET_ANGLE_CLOSE)) {
+ return null;
+ }
+ $tokens->next();
+
+ return new GenericNode(
+ $open,
+ $this->createTypeFromToken($type),
+ $typeList,
+ $tokens->current()
+ );
+ }
+
+ $tokens->next();
return $this->createTypeFromToken($type);
}
@@ -90,4 +114,18 @@ private function parseVariable(Tokens $tokens): ?VariableNode
return new VariableNode($name);
}
+
+ private function parseTypeList(Tokens $tokens): TypeList
+ {
+ $types = [];
+ while (true) {
+ $types[] = $this->parseType($tokens);
+ if ($tokens->current()->type() !== Token::T_COMMA) {
+ break;
+ }
+ $tokens->next();
+ }
+
+ return new TypeList($types);
+ }
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 9cc3aeff..0edeabe0 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -4,10 +4,12 @@
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Element;
+use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Ast\ParamNode;
+use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
use Phpactor\Docblock\Ast\VariableNode;
use Phpactor\Docblock\Printer;
@@ -57,6 +59,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof GenericNode) {
+ $this->renderGenericNode($node);
+ return;
+ }
+
if ($node instanceof TypeNode) {
$this->renderTypeNode($node);
return;
@@ -109,4 +116,23 @@ private function renderListNode(ListNode $node): void
$this->render($node->type());
$this->out[] = ')';
}
+
+ private function renderGenericNode(GenericNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ $this->out[] = ',';
+ $this->renderTypeList($node->parameters());
+ $this->out[] = ')';
+ }
+
+ private function renderTypeList(TypeList $typeList): void
+ {
+ foreach ($typeList as $i => $param) {
+ $this->render($param);
+ if ($i + 1 !== $typeList->count()) {
+ $this->out[] = ',';
+ }
+ }
+ }
}
diff --git a/lib/Token.php b/lib/Token.php
index 5350327b..517ca24d 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -16,7 +16,7 @@ final class Token implements Element
public const T_UNKNOWN = 'UNKNOWN';
public const T_TAG = 'TAG';
public const T_COMMA = 'COMMA';
- public const T_LIST = '[]';
+ public const T_LIST = 'LIST';
public const T_LABEL = 'LABEL';
public const T_WHITESPACE = 'WHITESPACE';
public const T_BRACKET_SQUARE_OPEN = 'BRACKET_SQUARE_OPEN';
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 69bb0326..107c67ee 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -74,6 +74,10 @@ public function current(): Token
*/
public function skip(string $type): self
{
+ if ($this->current()->type() !== $type) {
+ return $this;
+ }
+
while (null !== $current = $this->next()) {
if ($current->type() === $type) {
continue;
diff --git a/tests/Printer/examples/generic1.test b/tests/Printer/examples/generic1.test
new file mode 100644
index 00000000..8b3b802d
--- /dev/null
+++ b/tests/Printer/examples/generic1.test
@@ -0,0 +1,7 @@
+/**
+ * @param Foobar $foobar
+ */
+---
+/**
+ * ParamNode(GenericNode(ClassNode(Foobar),ListNode(ClassNode(Barfoo))),VariableNode($foobar))
+ */
diff --git a/tests/Printer/examples/generic2.test b/tests/Printer/examples/generic2.test
new file mode 100644
index 00000000..94017cf8
--- /dev/null
+++ b/tests/Printer/examples/generic2.test
@@ -0,0 +1,7 @@
+/**
+ * @param Foobar $foobar
+ */
+---
+/**
+ * ParamNode(GenericNode(ClassNode(Foobar),ListNode(ClassNode(Barfoo)),ScalarNode(string)),VariableNode($foobar))
+ */
diff --git a/tests/Printer/examples/generic3.test b/tests/Printer/examples/generic3.test
new file mode 100644
index 00000000..13b5eba0
--- /dev/null
+++ b/tests/Printer/examples/generic3.test
@@ -0,0 +1,7 @@
+/**
+ * @param Foobar,string> $foobar
+ */
+---
+/**
+ * ParamNode(GenericNode(ClassNode(Foobar),GenericNode(ClassNode(Barfoo),ScalarNode(int),ScalarNode(string)),ScalarNode(string)),VariableNode($foobar))
+ */
diff --git a/tests/Printer/examples/generic4.test b/tests/Printer/examples/generic4.test
new file mode 100644
index 00000000..d2e633da
--- /dev/null
+++ b/tests/Printer/examples/generic4.test
@@ -0,0 +1,7 @@
+/**
+ * @param Foobar,string, Baz> $foobar
+ */
+---
+/**
+ * ParamNode(GenericNode(ClassNode(Foobar),GenericNode(ClassNode(Barfoo),ListNode(ScalarNode(int)),ListNode(ScalarNode(int))),ScalarNode(string),ClassNode(Baz)),VariableNode($foobar))
+ */
diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php
index b9661e6c..dd223d7b 100644
--- a/tests/Unit/ParserTest.php
+++ b/tests/Unit/ParserTest.php
@@ -21,7 +21,6 @@ class ParserTest extends TestCase
public function testParse(string $text, Node $expected): void
{
$node = (new Parser())->parse((new Lexer())->lex($text));
- dump($node);
self::assertEquals($expected, $node);
}
@@ -31,25 +30,21 @@ public function testParse(string $text, Node $expected): void
public function provideParse(): Generator
{
yield [
- '/** Hello */',
+ '/** */',
new Docblock([
- new Token(0, Token::T_PHPDOC_OPEN, '/** '),
- new Token(4, Token::T_LABEL, 'Hello'),
- new Token(9, Token::T_WHITESPACE, ' '),
- new Token(10, Token::T_PHPDOC_CLOSE, '*/'),
+ new Token(0, Token::T_PHPDOC_OPEN, '/**'),
+ new Token(3, Token::T_WHITESPACE, ' '),
+ new Token(4, Token::T_PHPDOC_CLOSE, '*/'),
])
];
-
yield [
- '/** @param Foobar $foobar */',
+ '/** Hello */',
new Docblock([
- new Token(0, Token::T_PHPDOC_OPEN, '/** '),
- new ParamNode(
- new ClassNode(new Token(11, Token::T_LABEL, 'Foobar')),
- new VariableNode(new Token(18, Token::T_VARIABLE, '$foobar'))
- ),
- new Token(25, Token::T_WHITESPACE, ' '),
- new Token(26, Token::T_PHPDOC_CLOSE, '*/'),
+ new Token(0, Token::T_PHPDOC_OPEN, '/**'),
+ new Token(3, Token::T_WHITESPACE, ' '),
+ new Token(4, Token::T_LABEL, 'Hello'),
+ new Token(9, Token::T_WHITESPACE, ' '),
+ new Token(10, Token::T_PHPDOC_CLOSE, '*/'),
])
];
}
From a36dae509b2327188cbf2fc7bf393ac99c8818b8 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Fri, 22 Jan 2021 21:04:59 +0000
Subject: [PATCH 10/63] Fixed tests
---
lib/Lexer.php | 8 ++++++--
tests/Unit/LexerTest.php | 10 ++++++----
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index f23f91cc..0c4493ff 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -8,9 +8,9 @@ class Lexer
* @var string[]
*/
private $patterns = [
- '^/\*+\s*\*?', // start tag
+ '^/\*+', // start tag
'\*/', // close tag
- '\s*\*', // border
+ '\*', // leading tag
'\[\]', //tag
'@\w+', //tag
'\s+', // whitespace
@@ -71,6 +71,10 @@ private function resolveType(string $value, ?string $prevValue): string
return Token::T_PHPDOC_LEADING;
}
+ if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
+ return Token::T_PHPDOC_LEADING;
+ }
+
if (0 === strpos($value, '$')) {
return Token::T_VARIABLE;
}
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 624b6e5f..f086a376 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -41,7 +41,9 @@ public function provideLex(): Generator
EOT
,[
- [Token::T_PHPDOC_OPEN, "/**\n *"],
+ [Token::T_PHPDOC_OPEN, "/**"],
+ [Token::T_WHITESPACE, "\n "],
+ [Token::T_PHPDOC_LEADING, "*"],
[Token::T_WHITESPACE, " "],
[Token::T_LABEL, "Hello"],
[Token::T_WHITESPACE, " "],
@@ -49,7 +51,8 @@ public function provideLex(): Generator
[Token::T_WHITESPACE, " "],
[Token::T_LABEL, "is"],
[Token::T_WHITESPACE, "\n "],
- [Token::T_PHPDOC_LEADING, "* "],
+ [Token::T_PHPDOC_LEADING, "*"],
+ [Token::T_WHITESPACE, " "],
[Token::T_LABEL, "Multi"],
[Token::T_WHITESPACE, "\n "],
[Token::T_PHPDOC_CLOSE, "*/"],
@@ -66,8 +69,7 @@ public function provideLex(): Generator
'Foobar[]',
[
[Token::T_LABEL, "Foobar"],
- [Token::T_BRACKET_SQUARE_OPEN, "["],
- [Token::T_BRACKET_SQUARE_CLOSE, "]"],
+ [Token::T_LIST, "[]"],
]
];
yield [
From a507778144a44bee52e94cb7901c15db3a69d731 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Fri, 22 Jan 2021 23:59:55 +0000
Subject: [PATCH 11/63] Refactored
---
lib/Ast/VarNode.php | 32 +++++++++
lib/Parser.php | 102 ++++++++++++++++++-----------
lib/Printer/TestPrinter.php | 17 +++++
lib/Tokens.php | 72 +++++++++++++-------
tests/Printer/PrinterTest.php | 5 +-
tests/Printer/examples/param4.test | 7 ++
tests/Printer/examples/var1.test | 7 ++
7 files changed, 176 insertions(+), 66 deletions(-)
create mode 100644 lib/Ast/VarNode.php
create mode 100644 tests/Printer/examples/param4.test
create mode 100644 tests/Printer/examples/var1.test
diff --git a/lib/Ast/VarNode.php b/lib/Ast/VarNode.php
new file mode 100644
index 00000000..aa25ee69
--- /dev/null
+++ b/lib/Ast/VarNode.php
@@ -0,0 +1,32 @@
+type = $type;
+ $this->variable = $variable;
+ }
+
+ public function type(): ?TypeNode
+ {
+ return $this->type;
+ }
+
+ public function variable(): ?VariableNode
+ {
+ return $this->variable;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index 4f751df6..5c383807 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -13,85 +13,105 @@
use Phpactor\Docblock\Ast\Type\ListNode;
use Phpactor\Docblock\Ast\Type\ScalarNode;
use Phpactor\Docblock\Ast\UnknownTag;
+use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
final class Parser
{
+ /**
+ * @var Tokens
+ */
+ private $tokens;
+
private const SCALAR_TYPES = [
'int', 'float', 'bool', 'string'
];
public function parse(Tokens $tokens): Node
{
- $children = [$tokens->current()];
+ $this->tokens = $tokens;
+ $children = [];
- while ($token = $tokens->next()) {
- assert($token instanceof Token);
- if ($token->type() === Token::T_TAG) {
- $children[] = $this->parseTag($token, $tokens);
+ while ($tokens->hasAnother()) {
+ if ($tokens->current()->type() === Token::T_TAG) {
+ $children[] = $this->parseTag();
continue;
}
- $children[] = $token;
+ $children[] = $tokens->chomp();
}
return new Docblock($children);
}
- private function parseTag(Token $token, Tokens $tokens): TagNode
+ private function parseTag(): TagNode
{
+ $token = $this->tokens->current();
+
if ($token->value() === '@param') {
- return $this->parseParam($tokens);
+ return $this->parseParam();
+ }
+
+ if ($token->value() === '@var') {
+ return $this->parseVar();
}
return new UnknownTag($token);
}
- private function parseParam(Tokens $tokens): ParamNode
+ private function parseParam(): ParamNode
{
- $tokens->next();
- $type = $this->parseType($tokens);
- $variable = $this->parseVariable($tokens->skip(Token::T_WHITESPACE));
+ $type = $variable = null;
+ $this->tokens->chomp(Token::T_TAG);
+
+ if ($this->tokens->ifNextIs(Token::T_LABEL)) {
+ $type = $this->parseType();
+ }
+ if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
+ $variable = $this->parseVariable();
+ }
return new ParamNode($type, $variable);
}
- private function parseType(Tokens $tokens): ?TypeNode
+ private function parseVar(): VarNode
{
- $tokens->skip(Token::T_WHITESPACE);
- $isList = false;
-
- if (!$tokens->isType(Token::T_LABEL)) {
- return null;
+ $this->tokens->chomp(Token::T_TAG);
+ $type = null;
+ if ($this->tokens->if(Token::T_LABEL)) {
+ $type = $this->parseType();
}
+ return new VarNode($type, null);
+ }
- $type = $tokens->current();
+ private function parseType(): ?TypeNode
+ {
+ $type = $this->tokens->chomp(Token::T_LABEL);
+ $isList = false;
- if ($tokens->peek()->type() === Token::T_LIST) {
- $tokens->next();
- $tokens->next();
- return new ListNode($this->createTypeFromToken($type), $tokens->current());
+ if ($this->tokens->current()->type() === Token::T_LIST) {
+ $list = $this->tokens->chomp();
+ return new ListNode($this->createTypeFromToken($type), $list);
}
- if ($tokens->peek()->type() === Token::T_BRACKET_ANGLE_OPEN) {
- $open = $tokens->next();
- $tokens->next();
- $typeList = $this->parseTypeList($tokens);
+ if ($this->tokens->current()->type() === Token::T_BRACKET_ANGLE_OPEN) {
+ $open = $this->tokens->chomp();
+ if ($this->tokens->if(Token::T_LABEL)) {
+ $typeList = $this->parseTypeList();
+ }
- if (!$tokens->isType(Token::T_BRACKET_ANGLE_CLOSE)) {
+ if ($this->tokens->current()->type() !== Token::T_BRACKET_ANGLE_CLOSE) {
return null;
}
- $tokens->next();
return new GenericNode(
$open,
$this->createTypeFromToken($type),
$typeList,
- $tokens->current()
+ $this->tokens->chomp()
);
}
- $tokens->next();
return $this->createTypeFromToken($type);
}
@@ -104,27 +124,31 @@ private function createTypeFromToken(Token $type): TypeNode
return new ClassNode($type);
}
- private function parseVariable(Tokens $tokens): ?VariableNode
+ private function parseVariable(): ?VariableNode
{
- if (!$tokens->isType(Token::T_VARIABLE)) {
+ if ($this->tokens->current()->type() !== Token::T_VARIABLE) {
return null;
}
- $name = $tokens->current();
+ $name = $this->tokens->chomp(Token::T_VARIABLE);
return new VariableNode($name);
}
- private function parseTypeList(Tokens $tokens): TypeList
+ private function parseTypeList(): TypeList
{
$types = [];
while (true) {
- $types[] = $this->parseType($tokens);
- if ($tokens->current()->type() !== Token::T_COMMA) {
- break;
+ if ($this->tokens->if(Token::T_LABEL)) {
+ $types[] = $this->parseType();
+ }
+ if ($this->tokens->if(Token::T_COMMA)) {
+ $this->tokens->chomp();
+ continue;
}
- $tokens->next();
+ break;
}
+ dump($types);
return new TypeList($types);
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 0edeabe0..8ac4472c 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -11,6 +11,7 @@
use Phpactor\Docblock\Ast\ParamNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
use Phpactor\Docblock\Printer;
use Phpactor\Docblock\Token;
@@ -54,6 +55,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof VarNode) {
+ $this->renderVar($node);
+ return;
+ }
+
if ($node instanceof ListNode) {
$this->renderListNode($node);
return;
@@ -96,6 +102,17 @@ private function renderParam(ParamNode $node): void
$this->out[] = ')';
}
+ private function renderVar(VarNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ if ($node->variable()) {
+ $this->out[] = ',';
+ $this->render($node->variable());
+ }
+ $this->out[] = ')';
+ }
+
private function renderTypeNode(TypeNode $node): void
{
$this->out[] = $node->shortName() . '(';
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 107c67ee..cc6e2049 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -39,58 +39,80 @@ public function getIterator(): ArrayIterator
return new ArrayIterator($this->tokens);
}
- public function peek(): ?Token
+ public function hasAnother(): bool
{
- if (!isset($this->tokens[$this->position + 1])) {
- return null;
- }
-
- return $this->tokens[$this->position + 1];
+ return isset($this->tokens[$this->position + 1]);
}
- public function next(): ?Token
+ /**
+ * Return the current token and move the position ahead.
+ */
+ public function chomp(?string $type = null): ?Token
{
- if (!isset($this->tokens[$this->position + 1])) {
+ if (!isset($this->tokens[$this->position])) {
return null;
}
- return $this->tokens[++$this->position];
+ $token = $this->tokens[$this->position++];
+
+ if (null !== $type && $token->type() !== $type) {
+ throw new RuntimeException(sprintf(
+ 'Expected type "%s" at position "%s": "%s"',
+ $type, $this->position,
+ implode('', array_map(function (Token $token) {
+ return $token->value();
+ }, $this->tokens))
+ ));
+ }
+
+ return $token;
}
public function current(): Token
{
if (!isset($this->tokens[$this->position])) {
throw new RuntimeException(sprintf(
- 'No token at current position "%s"',
- $this->position
+ 'No token at position "%s"', $this->position
));
}
return $this->tokens[$this->position];
}
- /**
- * Skip until all tokens of the given type
- */
- public function skip(string $type): self
+ public function ifNextIs(string $type): bool
+ {
+ if ($this->next()->type() === $type) {
+ $this->position++;
+ return true;
+ }
+
+ return false;
+ }
+
+ public function if(string $type): bool
{
- if ($this->current()->type() !== $type) {
- return $this;
+ if ($this->current()->type() === $type) {
+ return true;
}
- while (null !== $current = $this->next()) {
- if ($current->type() === $type) {
- continue;
- }
+ if ($this->current()->type() !== Token::T_WHITESPACE) {
+ return false;
+ }
- return $this;
+ if ($this->next()->type() === $type) {
+ $this->position++;
+ return true;
}
- return $this;
+ return false;
}
- public function isType(string $type): bool
+ public function next(): ?Token
{
- return $this->current()->type() === $type;
+ if (!isset($this->tokens[$this->position + 1])) {
+ return null;
+ }
+
+ return $this->tokens[$this->position + 1];
}
}
diff --git a/tests/Printer/PrinterTest.php b/tests/Printer/PrinterTest.php
index 3d0ca500..6ae9bbbd 100644
--- a/tests/Printer/PrinterTest.php
+++ b/tests/Printer/PrinterTest.php
@@ -21,7 +21,8 @@ public function testPrint(string $path): void
$parts = explode('---', $contents);
- $node = (new Parser())->parse((new Lexer())->lex($parts[0]));
+ $tokens = (new Lexer())->lex($parts[0]);
+ $node = (new Parser())->parse($tokens);
$rendered = (new TestPrinter())->print($node);
/**
@@ -33,7 +34,7 @@ public function testPrint(string $path): void
return;
}
- self::assertEquals(ltrim($parts[1]), $rendered);
+ self::assertEquals(trim($parts[1]), $rendered);
}
/**
diff --git a/tests/Printer/examples/param4.test b/tests/Printer/examples/param4.test
new file mode 100644
index 00000000..b189e757
--- /dev/null
+++ b/tests/Printer/examples/param4.test
@@ -0,0 +1,7 @@
+/**
+ * @param bool
+ */
+---
+/**
+ * ParamNode(ScalarNode(bool),#missing#)
+ */
diff --git a/tests/Printer/examples/var1.test b/tests/Printer/examples/var1.test
new file mode 100644
index 00000000..5926e58d
--- /dev/null
+++ b/tests/Printer/examples/var1.test
@@ -0,0 +1,7 @@
+/**
+ * @var string[]
+ */
+---
+/**
+ * VarNode(ListNode(ScalarNode(string)))
+ */
From 28459e8faf7302677e81ffc2be489d8e44ede9f9 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 00:05:38 +0000
Subject: [PATCH 12/63] Remove dump
---
lib/Parser.php | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/lib/Parser.php b/lib/Parser.php
index 5c383807..ed4ee54c 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -76,12 +76,15 @@ private function parseParam(): ParamNode
private function parseVar(): VarNode
{
$this->tokens->chomp(Token::T_TAG);
- $type = null;
+ $type = $variable = null;
if ($this->tokens->if(Token::T_LABEL)) {
$type = $this->parseType();
}
+ if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
+ $variable = $this->parseVariable();
+ }
- return new VarNode($type, null);
+ return new VarNode($type, $variable);
}
private function parseType(): ?TypeNode
@@ -148,7 +151,6 @@ private function parseTypeList(): TypeList
}
break;
}
- dump($types);
return new TypeList($types);
}
From 38e0a6e387aeae6977801ccc05513d89129281a3 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 00:18:19 +0000
Subject: [PATCH 13/63] Add bench
---
composer.json | 1 +
tests/Benchmark/ParserBench.php | 26 ++++++++++++++++++++++++++
tests/Printer/examples/var2.test | 3 +++
3 files changed, 30 insertions(+)
create mode 100644 tests/Benchmark/ParserBench.php
create mode 100644 tests/Printer/examples/var2.test
diff --git a/composer.json b/composer.json
index 7405546d..39f03878 100644
--- a/composer.json
+++ b/composer.json
@@ -13,6 +13,7 @@
"php": "^7.3"
},
"require-dev": {
+ "phpbench/phpbench": "^1.0",
"ergebnis/composer-normalize": "^2.0",
"friendsofphp/php-cs-fixer": "^2.17",
"phpstan/phpstan": "~0.12.0",
diff --git a/tests/Benchmark/ParserBench.php b/tests/Benchmark/ParserBench.php
new file mode 100644
index 00000000..11ac49a0
--- /dev/null
+++ b/tests/Benchmark/ParserBench.php
@@ -0,0 +1,26 @@
+parse((new Lexer())->lex($doc));
+ }
+}
diff --git a/tests/Printer/examples/var2.test b/tests/Printer/examples/var2.test
new file mode 100644
index 00000000..4c21c5b4
--- /dev/null
+++ b/tests/Printer/examples/var2.test
@@ -0,0 +1,3 @@
+/** @var string $foobar */
+---
+/** VarNode(ScalarNode(string),VariableNode($foobar)) */
From 97f060d5cafb13edd1dc9032b6841645a681b5d7 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 00:31:32 +0000
Subject: [PATCH 14/63] Use dev-master
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index 39f03878..420e6c04 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,7 @@
"php": "^7.3"
},
"require-dev": {
- "phpbench/phpbench": "^1.0",
+ "phpbench/phpbench": "dev-master",
"ergebnis/composer-normalize": "^2.0",
"friendsofphp/php-cs-fixer": "^2.17",
"phpstan/phpstan": "~0.12.0",
From f54f6467a4fca86c6edf62f1dfa664018a53f24d Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 10:55:51 +0000
Subject: [PATCH 15/63] Experiment with preg_match_all lexer
---
lib/Lexer.php | 54 +++++++++++++++++++++++++--------------------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 0c4493ff..b4787054 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -8,16 +8,23 @@ class Lexer
* @var string[]
*/
private $patterns = [
- '^/\*+', // start tag
- '\*/', // close tag
- '\*', // leading tag
- '\[\]', //tag
- '@\w+', //tag
- '\s+', // whitespace
- ',', // comma
- '\{', '\}', '\[', '\]', '<', '>', // brackets
- '\$[a-zA-Z0-9_\x80-\xff]+', // variable
- '[^a-zA-Z0-9_\x80-\xff]+', // label
+ '^/\*+' => Token::T_PHPDOC_OPEN,
+ '\*/' => Token::T_PHPDOC_CLOSE,
+ '\*' => Token::T_PHPDOC_LEADING,
+ '\[\]' => Token::T_LIST,
+ '@\w+' => Token::T_TAG,
+ '\s+' => Token::T_WHITESPACE,
+ ',' => Token::T_COMMA,
+ '\{' => Token::T_BRACKET_CURLY_OPEN,
+ '\}' => Token::T_BRACKET_CURLY_CLOSE,
+ '\[' => Token::T_BRACKET_SQUARE_OPEN,
+ '\]' => Token::T_BRACKET_SQUARE_CLOSE,
+ '<' => Token::T_BRACKET_ANGLE_OPEN,
+ '>' => Token::T_BRACKET_ANGLE_CLOSE,
+ // brackets'\$[a-zA-Z0-9_\x80-\xff]+',
+ '\$[a-zA-Z0-9_\x80-\xff]+' => Token::T_VARIABLE,
+ '[A-Za-zA-Z0-9_\x80-\xff]+' => Token::T_LABEL,
+ // label
];
/**
@@ -31,27 +38,20 @@ public function lex(string $docblock): Tokens
{
$pattern = sprintf(
'{(%s)|%s}',
- implode(')|(', $this->patterns),
+ implode(')|(', array_map(function (string $pattern, string $name) {
+ return sprintf('?P<%s>%s', $name, $pattern);
+ }, array_keys($this->patterns), array_values($this->patterns))),
implode('|', $this->ignorePatterns)
);
- $chunks = (array)preg_split(
- $pattern,
- $docblock,
- null,
- PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE
- );
+ preg_match_all($pattern, $docblock, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE|PREG_UNMATCHED_AS_NULL);
$tokens = [];
- $prevChunk = [null,null];
- foreach ($chunks as $chunk) {
- [ $value, $offset ] = $chunk;
- [ $prevValue, $_ ] = $prevChunk;
- $tokens[] = new Token(
- $offset,
- $this->resolveType($value, $prevValue),
- $value
- );
- $prevChunk = $chunk;
+ foreach ($matches as $groups) {
+ foreach ($groups as $name => $group) {
+ if (is_string($name) && $group[1] >= 0) {
+ $tokens[] = new Token($group[1], $name, $group[0]);
+ }
+ }
}
return new Tokens($tokens);
From 8e8ea0bc4e4cdd05a3bc085197a55f1ea7d6e130 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 10:57:09 +0000
Subject: [PATCH 16/63] Added phpstan comparison benchmark
---
composer.json | 3 +-
...rBench.php => AbstractParserBenchCase.php} | 9 +++--
tests/Benchmark/PhpactorParserBench.php | 29 ++++++++++++++++
tests/Benchmark/PhpstanParserBench.php | 34 +++++++++++++++++++
4 files changed, 71 insertions(+), 4 deletions(-)
rename tests/Benchmark/{ParserBench.php => AbstractParserBenchCase.php} (61%)
create mode 100644 tests/Benchmark/PhpactorParserBench.php
create mode 100644 tests/Benchmark/PhpstanParserBench.php
diff --git a/composer.json b/composer.json
index 420e6c04..93f4220e 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,8 @@
"phpstan/phpstan": "~0.12.0",
"phpunit/phpunit": "^9.0",
"phpspec/prophecy-phpunit": "^2.0",
- "symfony/var-dumper": "^5.2"
+ "symfony/var-dumper": "^5.2",
+ "phpstan/phpdoc-parser": "^0.4.10"
},
"extra": {
"branch-alias": {
diff --git a/tests/Benchmark/ParserBench.php b/tests/Benchmark/AbstractParserBenchCase.php
similarity index 61%
rename from tests/Benchmark/ParserBench.php
rename to tests/Benchmark/AbstractParserBenchCase.php
index 11ac49a0..3a3c4a14 100644
--- a/tests/Benchmark/ParserBench.php
+++ b/tests/Benchmark/AbstractParserBenchCase.php
@@ -8,8 +8,9 @@
/**
* @Iterations(33)
* @Revs(50)
+ * @BeforeMethods({"setUp"})
*/
-class ParserBench
+abstract class AbstractParserBenchCase
{
public function benchParse(): void
{
@@ -20,7 +21,9 @@ public function benchParse(): void
* @param string $baz
*/
EOT;
- $parser = new Parser();
- $parser->parse((new Lexer())->lex($doc));
+ $this->parse($doc);
}
+
+ abstract public function setUp(): void;
+ abstract public function parse(string $doc): void;
}
diff --git a/tests/Benchmark/PhpactorParserBench.php b/tests/Benchmark/PhpactorParserBench.php
new file mode 100644
index 00000000..94a7b4f8
--- /dev/null
+++ b/tests/Benchmark/PhpactorParserBench.php
@@ -0,0 +1,29 @@
+parser = new Parser();
+ $this->lexer = new Lexer();
+ }
+ public function parse(string $doc): void
+ {
+ $this->parser->parse($this->lexer->lex($doc));
+ }
+}
diff --git a/tests/Benchmark/PhpstanParserBench.php b/tests/Benchmark/PhpstanParserBench.php
new file mode 100644
index 00000000..dcf0884a
--- /dev/null
+++ b/tests/Benchmark/PhpstanParserBench.php
@@ -0,0 +1,34 @@
+parser = new PhpDocParser(new TypeParser(), new ConstExprParser());
+ $this->lexer = new Lexer();
+ }
+
+ public function parse(string $doc): void
+ {
+ $tokens = new TokenIterator($this->lexer->tokenize($doc));
+ $this->parser->parse($tokens);
+ }
+}
From 339ce227b8c8e2d3cf9c5ef539ab868cfafa28c4 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 10:57:24 +0000
Subject: [PATCH 17/63] Revert "Experiment with preg_match_all lexer"
This reverts commit f54f6467a4fca86c6edf62f1dfa664018a53f24d.
---
lib/Lexer.php | 54 +++++++++++++++++++++++++--------------------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index b4787054..0c4493ff 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -8,23 +8,16 @@ class Lexer
* @var string[]
*/
private $patterns = [
- '^/\*+' => Token::T_PHPDOC_OPEN,
- '\*/' => Token::T_PHPDOC_CLOSE,
- '\*' => Token::T_PHPDOC_LEADING,
- '\[\]' => Token::T_LIST,
- '@\w+' => Token::T_TAG,
- '\s+' => Token::T_WHITESPACE,
- ',' => Token::T_COMMA,
- '\{' => Token::T_BRACKET_CURLY_OPEN,
- '\}' => Token::T_BRACKET_CURLY_CLOSE,
- '\[' => Token::T_BRACKET_SQUARE_OPEN,
- '\]' => Token::T_BRACKET_SQUARE_CLOSE,
- '<' => Token::T_BRACKET_ANGLE_OPEN,
- '>' => Token::T_BRACKET_ANGLE_CLOSE,
- // brackets'\$[a-zA-Z0-9_\x80-\xff]+',
- '\$[a-zA-Z0-9_\x80-\xff]+' => Token::T_VARIABLE,
- '[A-Za-zA-Z0-9_\x80-\xff]+' => Token::T_LABEL,
- // label
+ '^/\*+', // start tag
+ '\*/', // close tag
+ '\*', // leading tag
+ '\[\]', //tag
+ '@\w+', //tag
+ '\s+', // whitespace
+ ',', // comma
+ '\{', '\}', '\[', '\]', '<', '>', // brackets
+ '\$[a-zA-Z0-9_\x80-\xff]+', // variable
+ '[^a-zA-Z0-9_\x80-\xff]+', // label
];
/**
@@ -38,20 +31,27 @@ public function lex(string $docblock): Tokens
{
$pattern = sprintf(
'{(%s)|%s}',
- implode(')|(', array_map(function (string $pattern, string $name) {
- return sprintf('?P<%s>%s', $name, $pattern);
- }, array_keys($this->patterns), array_values($this->patterns))),
+ implode(')|(', $this->patterns),
implode('|', $this->ignorePatterns)
);
+ $chunks = (array)preg_split(
+ $pattern,
+ $docblock,
+ null,
+ PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE
+ );
- preg_match_all($pattern, $docblock, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE|PREG_UNMATCHED_AS_NULL);
$tokens = [];
- foreach ($matches as $groups) {
- foreach ($groups as $name => $group) {
- if (is_string($name) && $group[1] >= 0) {
- $tokens[] = new Token($group[1], $name, $group[0]);
- }
- }
+ $prevChunk = [null,null];
+ foreach ($chunks as $chunk) {
+ [ $value, $offset ] = $chunk;
+ [ $prevValue, $_ ] = $prevChunk;
+ $tokens[] = new Token(
+ $offset,
+ $this->resolveType($value, $prevValue),
+ $value
+ );
+ $prevChunk = $chunk;
}
return new Tokens($tokens);
From 9cf4db7001d4358a07bd4d820412644e61535a28 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 11:02:34 +0000
Subject: [PATCH 18/63] Fixed parser test
---
lib/Parser.php | 2 +-
lib/Tokens.php | 5 +++++
tests/Printer/PrinterTest.php | 2 +-
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/lib/Parser.php b/lib/Parser.php
index ed4ee54c..67f9f040 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -32,7 +32,7 @@ public function parse(Tokens $tokens): Node
$this->tokens = $tokens;
$children = [];
- while ($tokens->hasAnother()) {
+ while ($tokens->hasCurrent()) {
if ($tokens->current()->type() === Token::T_TAG) {
$children[] = $this->parseTag();
continue;
diff --git a/lib/Tokens.php b/lib/Tokens.php
index cc6e2049..cbf44cf5 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -39,6 +39,11 @@ public function getIterator(): ArrayIterator
return new ArrayIterator($this->tokens);
}
+ public function hasCurrent(): bool
+ {
+ return isset($this->tokens[$this->position]);
+ }
+
public function hasAnother(): bool
{
return isset($this->tokens[$this->position + 1]);
diff --git a/tests/Printer/PrinterTest.php b/tests/Printer/PrinterTest.php
index 6ae9bbbd..501d5077 100644
--- a/tests/Printer/PrinterTest.php
+++ b/tests/Printer/PrinterTest.php
@@ -34,7 +34,7 @@ public function testPrint(string $path): void
return;
}
- self::assertEquals(trim($parts[1]), $rendered);
+ self::assertEquals(trim($parts[1]), trim($rendered));
}
/**
From c7797e5f127e6334b5b93e1ed6a26b412d5f2a6a Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 11:13:22 +0000
Subject: [PATCH 19/63] Optimisations
---
lib/Lexer.php | 75 +++++++++++++++++++++------------------------------
1 file changed, 30 insertions(+), 45 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 0c4493ff..991d3b76 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -20,6 +20,17 @@ class Lexer
'[^a-zA-Z0-9_\x80-\xff]+', // label
];
+ private $tokenValueMap = [
+ ']' => Token::T_BRACKET_SQUARE_CLOSE,
+ '[' => Token::T_BRACKET_SQUARE_OPEN,
+ '>' => Token::T_BRACKET_ANGLE_CLOSE,
+ '<' => Token::T_BRACKET_ANGLE_OPEN,
+ '{' => Token::T_BRACKET_CURLY_OPEN,
+ '}' => Token::T_BRACKET_CURLY_CLOSE,
+ ',' => Token::T_COMMA,
+ '[]' => Token::T_LIST,
+ ];
+
/**
* @var string[]
*/
@@ -59,27 +70,33 @@ public function lex(string $docblock): Tokens
private function resolveType(string $value, ?string $prevValue): string
{
- if (false !== strpos($value, '/*')) {
- return Token::T_PHPDOC_OPEN;
- }
+ // performance: saves around 7%
+ //
+ // if (false !== strpos($value, '/*')) {
+ // return Token::T_PHPDOC_OPEN;
+ // }
- if (false !== strpos($value, '*/')) {
- return Token::T_PHPDOC_CLOSE;
- }
+ // if (false !== strpos($value, '*/')) {
+ // return Token::T_PHPDOC_CLOSE;
+ // }
- if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
- return Token::T_PHPDOC_LEADING;
- }
+ // if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
+ // return Token::T_PHPDOC_LEADING;
+ // }
+
+ // if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
+ // return Token::T_PHPDOC_LEADING;
+ // }
- if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
- return Token::T_PHPDOC_LEADING;
+ if (array_key_exists($value, $this->tokenValueMap)) {
+ return $this->tokenValueMap[$value];
}
- if (0 === strpos($value, '$')) {
+ if ($value[0] === '$') {
return Token::T_VARIABLE;
}
- if (0 === strpos($value, '@')) {
+ if ($value[0] === '@') {
return Token::T_TAG;
}
@@ -95,38 +112,6 @@ private function resolveType(string $value, ?string $prevValue): string
return Token::T_LABEL;
}
- if ($value === ']') {
- return Token::T_BRACKET_SQUARE_CLOSE;
- }
-
- if ($value === '[') {
- return Token::T_BRACKET_SQUARE_OPEN;
- }
-
- if ($value === '<') {
- return Token::T_BRACKET_ANGLE_OPEN;
- }
-
- if ($value === '>') {
- return Token::T_BRACKET_ANGLE_CLOSE;
- }
-
- if ($value === '{') {
- return Token::T_BRACKET_CURLY_OPEN;
- }
-
- if ($value === '}') {
- return Token::T_BRACKET_CURLY_CLOSE;
- }
-
- if ($value === ',') {
- return Token::T_COMMA;
- }
-
- if ($value === '[]') {
- return Token::T_LIST;
- }
-
return Token::T_UNKNOWN;
}
From 61d6dbf192823f02044aed6166ed376affca01cf Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 11:31:43 +0000
Subject: [PATCH 20/63] Performance optimization
---
lib/Lexer.php | 63 +++++++++++++++++++++------------------
tests/Unit/LexerTest.php | 2 +-
tests/Unit/ParserTest.php | 2 +-
3 files changed, 36 insertions(+), 31 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 991d3b76..6ce16e9f 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -7,7 +7,7 @@ class Lexer
/**
* @var string[]
*/
- private $patterns = [
+ private const PATTERNS = [
'^/\*+', // start tag
'\*/', // close tag
'\*', // leading tag
@@ -20,7 +20,7 @@ class Lexer
'[^a-zA-Z0-9_\x80-\xff]+', // label
];
- private $tokenValueMap = [
+ private const TOKEN_VALUE_MAP = [
']' => Token::T_BRACKET_SQUARE_CLOSE,
'[' => Token::T_BRACKET_SQUARE_OPEN,
'>' => Token::T_BRACKET_ANGLE_CLOSE,
@@ -34,16 +34,26 @@ class Lexer
/**
* @var string[]
*/
- private $ignorePatterns = [
+ private const IGNORE_PATTERNS = [
'\s+'
];
+ /**
+ * @var bool
+ */
+ private $includeSurrounding;
+
+ public function __construct(bool $includeSurrounding = false)
+ {
+ $this->includeSurrounding = $includeSurrounding;
+ }
+
public function lex(string $docblock): Tokens
{
$pattern = sprintf(
'{(%s)|%s}',
- implode(')|(', $this->patterns),
- implode('|', $this->ignorePatterns)
+ implode(')|(', self::PATTERNS),
+ implode('|', self::IGNORE_PATTERNS)
);
$chunks = (array)preg_split(
$pattern,
@@ -53,13 +63,12 @@ public function lex(string $docblock): Tokens
);
$tokens = [];
- $prevChunk = [null,null];
+ $prevChunk = null;
foreach ($chunks as $chunk) {
[ $value, $offset ] = $chunk;
- [ $prevValue, $_ ] = $prevChunk;
$tokens[] = new Token(
$offset,
- $this->resolveType($value, $prevValue),
+ $this->resolveType($value, $prevChunk),
$value
);
$prevChunk = $chunk;
@@ -68,28 +77,24 @@ public function lex(string $docblock): Tokens
return new Tokens($tokens);
}
- private function resolveType(string $value, ?string $prevValue): string
+ private function resolveType(string $value, ?array $prevChunk = null): string
{
- // performance: saves around 7%
- //
- // if (false !== strpos($value, '/*')) {
- // return Token::T_PHPDOC_OPEN;
- // }
-
- // if (false !== strpos($value, '*/')) {
- // return Token::T_PHPDOC_CLOSE;
- // }
-
- // if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
- // return Token::T_PHPDOC_LEADING;
- // }
-
- // if ($prevValue && 0 === strpos($prevValue, "\n") && trim($value) === '*') {
- // return Token::T_PHPDOC_LEADING;
- // }
-
- if (array_key_exists($value, $this->tokenValueMap)) {
- return $this->tokenValueMap[$value];
+ if ($this->includeSurrounding) {
+ if (false !== strpos($value, '/*')) {
+ return Token::T_PHPDOC_OPEN;
+ }
+
+ if (false !== strpos($value, '*/')) {
+ return Token::T_PHPDOC_CLOSE;
+ }
+
+ if ($prevChunk && 0 === strpos($prevChunk[0], "\n") && trim($value) === '*') {
+ return Token::T_PHPDOC_LEADING;
+ }
+ }
+
+ if (array_key_exists($value, self::TOKEN_VALUE_MAP)) {
+ return self::TOKEN_VALUE_MAP[$value];
}
if ($value[0] === '$') {
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index f086a376..056e0af4 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -15,7 +15,7 @@ class LexerTest extends TestCase
*/
public function testLex(string $lex, array $expectedTokens): void
{
- $tokens = (new Lexer())->lex($lex)->toArray();
+ $tokens = (new Lexer(true))->lex($lex)->toArray();
self::assertCount(count($expectedTokens), $tokens, 'Expected number of tokens');
diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php
index dd223d7b..73c501e9 100644
--- a/tests/Unit/ParserTest.php
+++ b/tests/Unit/ParserTest.php
@@ -20,7 +20,7 @@ class ParserTest extends TestCase
*/
public function testParse(string $text, Node $expected): void
{
- $node = (new Parser())->parse((new Lexer())->lex($text));
+ $node = (new Parser())->parse((new Lexer(true))->lex($text));
self::assertEquals($expected, $node);
}
From ba68f54aecbcdbfdce38008fcf66dc27b97133d4 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 11:43:11 +0000
Subject: [PATCH 21/63] Use token properties
---
lib/Parser.php | 16 ++++++++--------
lib/Printer/TestPrinter.php | 2 +-
lib/Token.php | 21 +++------------------
lib/Tokens.php | 10 +++++-----
tests/Unit/LexerTest.php | 2 +-
5 files changed, 18 insertions(+), 33 deletions(-)
diff --git a/lib/Parser.php b/lib/Parser.php
index 67f9f040..b90b9e7b 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -33,7 +33,7 @@ public function parse(Tokens $tokens): Node
$children = [];
while ($tokens->hasCurrent()) {
- if ($tokens->current()->type() === Token::T_TAG) {
+ if ($tokens->current()->type === Token::T_TAG) {
$children[] = $this->parseTag();
continue;
}
@@ -47,11 +47,11 @@ private function parseTag(): TagNode
{
$token = $this->tokens->current();
- if ($token->value() === '@param') {
+ if ($token->value === '@param') {
return $this->parseParam();
}
- if ($token->value() === '@var') {
+ if ($token->value === '@var') {
return $this->parseVar();
}
@@ -92,18 +92,18 @@ private function parseType(): ?TypeNode
$type = $this->tokens->chomp(Token::T_LABEL);
$isList = false;
- if ($this->tokens->current()->type() === Token::T_LIST) {
+ if ($this->tokens->current()->type === Token::T_LIST) {
$list = $this->tokens->chomp();
return new ListNode($this->createTypeFromToken($type), $list);
}
- if ($this->tokens->current()->type() === Token::T_BRACKET_ANGLE_OPEN) {
+ if ($this->tokens->current()->type === Token::T_BRACKET_ANGLE_OPEN) {
$open = $this->tokens->chomp();
if ($this->tokens->if(Token::T_LABEL)) {
$typeList = $this->parseTypeList();
}
- if ($this->tokens->current()->type() !== Token::T_BRACKET_ANGLE_CLOSE) {
+ if ($this->tokens->current()->type !== Token::T_BRACKET_ANGLE_CLOSE) {
return null;
}
@@ -120,7 +120,7 @@ private function parseType(): ?TypeNode
private function createTypeFromToken(Token $type): TypeNode
{
- if (in_array($type->value(), self::SCALAR_TYPES)) {
+ if (in_array($type->value, self::SCALAR_TYPES)) {
return new ScalarNode($type);
}
@@ -129,7 +129,7 @@ private function createTypeFromToken(Token $type): TypeNode
private function parseVariable(): ?VariableNode
{
- if ($this->tokens->current()->type() !== Token::T_VARIABLE) {
+ if ($this->tokens->current()->type !== Token::T_VARIABLE) {
return null;
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 8ac4472c..dccadb6c 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -46,7 +46,7 @@ private function render(?Element $node): void
}
if ($node instanceof Token) {
- $this->out[] = $node->value();
+ $this->out[] = $node->value;
return;
}
diff --git a/lib/Token.php b/lib/Token.php
index 517ca24d..4d97cd66 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -29,17 +29,17 @@ final class Token implements Element
/**
* @var int
*/
- private $byteOffset;
+ public $byteOffset;
/**
* @var string
*/
- private $type;
+ public $type;
/**
* @var string
*/
- private $value;
+ public $value;
public function __construct(int $byteOffset, string $type, string $value)
{
@@ -48,21 +48,6 @@ public function __construct(int $byteOffset, string $type, string $value)
$this->value = $value;
}
- public function byteOffset(): int
- {
- return $this->byteOffset;
- }
-
- public function value(): string
- {
- return $this->value;
- }
-
- public function type(): string
- {
- return $this->type;
- }
-
public function __toString(): string
{
return $this->value;
diff --git a/lib/Tokens.php b/lib/Tokens.php
index cbf44cf5..61d64e42 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -60,7 +60,7 @@ public function chomp(?string $type = null): ?Token
$token = $this->tokens[$this->position++];
- if (null !== $type && $token->type() !== $type) {
+ if (null !== $type && $token->type !== $type) {
throw new RuntimeException(sprintf(
'Expected type "%s" at position "%s": "%s"',
$type, $this->position,
@@ -86,7 +86,7 @@ public function current(): Token
public function ifNextIs(string $type): bool
{
- if ($this->next()->type() === $type) {
+ if ($this->next()->type === $type) {
$this->position++;
return true;
}
@@ -96,15 +96,15 @@ public function ifNextIs(string $type): bool
public function if(string $type): bool
{
- if ($this->current()->type() === $type) {
+ if ($this->current()->type === $type) {
return true;
}
- if ($this->current()->type() !== Token::T_WHITESPACE) {
+ if ($this->current()->type !== Token::T_WHITESPACE) {
return false;
}
- if ($this->next()->type() === $type) {
+ if ($this->next()->type === $type) {
$this->position++;
return true;
}
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 056e0af4..1655bef4 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -21,7 +21,7 @@ public function testLex(string $lex, array $expectedTokens): void
foreach ($tokens as $index => $token) {
[$type, $value] = $expectedTokens[$index];
- $expectedToken = new Token($token->byteOffset(), $type, $value);
+ $expectedToken = new Token($token->byteOffset, $type, $value);
self::assertEquals($expectedToken, $token);
}
}
From a7e599b56cfff399040b03eb344fe759cbdf9731 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 11:53:46 +0000
Subject: [PATCH 22/63] Access current as property
---
lib/Parser.php | 12 ++++++------
lib/Tokens.php | 30 ++++++++++++++----------------
2 files changed, 20 insertions(+), 22 deletions(-)
diff --git a/lib/Parser.php b/lib/Parser.php
index b90b9e7b..cf9bb035 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -33,7 +33,7 @@ public function parse(Tokens $tokens): Node
$children = [];
while ($tokens->hasCurrent()) {
- if ($tokens->current()->type === Token::T_TAG) {
+ if ($tokens->current->type === Token::T_TAG) {
$children[] = $this->parseTag();
continue;
}
@@ -45,7 +45,7 @@ public function parse(Tokens $tokens): Node
private function parseTag(): TagNode
{
- $token = $this->tokens->current();
+ $token = $this->tokens->current;
if ($token->value === '@param') {
return $this->parseParam();
@@ -92,18 +92,18 @@ private function parseType(): ?TypeNode
$type = $this->tokens->chomp(Token::T_LABEL);
$isList = false;
- if ($this->tokens->current()->type === Token::T_LIST) {
+ if ($this->tokens->current->type === Token::T_LIST) {
$list = $this->tokens->chomp();
return new ListNode($this->createTypeFromToken($type), $list);
}
- if ($this->tokens->current()->type === Token::T_BRACKET_ANGLE_OPEN) {
+ if ($this->tokens->current->type === Token::T_BRACKET_ANGLE_OPEN) {
$open = $this->tokens->chomp();
if ($this->tokens->if(Token::T_LABEL)) {
$typeList = $this->parseTypeList();
}
- if ($this->tokens->current()->type !== Token::T_BRACKET_ANGLE_CLOSE) {
+ if ($this->tokens->current->type !== Token::T_BRACKET_ANGLE_CLOSE) {
return null;
}
@@ -129,7 +129,7 @@ private function createTypeFromToken(Token $type): TypeNode
private function parseVariable(): ?VariableNode
{
- if ($this->tokens->current()->type !== Token::T_VARIABLE) {
+ if ($this->tokens->current->type !== Token::T_VARIABLE) {
return null;
}
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 61d64e42..24e47ec0 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -13,6 +13,11 @@ final class Tokens implements IteratorAggregate
*/
private $tokens;
+ /**
+ * @var ?Token
+ */
+ public $current;
+
private $position = 0;
/**
@@ -21,6 +26,9 @@ final class Tokens implements IteratorAggregate
public function __construct(array $tokens)
{
$this->tokens = $tokens;
+ if (count($tokens)) {
+ $this->current = $tokens[$this->position];
+ }
}
/**
@@ -59,13 +67,14 @@ public function chomp(?string $type = null): ?Token
}
$token = $this->tokens[$this->position++];
+ $this->current = @$this->tokens[$this->position];
if (null !== $type && $token->type !== $type) {
throw new RuntimeException(sprintf(
'Expected type "%s" at position "%s": "%s"',
$type, $this->position,
implode('', array_map(function (Token $token) {
- return $token->value();
+ return $token->value;
}, $this->tokens))
));
}
@@ -73,21 +82,10 @@ public function chomp(?string $type = null): ?Token
return $token;
}
- public function current(): Token
- {
- if (!isset($this->tokens[$this->position])) {
- throw new RuntimeException(sprintf(
- 'No token at position "%s"', $this->position
- ));
- }
-
- return $this->tokens[$this->position];
- }
-
public function ifNextIs(string $type): bool
{
if ($this->next()->type === $type) {
- $this->position++;
+ $this->current = @$this->tokens[++$this->position];
return true;
}
@@ -96,16 +94,16 @@ public function ifNextIs(string $type): bool
public function if(string $type): bool
{
- if ($this->current()->type === $type) {
+ if ($this->current->type === $type) {
return true;
}
- if ($this->current()->type !== Token::T_WHITESPACE) {
+ if ($this->current->type !== Token::T_WHITESPACE) {
return false;
}
if ($this->next()->type === $type) {
- $this->position++;
+ $this->current = $this->tokens[++$this->position];
return true;
}
From 9da77bfe99d7a2114f1ebd1309dd09cf2152f3aa Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 12:40:26 +0000
Subject: [PATCH 23/63] Add type hint
---
lib/Tokens.php | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 24e47ec0..2666a84a 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -18,6 +18,9 @@ final class Tokens implements IteratorAggregate
*/
public $current;
+ /**
+ * @var int
+ */
private $position = 0;
/**
From 3aae081668877f973bef73ffced206b3a4b93dcf Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 12:46:18 +0000
Subject: [PATCH 24/63] Create pattern in constructor
---
lib/Lexer.php | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 6ce16e9f..cadcdbd1 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -43,20 +43,28 @@ class Lexer
*/
private $includeSurrounding;
+ /**
+ * @var string
+ */
+ private $pattern;
+
+ /**
+ * @param bool $includeSurrounding Tokenize DOCBLOCK_ tags, decreases performance.
+ */
public function __construct(bool $includeSurrounding = false)
{
$this->includeSurrounding = $includeSurrounding;
- }
-
- public function lex(string $docblock): Tokens
- {
- $pattern = sprintf(
+ $this->pattern = sprintf(
'{(%s)|%s}',
implode(')|(', self::PATTERNS),
implode('|', self::IGNORE_PATTERNS)
);
+ }
+
+ public function lex(string $docblock): Tokens
+ {
$chunks = (array)preg_split(
- $pattern,
+ $this->pattern,
$docblock,
null,
PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE
From 27cf88b2bce6273e6611594bec550f3f558bbfe5 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 13:17:58 +0000
Subject: [PATCH 25/63] Support param text
---
lib/Ast/ParamNode.php | 13 ++++++++++++-
lib/Ast/TextNode.php | 28 ++++++++++++++++++++++++++++
lib/Parser.php | 29 +++++++++++++++++++++++++++--
lib/Printer/TestPrinter.php | 17 +++++++++++++++++
lib/Tokens.php | 4 ++++
tests/Printer/PrinterTest.php | 2 +-
tests/Printer/examples/param5.test | 7 +++++++
tests/Printer/examples/param6.test | 11 +++++++++++
tests/Printer/examples/var3.test | 3 +++
9 files changed, 110 insertions(+), 4 deletions(-)
create mode 100644 lib/Ast/TextNode.php
create mode 100644 tests/Printer/examples/param5.test
create mode 100644 tests/Printer/examples/param6.test
create mode 100644 tests/Printer/examples/var3.test
diff --git a/lib/Ast/ParamNode.php b/lib/Ast/ParamNode.php
index e2bac69e..0ed5dade 100644
--- a/lib/Ast/ParamNode.php
+++ b/lib/Ast/ParamNode.php
@@ -14,10 +14,16 @@ class ParamNode extends TagNode
*/
private $variable;
- public function __construct(?TypeNode $type, ?VariableNode $variable)
+ /**
+ * @var TextNode|null
+ */
+ private $text;
+
+ public function __construct(?TypeNode $type, ?VariableNode $variable, ?TextNode $text = null)
{
$this->type = $type;
$this->variable = $variable;
+ $this->text = $text;
}
public function type(): ?TypeNode
@@ -29,4 +35,9 @@ public function variable(): ?VariableNode
{
return $this->variable;
}
+
+ public function text(): ?TextNode
+ {
+ return $this->text;
+ }
}
diff --git a/lib/Ast/TextNode.php b/lib/Ast/TextNode.php
new file mode 100644
index 00000000..3855ebe3
--- /dev/null
+++ b/lib/Ast/TextNode.php
@@ -0,0 +1,28 @@
+tokens = $tokens;
+ }
+
+ public function toString(): string
+ {
+ return implode('', array_map(function (Token $token) {
+ return $token->value;
+ }, $this->tokens));
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index cf9bb035..59f313d2 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -3,6 +3,7 @@
namespace Phpactor\Docblock;
use Phpactor\Docblock\Ast\Docblock;
+use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Node;
@@ -60,7 +61,7 @@ private function parseTag(): TagNode
private function parseParam(): ParamNode
{
- $type = $variable = null;
+ $type = $variable = $textNode = null;
$this->tokens->chomp(Token::T_TAG);
if ($this->tokens->ifNextIs(Token::T_LABEL)) {
@@ -70,7 +71,31 @@ private function parseParam(): ParamNode
$variable = $this->parseVariable();
}
- return new ParamNode($type, $variable);
+ $text = [];
+ if (
+ $this->tokens->current->type === Token::T_WHITESPACE &&
+ $this->tokens->next()->type === Token::T_LABEL
+ ) {
+ $this->tokens->chomp();
+ }
+ while ($this->tokens->current) {
+ if ($this->tokens->current->type === Token::T_PHPDOC_CLOSE) {
+ break;
+ }
+ if ($this->tokens->current->type === Token::T_PHPDOC_LEADING) {
+ break;
+ }
+ if (false !== strpos($this->tokens->current->value, "\n")) {
+ break;
+ }
+ $text[] = $this->tokens->chomp();
+ }
+
+ if ($text) {
+ $textNode = new TextNode($text);
+ }
+
+ return new ParamNode($type, $variable, $textNode);
}
private function parseVar(): VarNode
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index dccadb6c..208ee968 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -4,6 +4,7 @@
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Element;
+use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
use Phpactor\Docblock\Ast\Type\ClassNode;
@@ -75,6 +76,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof TextNode) {
+ $this->renderTextNode($node);
+ return;
+ }
+
if ($node instanceof VariableNode) {
$this->renderVariableNode($node);
return;
@@ -99,6 +105,10 @@ private function renderParam(ParamNode $node): void
$this->render($node->type());
$this->out[] = ',';
$this->render($node->variable());
+ if ($node->text()) {
+ $this->out[] = ',';
+ $this->render($node->text());
+ }
$this->out[] = ')';
}
@@ -152,4 +162,11 @@ private function renderTypeList(TypeList $typeList): void
}
}
}
+
+ private function renderTextNode(TextNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->out[] = $node->toString();
+ $this->out[] = ')';
+ }
}
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 2666a84a..c5bba974 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -95,6 +95,10 @@ public function ifNextIs(string $type): bool
return false;
}
+ /**
+ * If the current or next non-whitespace node matches,
+ * advance internal pointer and return true;
+ */
public function if(string $type): bool
{
if ($this->current->type === $type) {
diff --git a/tests/Printer/PrinterTest.php b/tests/Printer/PrinterTest.php
index 501d5077..6652fcd2 100644
--- a/tests/Printer/PrinterTest.php
+++ b/tests/Printer/PrinterTest.php
@@ -21,7 +21,7 @@ public function testPrint(string $path): void
$parts = explode('---', $contents);
- $tokens = (new Lexer())->lex($parts[0]);
+ $tokens = (new Lexer(true))->lex($parts[0]);
$node = (new Parser())->parse($tokens);
$rendered = (new TestPrinter())->print($node);
diff --git a/tests/Printer/examples/param5.test b/tests/Printer/examples/param5.test
new file mode 100644
index 00000000..477e13bd
--- /dev/null
+++ b/tests/Printer/examples/param5.test
@@ -0,0 +1,7 @@
+/**
+ * @param bool $bool This is a boolean
+ */
+---
+/**
+ * ParamNode(ScalarNode(bool),VariableNode($bool),TextNode(This is a boolean))
+ */
diff --git a/tests/Printer/examples/param6.test b/tests/Printer/examples/param6.test
new file mode 100644
index 00000000..5e3e4f75
--- /dev/null
+++ b/tests/Printer/examples/param6.test
@@ -0,0 +1,11 @@
+/**
+ * @param bool $bool This is a boolean
+ * multiline comments not currently supported
+ * @param bool $bool This is a boolean
+ */
+---
+/**
+ * ParamNode(ScalarNode(bool),VariableNode($bool),TextNode(This is a boolean))
+ * multiline comments not currently supported
+ * ParamNode(ScalarNode(bool),VariableNode($bool),TextNode(This is a boolean))
+ */
diff --git a/tests/Printer/examples/var3.test b/tests/Printer/examples/var3.test
new file mode 100644
index 00000000..4c21c5b4
--- /dev/null
+++ b/tests/Printer/examples/var3.test
@@ -0,0 +1,3 @@
+/** @var string $foobar */
+---
+/** VarNode(ScalarNode(string),VariableNode($foobar)) */
From 4670c87bd97c6733e6dc9975f410942295c1fe93 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 13:21:54 +0000
Subject: [PATCH 26/63] Remove skip docblock tokens
---
lib/Lexer.php | 35 ++++++++++++-----------------------
tests/Printer/PrinterTest.php | 2 +-
tests/Unit/LexerTest.php | 2 +-
tests/Unit/ParserTest.php | 2 +-
4 files changed, 15 insertions(+), 26 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index cadcdbd1..1e7297ae 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -2,7 +2,7 @@
namespace Phpactor\Docblock;
-class Lexer
+final class Lexer
{
/**
* @var string[]
@@ -38,22 +38,13 @@ class Lexer
'\s+'
];
- /**
- * @var bool
- */
- private $includeSurrounding;
-
/**
* @var string
*/
private $pattern;
- /**
- * @param bool $includeSurrounding Tokenize DOCBLOCK_ tags, decreases performance.
- */
- public function __construct(bool $includeSurrounding = false)
+ public function __construct()
{
- $this->includeSurrounding = $includeSurrounding;
$this->pattern = sprintf(
'{(%s)|%s}',
implode(')|(', self::PATTERNS),
@@ -87,18 +78,16 @@ public function lex(string $docblock): Tokens
private function resolveType(string $value, ?array $prevChunk = null): string
{
- if ($this->includeSurrounding) {
- if (false !== strpos($value, '/*')) {
- return Token::T_PHPDOC_OPEN;
- }
-
- if (false !== strpos($value, '*/')) {
- return Token::T_PHPDOC_CLOSE;
- }
-
- if ($prevChunk && 0 === strpos($prevChunk[0], "\n") && trim($value) === '*') {
- return Token::T_PHPDOC_LEADING;
- }
+ if (false !== strpos($value, '/*')) {
+ return Token::T_PHPDOC_OPEN;
+ }
+
+ if (false !== strpos($value, '*/')) {
+ return Token::T_PHPDOC_CLOSE;
+ }
+
+ if ($prevChunk && 0 === strpos($prevChunk[0], "\n") && trim($value) === '*') {
+ return Token::T_PHPDOC_LEADING;
}
if (array_key_exists($value, self::TOKEN_VALUE_MAP)) {
diff --git a/tests/Printer/PrinterTest.php b/tests/Printer/PrinterTest.php
index 6652fcd2..501d5077 100644
--- a/tests/Printer/PrinterTest.php
+++ b/tests/Printer/PrinterTest.php
@@ -21,7 +21,7 @@ public function testPrint(string $path): void
$parts = explode('---', $contents);
- $tokens = (new Lexer(true))->lex($parts[0]);
+ $tokens = (new Lexer())->lex($parts[0]);
$node = (new Parser())->parse($tokens);
$rendered = (new TestPrinter())->print($node);
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 1655bef4..d5ff9478 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -15,7 +15,7 @@ class LexerTest extends TestCase
*/
public function testLex(string $lex, array $expectedTokens): void
{
- $tokens = (new Lexer(true))->lex($lex)->toArray();
+ $tokens = (new Lexer())->lex($lex)->toArray();
self::assertCount(count($expectedTokens), $tokens, 'Expected number of tokens');
diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php
index 73c501e9..dd223d7b 100644
--- a/tests/Unit/ParserTest.php
+++ b/tests/Unit/ParserTest.php
@@ -20,7 +20,7 @@ class ParserTest extends TestCase
*/
public function testParse(string $text, Node $expected): void
{
- $node = (new Parser())->parse((new Lexer(true))->lex($text));
+ $node = (new Parser())->parse((new Lexer())->lex($text));
self::assertEquals($expected, $node);
}
From d01f5e990202bd3e6560cfdaf3f45320f1660800 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 13:25:58 +0000
Subject: [PATCH 27/63] Add ignored tests
---
tests/Printer/PrinterTest.php | 5 +++++
tests/Printer/examples/deprecated1.test | 1 +
tests/Printer/examples/method1.test | 1 +
tests/Printer/examples/mixin1.test | 1 +
tests/Printer/examples/nullable1.test | 1 +
tests/Printer/examples/property1.test | 1 +
tests/Printer/examples/return1.test | 1 +
tests/Printer/examples/union1.test | 1 +
8 files changed, 12 insertions(+)
create mode 100644 tests/Printer/examples/deprecated1.test
create mode 100644 tests/Printer/examples/method1.test
create mode 100644 tests/Printer/examples/mixin1.test
create mode 100644 tests/Printer/examples/nullable1.test
create mode 100644 tests/Printer/examples/property1.test
create mode 100644 tests/Printer/examples/return1.test
create mode 100644 tests/Printer/examples/union1.test
diff --git a/tests/Printer/PrinterTest.php b/tests/Printer/PrinterTest.php
index 501d5077..3e2073ac 100644
--- a/tests/Printer/PrinterTest.php
+++ b/tests/Printer/PrinterTest.php
@@ -21,6 +21,11 @@ public function testPrint(string $path): void
$parts = explode('---', $contents);
+ if (empty($parts[0])) {
+ $this->markTestIncomplete(sprintf('No example given for "%s"', $path));
+ return;
+ }
+
$tokens = (new Lexer())->lex($parts[0]);
$node = (new Parser())->parse($tokens);
$rendered = (new TestPrinter())->print($node);
diff --git a/tests/Printer/examples/deprecated1.test b/tests/Printer/examples/deprecated1.test
new file mode 100644
index 00000000..ed97d539
--- /dev/null
+++ b/tests/Printer/examples/deprecated1.test
@@ -0,0 +1 @@
+---
diff --git a/tests/Printer/examples/method1.test b/tests/Printer/examples/method1.test
new file mode 100644
index 00000000..ed97d539
--- /dev/null
+++ b/tests/Printer/examples/method1.test
@@ -0,0 +1 @@
+---
diff --git a/tests/Printer/examples/mixin1.test b/tests/Printer/examples/mixin1.test
new file mode 100644
index 00000000..ed97d539
--- /dev/null
+++ b/tests/Printer/examples/mixin1.test
@@ -0,0 +1 @@
+---
diff --git a/tests/Printer/examples/nullable1.test b/tests/Printer/examples/nullable1.test
new file mode 100644
index 00000000..ed97d539
--- /dev/null
+++ b/tests/Printer/examples/nullable1.test
@@ -0,0 +1 @@
+---
diff --git a/tests/Printer/examples/property1.test b/tests/Printer/examples/property1.test
new file mode 100644
index 00000000..ed97d539
--- /dev/null
+++ b/tests/Printer/examples/property1.test
@@ -0,0 +1 @@
+---
diff --git a/tests/Printer/examples/return1.test b/tests/Printer/examples/return1.test
new file mode 100644
index 00000000..ed97d539
--- /dev/null
+++ b/tests/Printer/examples/return1.test
@@ -0,0 +1 @@
+---
diff --git a/tests/Printer/examples/union1.test b/tests/Printer/examples/union1.test
new file mode 100644
index 00000000..ed97d539
--- /dev/null
+++ b/tests/Printer/examples/union1.test
@@ -0,0 +1 @@
+---
From b9258c921e83589a6802b1f5c7b479f1e89385ca Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 13:30:33 +0000
Subject: [PATCH 28/63] support unknown tag
---
lib/Parser.php | 2 +-
lib/Printer/TestPrinter.php | 6 ++++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/lib/Parser.php b/lib/Parser.php
index 59f313d2..06d48f6c 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -56,7 +56,7 @@ private function parseTag(): TagNode
return $this->parseVar();
}
- return new UnknownTag($token);
+ return new UnknownTag($this->tokens->chomp());
}
private function parseParam(): ParamNode
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 208ee968..243e2d4f 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -12,6 +12,7 @@
use Phpactor\Docblock\Ast\ParamNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
use Phpactor\Docblock\Printer;
@@ -86,6 +87,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof UnknownTag) {
+ $this->out[] = $node->shortName();
+ return;
+ }
+
throw new RuntimeException(sprintf(
'Do not know how to render "%s"',
get_class($node)
From f1b65b4a603922b955975476f98a82e61ee3c399 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 13:37:19 +0000
Subject: [PATCH 29/63] Support deprecated tag
---
lib/Ast/DeprecatedNode.php | 21 ++++++++
lib/Parser.php | 66 +++++++++++++++----------
lib/Printer/TestPrinter.php | 15 ++++++
tests/Printer/examples/deprecated1.test | 6 +++
tests/Printer/examples/deprecated2.test | 7 +++
5 files changed, 90 insertions(+), 25 deletions(-)
create mode 100644 lib/Ast/DeprecatedNode.php
create mode 100644 tests/Printer/examples/deprecated2.test
diff --git a/lib/Ast/DeprecatedNode.php b/lib/Ast/DeprecatedNode.php
new file mode 100644
index 00000000..ea2604fe
--- /dev/null
+++ b/lib/Ast/DeprecatedNode.php
@@ -0,0 +1,21 @@
+text = $text;
+ }
+
+ public function text(): ?TextNode
+ {
+ return $this->text;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index 06d48f6c..ca50a697 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -2,6 +2,7 @@
namespace Phpactor\Docblock;
+use Phpactor\Docblock\Ast\DeprecatedNode;
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
@@ -56,6 +57,10 @@ private function parseTag(): TagNode
return $this->parseVar();
}
+ if ($token->value === '@deprecated') {
+ return $this->parseDeprecated();
+ }
+
return new UnknownTag($this->tokens->chomp());
}
@@ -71,31 +76,7 @@ private function parseParam(): ParamNode
$variable = $this->parseVariable();
}
- $text = [];
- if (
- $this->tokens->current->type === Token::T_WHITESPACE &&
- $this->tokens->next()->type === Token::T_LABEL
- ) {
- $this->tokens->chomp();
- }
- while ($this->tokens->current) {
- if ($this->tokens->current->type === Token::T_PHPDOC_CLOSE) {
- break;
- }
- if ($this->tokens->current->type === Token::T_PHPDOC_LEADING) {
- break;
- }
- if (false !== strpos($this->tokens->current->value, "\n")) {
- break;
- }
- $text[] = $this->tokens->chomp();
- }
-
- if ($text) {
- $textNode = new TextNode($text);
- }
-
- return new ParamNode($type, $variable, $textNode);
+ return new ParamNode($type, $variable, $this->parseText());
}
private function parseVar(): VarNode
@@ -179,4 +160,39 @@ private function parseTypeList(): TypeList
return new TypeList($types);
}
+
+ private function parseDeprecated(): DeprecatedNode
+ {
+ $this->tokens->chomp();
+ return new DeprecatedNode($this->parseText());
+ }
+
+ private function parseText(): ?TextNode
+ {
+ $text = [];
+ if (
+ $this->tokens->current->type === Token::T_WHITESPACE &&
+ $this->tokens->next()->type === Token::T_LABEL
+ ) {
+ $this->tokens->chomp();
+ }
+ while ($this->tokens->current) {
+ if ($this->tokens->current->type === Token::T_PHPDOC_CLOSE) {
+ break;
+ }
+ if ($this->tokens->current->type === Token::T_PHPDOC_LEADING) {
+ break;
+ }
+ if (false !== strpos($this->tokens->current->value, "\n")) {
+ break;
+ }
+ $text[] = $this->tokens->chomp();
+ }
+
+ if ($text) {
+ return new TextNode($text);
+ }
+
+ return null;
+ }
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 243e2d4f..ac87d933 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -2,6 +2,7 @@
namespace Phpactor\Docblock\Printer;
+use Phpactor\Docblock\Ast\DeprecatedNode;
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Element;
use Phpactor\Docblock\Ast\TextNode;
@@ -92,6 +93,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof DeprecatedNode) {
+ $this->renderDeprecated($node);
+ return;
+ }
+
throw new RuntimeException(sprintf(
'Do not know how to render "%s"',
get_class($node)
@@ -175,4 +181,13 @@ private function renderTextNode(TextNode $node): void
$this->out[] = $node->toString();
$this->out[] = ')';
}
+
+ private function renderDeprecated(DeprecatedNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ if ($node->text()) {
+ $this->render($node->text());
+ }
+ $this->out[] = ')';
+ }
}
diff --git a/tests/Printer/examples/deprecated1.test b/tests/Printer/examples/deprecated1.test
index ed97d539..dd9ff0d5 100644
--- a/tests/Printer/examples/deprecated1.test
+++ b/tests/Printer/examples/deprecated1.test
@@ -1 +1,7 @@
+/**
+ * @deprecated
+ */
---
+/**
+ * DeprecatedNode()
+ */
diff --git a/tests/Printer/examples/deprecated2.test b/tests/Printer/examples/deprecated2.test
new file mode 100644
index 00000000..9695d03a
--- /dev/null
+++ b/tests/Printer/examples/deprecated2.test
@@ -0,0 +1,7 @@
+/**
+ * @deprecated This is because
+ */
+---
+/**
+ * DeprecatedNode(TextNode(This is because))
+ */
From c6bdf9eebb02e946562f3676e553fcdad1c19d23 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 13:45:38 +0000
Subject: [PATCH 30/63] Support method node
---
lib/Ast/MethodNode.php | 34 +++++++++++++++++++++++++++++
lib/Parser.php | 19 ++++++++++++++++
lib/Printer/TestPrinter.php | 17 +++++++++++++++
tests/Printer/examples/method1.test | 6 +++++
4 files changed, 76 insertions(+)
create mode 100644 lib/Ast/MethodNode.php
diff --git a/lib/Ast/MethodNode.php b/lib/Ast/MethodNode.php
new file mode 100644
index 00000000..26002a02
--- /dev/null
+++ b/lib/Ast/MethodNode.php
@@ -0,0 +1,34 @@
+type = $type;
+ $this->name = $name;
+ }
+
+ public function name(): ?Token
+ {
+ return $this->name;
+ }
+
+ public function type(): ?TypeNode
+ {
+ return $this->type;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index ca50a697..bc285e85 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -4,6 +4,7 @@
use Phpactor\Docblock\Ast\DeprecatedNode;
use Phpactor\Docblock\Ast\Docblock;
+use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\Type\ClassNode;
@@ -61,6 +62,10 @@ private function parseTag(): TagNode
return $this->parseDeprecated();
}
+ if ($token->value === '@method') {
+ return $this->parseMethod();
+ }
+
return new UnknownTag($this->tokens->chomp());
}
@@ -93,6 +98,20 @@ private function parseVar(): VarNode
return new VarNode($type, $variable);
}
+ private function parseMethod(): MethodNode
+ {
+ $this->tokens->chomp(Token::T_TAG);
+ $type = $name = null;
+ if ($this->tokens->if(Token::T_LABEL)) {
+ $type = $this->parseType();
+ }
+ if ($this->tokens->ifNextIs(Token::T_LABEL)) {
+ $name = $this->tokens->chomp();
+ }
+
+ return new MethodNode($type, $name);
+ }
+
private function parseType(): ?TypeNode
{
$type = $this->tokens->chomp(Token::T_LABEL);
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index ac87d933..7ac21f9d 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -5,6 +5,7 @@
use Phpactor\Docblock\Ast\DeprecatedNode;
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Element;
+use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
@@ -98,6 +99,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof MethodNode) {
+ $this->renderMethod($node);
+ return;
+ }
+
throw new RuntimeException(sprintf(
'Do not know how to render "%s"',
get_class($node)
@@ -190,4 +196,15 @@ private function renderDeprecated(DeprecatedNode $node): void
}
$this->out[] = ')';
}
+
+ private function renderMethod(MethodNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ if ($node->name()) {
+ $this->out[] = ',';
+ $this->render($node->name());
+ }
+ $this->out[] = ')';
+ }
}
diff --git a/tests/Printer/examples/method1.test b/tests/Printer/examples/method1.test
index ed97d539..63ba6fae 100644
--- a/tests/Printer/examples/method1.test
+++ b/tests/Printer/examples/method1.test
@@ -1 +1,7 @@
+/**
+ * @method Foobar foobar()
+ */
---
+/**
+ * MethodNode(ClassNode(Foobar),foobar)()
+ */
From 4b028829993124ffe1ab5b2e17049e078d1ca3ba Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 14:04:50 +0000
Subject: [PATCH 31/63] Support nullable
---
lib/Ast/MixinNode.php | 23 ++++++++++++++++
lib/Ast/Type/NullableNode.php | 35 ++++++++++++++++++++++++
lib/Lexer.php | 2 ++
lib/Parser.php | 38 ++++++++++++++++++++++++---
lib/Printer/TestPrinter.php | 26 ++++++++++++++++++
lib/Token.php | 4 +--
tests/Printer/examples/mixin1.test | 6 +++++
tests/Printer/examples/nullable1.test | 6 +++++
8 files changed, 134 insertions(+), 6 deletions(-)
create mode 100644 lib/Ast/MixinNode.php
create mode 100644 lib/Ast/Type/NullableNode.php
diff --git a/lib/Ast/MixinNode.php b/lib/Ast/MixinNode.php
new file mode 100644
index 00000000..6ebb834b
--- /dev/null
+++ b/lib/Ast/MixinNode.php
@@ -0,0 +1,23 @@
+class = $class;
+ }
+
+ public function class(): ?ClassNode
+ {
+ return $this->class;
+ }
+}
diff --git a/lib/Ast/Type/NullableNode.php b/lib/Ast/Type/NullableNode.php
new file mode 100644
index 00000000..ec6ce250
--- /dev/null
+++ b/lib/Ast/Type/NullableNode.php
@@ -0,0 +1,35 @@
+nullable = $nullable;
+ $this->type = $type;
+ }
+
+ public function nullable(): Token
+ {
+ return $this->nullable;
+ }
+
+ public function type(): TypeNode
+ {
+ return $this->type;
+ }
+}
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 1e7297ae..f9afd61c 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -12,6 +12,7 @@ final class Lexer
'\*/', // close tag
'\*', // leading tag
'\[\]', //tag
+ '\?', //tag
'@\w+', //tag
'\s+', // whitespace
',', // comma
@@ -29,6 +30,7 @@ final class Lexer
'}' => Token::T_BRACKET_CURLY_CLOSE,
',' => Token::T_COMMA,
'[]' => Token::T_LIST,
+ '?' => Token::T_NULLABLE,
];
/**
diff --git a/lib/Parser.php b/lib/Parser.php
index bc285e85..c01d4d86 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -2,9 +2,11 @@
namespace Phpactor\Docblock;
+use PhpParser\Node\NullableType;
use Phpactor\Docblock\Ast\DeprecatedNode;
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\MethodNode;
+use Phpactor\Docblock\Ast\MixinNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\Type\ClassNode;
@@ -14,6 +16,7 @@
use Phpactor\Docblock\Ast\TypeNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\Type\NullableNode;
use Phpactor\Docblock\Ast\Type\ScalarNode;
use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\VarNode;
@@ -66,6 +69,10 @@ private function parseTag(): TagNode
return $this->parseMethod();
}
+ if ($token->value === '@mixin') {
+ return $this->parseMixin();
+ }
+
return new UnknownTag($this->tokens->chomp());
}
@@ -74,7 +81,7 @@ private function parseParam(): ParamNode
$type = $variable = $textNode = null;
$this->tokens->chomp(Token::T_TAG);
- if ($this->tokens->ifNextIs(Token::T_LABEL)) {
+ if ($this->ifType()) {
$type = $this->parseType();
}
if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
@@ -88,7 +95,7 @@ private function parseVar(): VarNode
{
$this->tokens->chomp(Token::T_TAG);
$type = $variable = null;
- if ($this->tokens->if(Token::T_LABEL)) {
+ if ($this->ifType()) {
$type = $this->parseType();
}
if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
@@ -102,7 +109,7 @@ private function parseMethod(): MethodNode
{
$this->tokens->chomp(Token::T_TAG);
$type = $name = null;
- if ($this->tokens->if(Token::T_LABEL)) {
+ if ($this->ifType()) {
$type = $this->parseType();
}
if ($this->tokens->ifNextIs(Token::T_LABEL)) {
@@ -114,6 +121,11 @@ private function parseMethod(): MethodNode
private function parseType(): ?TypeNode
{
+ if ($this->tokens->current->type === Token::T_NULLABLE) {
+ $nullable = $this->tokens->chomp();
+ return new NullableNode($nullable, $this->parseType());
+ }
+
$type = $this->tokens->chomp(Token::T_LABEL);
$isList = false;
@@ -186,6 +198,21 @@ private function parseDeprecated(): DeprecatedNode
return new DeprecatedNode($this->parseText());
}
+ private function parseMixin(): MixinNode
+ {
+ $this->tokens->chomp();
+ $type = null;
+
+ if ($this->tokens->if(Token::T_LABEL)) {
+ $type = $this->parseType();
+ if (!$type instanceof ClassNode) {
+ $type = null;
+ }
+ }
+
+ return new MixinNode($type);
+ }
+
private function parseText(): ?TextNode
{
$text = [];
@@ -214,4 +241,9 @@ private function parseText(): ?TextNode
return null;
}
+
+ private function ifType(): bool
+ {
+ return $this->tokens->if(Token::T_LABEL) || $this->tokens->if(Token::T_NULLABLE);
+ }
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 7ac21f9d..776e00c9 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -6,6 +6,7 @@
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Element;
use Phpactor\Docblock\Ast\MethodNode;
+use Phpactor\Docblock\Ast\MixinNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
@@ -14,6 +15,7 @@
use Phpactor\Docblock\Ast\ParamNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\Type\NullableNode;
use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
@@ -74,6 +76,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof NullableNode) {
+ $this->renderNullable($node);
+ return;
+ }
+
if ($node instanceof TypeNode) {
$this->renderTypeNode($node);
return;
@@ -104,6 +111,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof MixinNode) {
+ $this->renderMixin($node);
+ return;
+ }
+
throw new RuntimeException(sprintf(
'Do not know how to render "%s"',
get_class($node)
@@ -207,4 +219,18 @@ private function renderMethod(MethodNode $node): void
}
$this->out[] = ')';
}
+
+ private function renderMixin(MixinNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->class());
+ $this->out[] = ')';
+ }
+
+ private function renderNullable(NullableNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ $this->out[] = ')';
+ }
}
diff --git a/lib/Token.php b/lib/Token.php
index 4d97cd66..b64ad171 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -6,14 +6,12 @@
final class Token implements Element
{
- public const T_PHPDOC_BORDER= 'PHPDOC_BORDER';
public const T_PHPDOC_OPEN = 'PHPDOC_OPEN';
public const T_PHPDOC_LEADING = 'PHPDOC_LEADING';
public const T_PHPDOC_CLOSE = 'PHPDOC_CLOSE';
- public const T_BORDER = 'BORDER';
- public const T_TEXT = 'TEXT';
public const T_VARIABLE = 'VARIABLE';
public const T_UNKNOWN = 'UNKNOWN';
+ public const T_NULLABLE = 'NULLABLE';
public const T_TAG = 'TAG';
public const T_COMMA = 'COMMA';
public const T_LIST = 'LIST';
diff --git a/tests/Printer/examples/mixin1.test b/tests/Printer/examples/mixin1.test
index ed97d539..acdadcfa 100644
--- a/tests/Printer/examples/mixin1.test
+++ b/tests/Printer/examples/mixin1.test
@@ -1 +1,7 @@
+/**
+ * @mixin Foobar
+ */
---
+/**
+ * MixinNode(ClassNode(Foobar))
+ */
diff --git a/tests/Printer/examples/nullable1.test b/tests/Printer/examples/nullable1.test
index ed97d539..9014ebf9 100644
--- a/tests/Printer/examples/nullable1.test
+++ b/tests/Printer/examples/nullable1.test
@@ -1 +1,7 @@
+/**
+ * @var ?Foo
+ */
---
+/**
+ * VarNode(NullableNode(ClassNode(Foo)))
+ */
From 38ee7272b201ca460f2c34d397c49e326d256a08 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 14:09:09 +0000
Subject: [PATCH 32/63] Support property
---
lib/Ast/PropertyNode.php | 34 +++++++++++++++++++++++++++
lib/Parser.php | 19 +++++++++++++++
lib/Printer/TestPrinter.php | 17 ++++++++++++++
tests/Printer/examples/property1.test | 6 +++++
4 files changed, 76 insertions(+)
create mode 100644 lib/Ast/PropertyNode.php
diff --git a/lib/Ast/PropertyNode.php b/lib/Ast/PropertyNode.php
new file mode 100644
index 00000000..93480583
--- /dev/null
+++ b/lib/Ast/PropertyNode.php
@@ -0,0 +1,34 @@
+type = $type;
+ $this->name = $name;
+ }
+
+ public function name(): ?Token
+ {
+ return $this->name;
+ }
+
+ public function type(): ?TypeNode
+ {
+ return $this->type;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index c01d4d86..d298fa76 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -7,6 +7,7 @@
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\MixinNode;
+use Phpactor\Docblock\Ast\PropertyNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\Type\ClassNode;
@@ -69,6 +70,10 @@ private function parseTag(): TagNode
return $this->parseMethod();
}
+ if ($token->value === '@property') {
+ return $this->parseProperty();
+ }
+
if ($token->value === '@mixin') {
return $this->parseMixin();
}
@@ -119,6 +124,20 @@ private function parseMethod(): MethodNode
return new MethodNode($type, $name);
}
+ private function parseProperty(): PropertyNode
+ {
+ $this->tokens->chomp(Token::T_TAG);
+ $type = $name = null;
+ if ($this->ifType()) {
+ $type = $this->parseType();
+ }
+ if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
+ $name = $this->tokens->chomp();
+ }
+
+ return new PropertyNode($type, $name);
+ }
+
private function parseType(): ?TypeNode
{
if ($this->tokens->current->type === Token::T_NULLABLE) {
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 776e00c9..25c55ce7 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -7,6 +7,7 @@
use Phpactor\Docblock\Ast\Element;
use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\MixinNode;
+use Phpactor\Docblock\Ast\PropertyNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
@@ -111,6 +112,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof PropertyNode) {
+ $this->renderProperty($node);
+ return;
+ }
+
if ($node instanceof MixinNode) {
$this->renderMixin($node);
return;
@@ -233,4 +239,15 @@ private function renderNullable(NullableNode $node): void
$this->render($node->type());
$this->out[] = ')';
}
+
+ private function renderProperty(PropertyNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ if ($node->name()) {
+ $this->out[] = ',';
+ $this->render($node->name());
+ }
+ $this->out[] = ')';
+ }
}
diff --git a/tests/Printer/examples/property1.test b/tests/Printer/examples/property1.test
index ed97d539..d05b05b0 100644
--- a/tests/Printer/examples/property1.test
+++ b/tests/Printer/examples/property1.test
@@ -1 +1,7 @@
+/**
+ * @property string $foo
+ */
---
+/**
+ * PropertyNode(ScalarNode(string),$foo)
+ */
From 034f18b539c08a193def9981aca4c37c7c1a7f92 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 14:13:32 +0000
Subject: [PATCH 33/63] Support return node
---
lib/Ast/ReturnNode.php | 21 +++++++++++++++++++++
lib/Parser.php | 17 +++++++++++++++++
lib/Printer/TestPrinter.php | 13 +++++++++++++
tests/Printer/examples/return1.test | 6 ++++++
4 files changed, 57 insertions(+)
create mode 100644 lib/Ast/ReturnNode.php
diff --git a/lib/Ast/ReturnNode.php b/lib/Ast/ReturnNode.php
new file mode 100644
index 00000000..4c84cf2f
--- /dev/null
+++ b/lib/Ast/ReturnNode.php
@@ -0,0 +1,21 @@
+type = $type;
+ }
+
+ public function type(): ?TypeNode
+ {
+ return $this->type;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index d298fa76..f19ae9fa 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -8,6 +8,7 @@
use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\MixinNode;
use Phpactor\Docblock\Ast\PropertyNode;
+use Phpactor\Docblock\Ast\ReturnNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\Type\ClassNode;
@@ -78,6 +79,10 @@ private function parseTag(): TagNode
return $this->parseMixin();
}
+ if ($token->value === '@return') {
+ return $this->parseReturn();
+ }
+
return new UnknownTag($this->tokens->chomp());
}
@@ -232,6 +237,18 @@ private function parseMixin(): MixinNode
return new MixinNode($type);
}
+ private function parseReturn(): ReturnNode
+ {
+ $this->tokens->chomp();
+ $type = null;
+
+ if ($this->tokens->if(Token::T_LABEL)) {
+ $type = $this->parseType();
+ }
+
+ return new ReturnNode($type);
+ }
+
private function parseText(): ?TextNode
{
$text = [];
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 25c55ce7..8bb2a954 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -8,6 +8,7 @@
use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\MixinNode;
use Phpactor\Docblock\Ast\PropertyNode;
+use Phpactor\Docblock\Ast\ReturnNode;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
@@ -122,6 +123,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof ReturnNode) {
+ $this->renderReturn($node);
+ return;
+ }
+
throw new RuntimeException(sprintf(
'Do not know how to render "%s"',
get_class($node)
@@ -250,4 +256,11 @@ private function renderProperty(PropertyNode $node): void
}
$this->out[] = ')';
}
+
+ private function renderReturn(ReturnNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->render($node->type());
+ $this->out[] = ')';
+ }
}
diff --git a/tests/Printer/examples/return1.test b/tests/Printer/examples/return1.test
index ed97d539..9e8ab1db 100644
--- a/tests/Printer/examples/return1.test
+++ b/tests/Printer/examples/return1.test
@@ -1 +1,7 @@
+/**
+ * @return Foobar
+ */
---
+/**
+ * ReturnNode(ClassNode(Foobar))
+ */
From 657f3449d4b72adf67d8860afa32f6bfeabfaad9 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 14:38:01 +0000
Subject: [PATCH 34/63] Support fully qualfiied names
---
lib/Ast/Type/UnionNode.php | 24 ++++++++++++++
lib/Lexer.php | 6 ++--
lib/Parser.php | 44 ++++++++++++++++++++------
lib/Printer/TestPrinter.php | 17 ++++++++--
lib/Token.php | 1 +
tests/Printer/examples/classname1.test | 8 +++++
tests/Printer/examples/classname2.test | 8 +++++
tests/Printer/examples/union1.test | 2 ++
8 files changed, 97 insertions(+), 13 deletions(-)
create mode 100644 lib/Ast/Type/UnionNode.php
create mode 100644 tests/Printer/examples/classname1.test
create mode 100644 tests/Printer/examples/classname2.test
diff --git a/lib/Ast/Type/UnionNode.php b/lib/Ast/Type/UnionNode.php
new file mode 100644
index 00000000..4d9946bf
--- /dev/null
+++ b/lib/Ast/Type/UnionNode.php
@@ -0,0 +1,24 @@
+types = $types;
+ }
+
+ public function types(): TypeList
+ {
+ return $this->types;
+ }
+}
diff --git a/lib/Lexer.php b/lib/Lexer.php
index f9afd61c..c5e93bbe 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -16,9 +16,10 @@ final class Lexer
'@\w+', //tag
'\s+', // whitespace
',', // comma
+ '\|', // bar (union)
'\{', '\}', '\[', '\]', '<', '>', // brackets
'\$[a-zA-Z0-9_\x80-\xff]+', // variable
- '[^a-zA-Z0-9_\x80-\xff]+', // label
+ '[^a-zA-Z0-9_\x80-\xff\\\]+', // label
];
private const TOKEN_VALUE_MAP = [
@@ -31,6 +32,7 @@ final class Lexer
',' => Token::T_COMMA,
'[]' => Token::T_LIST,
'?' => Token::T_NULLABLE,
+ '|' => Token::T_BAR,
];
/**
@@ -112,7 +114,7 @@ private function resolveType(string $value, ?array $prevChunk = null): string
return Token::T_WHITESPACE;
}
- if (ctype_alpha($value) || $value === '_') {
+ if (ctype_alpha($value) || false !== strpos($value, '\\')) {
return Token::T_LABEL;
}
diff --git a/lib/Parser.php b/lib/Parser.php
index f19ae9fa..1145cc22 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -20,6 +20,7 @@
use Phpactor\Docblock\Ast\Type\ListNode;
use Phpactor\Docblock\Ast\Type\NullableNode;
use Phpactor\Docblock\Ast\Type\ScalarNode;
+use Phpactor\Docblock\Ast\Type\UnionNode;
use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
@@ -92,7 +93,7 @@ private function parseParam(): ParamNode
$this->tokens->chomp(Token::T_TAG);
if ($this->ifType()) {
- $type = $this->parseType();
+ $type = $this->parseTypes();
}
if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
$variable = $this->parseVariable();
@@ -106,7 +107,7 @@ private function parseVar(): VarNode
$this->tokens->chomp(Token::T_TAG);
$type = $variable = null;
if ($this->ifType()) {
- $type = $this->parseType();
+ $type = $this->parseTypes();
}
if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
$variable = $this->parseVariable();
@@ -120,7 +121,7 @@ private function parseMethod(): MethodNode
$this->tokens->chomp(Token::T_TAG);
$type = $name = null;
if ($this->ifType()) {
- $type = $this->parseType();
+ $type = $this->parseTypes();
}
if ($this->tokens->ifNextIs(Token::T_LABEL)) {
$name = $this->tokens->chomp();
@@ -134,7 +135,7 @@ private function parseProperty(): PropertyNode
$this->tokens->chomp(Token::T_TAG);
$type = $name = null;
if ($this->ifType()) {
- $type = $this->parseType();
+ $type = $this->parseTypes();
}
if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
$name = $this->tokens->chomp();
@@ -143,11 +144,36 @@ private function parseProperty(): PropertyNode
return new PropertyNode($type, $name);
}
+ private function parseTypes(): ?TypeNode
+ {
+ $type = $this->parseType();
+ if (null === $type) {
+ return $type;
+ }
+ $types = [$type];
+
+ while (true) {
+ if ($this->tokens->if(Token::T_BAR)) {
+ $this->tokens->chomp();
+ $types[] = $this->parseType();
+ if (null !== $type) {
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (count($types) === 1) {
+ return $types[0];
+ }
+
+ return new UnionNode(new TypeList($types));
+ }
private function parseType(): ?TypeNode
{
if ($this->tokens->current->type === Token::T_NULLABLE) {
$nullable = $this->tokens->chomp();
- return new NullableNode($nullable, $this->parseType());
+ return new NullableNode($nullable, $this->parseTypes());
}
$type = $this->tokens->chomp(Token::T_LABEL);
@@ -199,12 +225,12 @@ private function parseVariable(): ?VariableNode
return new VariableNode($name);
}
- private function parseTypeList(): TypeList
+ private function parseTypeList(string $delimiter = ','): TypeList
{
$types = [];
while (true) {
if ($this->tokens->if(Token::T_LABEL)) {
- $types[] = $this->parseType();
+ $types[] = $this->parseTypes();
}
if ($this->tokens->if(Token::T_COMMA)) {
$this->tokens->chomp();
@@ -228,7 +254,7 @@ private function parseMixin(): MixinNode
$type = null;
if ($this->tokens->if(Token::T_LABEL)) {
- $type = $this->parseType();
+ $type = $this->parseTypes();
if (!$type instanceof ClassNode) {
$type = null;
}
@@ -243,7 +269,7 @@ private function parseReturn(): ReturnNode
$type = null;
if ($this->tokens->if(Token::T_LABEL)) {
- $type = $this->parseType();
+ $type = $this->parseTypes();
}
return new ReturnNode($type);
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 8bb2a954..07b645e8 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -18,6 +18,7 @@
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
use Phpactor\Docblock\Ast\Type\NullableNode;
+use Phpactor\Docblock\Ast\Type\UnionNode;
use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
@@ -83,6 +84,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof UnionNode) {
+ $this->renderUnion($node);
+ return;
+ }
+
if ($node instanceof TypeNode) {
$this->renderTypeNode($node);
return;
@@ -195,12 +201,12 @@ private function renderGenericNode(GenericNode $node): void
$this->out[] = ')';
}
- private function renderTypeList(TypeList $typeList): void
+ private function renderTypeList(TypeList $typeList, string $delimiter = ','): void
{
foreach ($typeList as $i => $param) {
$this->render($param);
if ($i + 1 !== $typeList->count()) {
- $this->out[] = ',';
+ $this->out[] = $delimiter;
}
}
}
@@ -263,4 +269,11 @@ private function renderReturn(ReturnNode $node): void
$this->render($node->type());
$this->out[] = ')';
}
+
+ private function renderUnion(UnionNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ $this->renderTypeList($node->types(), '|');
+ $this->out[] = ')';
+ }
}
diff --git a/lib/Token.php b/lib/Token.php
index b64ad171..5ebb40c8 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -12,6 +12,7 @@ final class Token implements Element
public const T_VARIABLE = 'VARIABLE';
public const T_UNKNOWN = 'UNKNOWN';
public const T_NULLABLE = 'NULLABLE';
+ public const T_BAR = 'BAR';
public const T_TAG = 'TAG';
public const T_COMMA = 'COMMA';
public const T_LIST = 'LIST';
diff --git a/tests/Printer/examples/classname1.test b/tests/Printer/examples/classname1.test
new file mode 100644
index 00000000..492b2b5b
--- /dev/null
+++ b/tests/Printer/examples/classname1.test
@@ -0,0 +1,8 @@
+/**
+ * @var Foobar\Barfoo
+ */
+---
+/**
+ * VarNode(ClassNode(Foobar\Barfoo))
+ */
+
diff --git a/tests/Printer/examples/classname2.test b/tests/Printer/examples/classname2.test
new file mode 100644
index 00000000..286959e4
--- /dev/null
+++ b/tests/Printer/examples/classname2.test
@@ -0,0 +1,8 @@
+/**
+ * @var \foo_bar\bar_foo
+ */
+---
+/**
+ * VarNode(ClassNode(\foo_bar\bar_foo))
+ */
+
diff --git a/tests/Printer/examples/union1.test b/tests/Printer/examples/union1.test
index ed97d539..379eba23 100644
--- a/tests/Printer/examples/union1.test
+++ b/tests/Printer/examples/union1.test
@@ -1 +1,3 @@
+/** @var string|bool|?Bar */
---
+/** VarNode(UnionNode(ScalarNode(string)|ScalarNode(bool)|NullableNode(ClassNode(Bar)))) */
From a264866625b26c686fde8be6eb39b3d69743f5e0 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 16:56:03 +0000
Subject: [PATCH 35/63] Remove duplicate check
---
lib/Lexer.php | 4 ----
1 file changed, 4 deletions(-)
diff --git a/lib/Lexer.php b/lib/Lexer.php
index c5e93bbe..74e1769d 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -110,10 +110,6 @@ private function resolveType(string $value, ?array $prevChunk = null): string
return Token::T_WHITESPACE;
}
- if (trim($value) === '') {
- return Token::T_WHITESPACE;
- }
-
if (ctype_alpha($value) || false !== strpos($value, '\\')) {
return Token::T_LABEL;
}
From 15382fbd31389d9f521f9e3c92c798576cc1a1c1 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 16:57:45 +0000
Subject: [PATCH 36/63] Use switch (doesn't make any perf diff)
---
lib/Parser.php | 35 +++++++++++++++--------------------
1 file changed, 15 insertions(+), 20 deletions(-)
diff --git a/lib/Parser.php b/lib/Parser.php
index 1145cc22..bd731922 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -56,32 +56,27 @@ private function parseTag(): TagNode
{
$token = $this->tokens->current;
- if ($token->value === '@param') {
- return $this->parseParam();
- }
+ switch ($token->value) {
+ case '@param':
+ return $this->parseParam();
- if ($token->value === '@var') {
- return $this->parseVar();
- }
+ case '@var':
+ return $this->parseVar();
- if ($token->value === '@deprecated') {
- return $this->parseDeprecated();
- }
+ case '@deprecated':
+ return $this->parseDeprecated();
- if ($token->value === '@method') {
- return $this->parseMethod();
- }
+ case '@method':
+ return $this->parseMethod();
- if ($token->value === '@property') {
- return $this->parseProperty();
- }
+ case '@property':
+ return $this->parseProperty();
- if ($token->value === '@mixin') {
- return $this->parseMixin();
- }
+ case '@mixin':
+ return $this->parseMixin();
- if ($token->value === '@return') {
- return $this->parseReturn();
+ case '@return':
+ return $this->parseReturn();
}
return new UnknownTag($this->tokens->chomp());
From 3c0abd67c2121cdbdedeb7dbf28b87a1b9feae9e Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 17:32:20 +0000
Subject: [PATCH 37/63] Support NULL and return text
---
lib/Ast/ReturnNode.php | 13 +-
lib/Ast/Type/NullNode.php | 24 +
lib/Parser.php | 6 +-
lib/Printer/TestPrinter.php | 10 +
tests/Benchmark/AbstractParserBenchCase.php | 20 +
tests/Benchmark/examples/php_core.example | 1043 +++++++++++++++++++
tests/Printer/examples/php_core1.test | 51 +
7 files changed, 1165 insertions(+), 2 deletions(-)
create mode 100644 lib/Ast/Type/NullNode.php
create mode 100644 tests/Benchmark/examples/php_core.example
create mode 100644 tests/Printer/examples/php_core1.test
diff --git a/lib/Ast/ReturnNode.php b/lib/Ast/ReturnNode.php
index 4c84cf2f..f6dea30a 100644
--- a/lib/Ast/ReturnNode.php
+++ b/lib/Ast/ReturnNode.php
@@ -9,13 +9,24 @@ class ReturnNode extends TagNode
*/
private $type;
- public function __construct(?TypeNode $type)
+ /**
+ * @var TextNode|null
+ */
+ private $text;
+
+ public function __construct(?TypeNode $type, ?TextNode $text = null)
{
$this->type = $type;
+ $this->text = $text;
}
public function type(): ?TypeNode
{
return $this->type;
}
+
+ public function text(): ?TextNode
+ {
+ return $this->text;
+ }
}
diff --git a/lib/Ast/Type/NullNode.php b/lib/Ast/Type/NullNode.php
new file mode 100644
index 00000000..0802c711
--- /dev/null
+++ b/lib/Ast/Type/NullNode.php
@@ -0,0 +1,24 @@
+null = $null;
+ }
+
+ public function null(): Token
+ {
+ return $this->null;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index bd731922..07c2f029 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -18,6 +18,7 @@
use Phpactor\Docblock\Ast\TypeNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\Type\NullNode;
use Phpactor\Docblock\Ast\Type\NullableNode;
use Phpactor\Docblock\Ast\Type\ScalarNode;
use Phpactor\Docblock\Ast\Type\UnionNode;
@@ -202,6 +203,9 @@ private function parseType(): ?TypeNode
private function createTypeFromToken(Token $type): TypeNode
{
+ if (strtolower($type->value) === 'null') {
+ return new NullNode($type);
+ }
if (in_array($type->value, self::SCALAR_TYPES)) {
return new ScalarNode($type);
}
@@ -267,7 +271,7 @@ private function parseReturn(): ReturnNode
$type = $this->parseTypes();
}
- return new ReturnNode($type);
+ return new ReturnNode($type, $this->parseText());
}
private function parseText(): ?TextNode
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 07b645e8..59ab9e07 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -17,6 +17,7 @@
use Phpactor\Docblock\Ast\ParamNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\Type\NullNode;
use Phpactor\Docblock\Ast\Type\NullableNode;
use Phpactor\Docblock\Ast\Type\UnionNode;
use Phpactor\Docblock\Ast\UnknownTag;
@@ -89,6 +90,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof NullNode) {
+ $this->out[] = $node->shortName() . '()';
+ return;
+ }
+
if ($node instanceof TypeNode) {
$this->renderTypeNode($node);
return;
@@ -267,6 +273,10 @@ private function renderReturn(ReturnNode $node): void
{
$this->out[] = $node->shortName() . '(';
$this->render($node->type());
+ if ($node->text()) {
+ $this->out[] = ',';
+ $this->render($node->text());
+ }
$this->out[] = ')';
}
diff --git a/tests/Benchmark/AbstractParserBenchCase.php b/tests/Benchmark/AbstractParserBenchCase.php
index 3a3c4a14..93db977b 100644
--- a/tests/Benchmark/AbstractParserBenchCase.php
+++ b/tests/Benchmark/AbstractParserBenchCase.php
@@ -2,6 +2,7 @@
namespace Phpactor\Docblock\Tests\Benchmark;
+use Generator;
use Phpactor\Docblock\Lexer;
use Phpactor\Docblock\Parser;
@@ -9,6 +10,7 @@
* @Iterations(33)
* @Revs(50)
* @BeforeMethods({"setUp"})
+ * @OutputTimeUnit("milliseconds")
*/
abstract class AbstractParserBenchCase
{
@@ -24,6 +26,24 @@ public function benchParse(): void
$this->parse($doc);
}
+ /**
+ * @ParamProviders({"provideCoreDocs"})
+ */
+ public function benchPhpCore(array $params): void
+ {
+ $this->parse(trim($params['doc']));
+ }
+
+ public function provideCoreDocs(): Generator
+ {
+ $contents = file_get_contents(__DIR__ . '/examples/php_core.example');
+ foreach (explode('#!---!#', $contents) as $doc) {
+ yield [
+ 'doc' => $doc
+ ];
+ }
+ }
+
abstract public function setUp(): void;
abstract public function parse(string $doc): void;
}
diff --git a/tests/Benchmark/examples/php_core.example b/tests/Benchmark/examples/php_core.example
new file mode 100644
index 00000000..32b500d3
--- /dev/null
+++ b/tests/Benchmark/examples/php_core.example
@@ -0,0 +1,1043 @@
+
+ * The argument offset. Function arguments are counted starting from
+ * zero.
+ *
+ * @return mixed|false the specified argument, or false on error.
+ */
+#!---!#
+
+/**
+ * Returns an array comprising a function's argument list
+ * @link https://php.net/manual/en/function.func-get-args.php
+ * @return array an array in which each element is a copy of the corresponding
+ * member of the current user-defined function's argument list.
+ */
+#!---!#
+
+/**
+ * Get string length
+ * @link https://php.net/manual/en/function.strlen.php
+ * @param string $string
+ * The string being measured for length.
+ *
+ * @return int The length of the string on success,
+ * and 0 if the string is empty.
+ */
+#!---!#
+
+/**
+ * Binary safe string comparison
+ * @link https://php.net/manual/en/function.strcmp.php
+ * @param string $str1
+ * The first string.
+ *
+ * @param string $str2
+ * The second string.
+ *
+ * @return int < 0 if str1 is less than
+ * str2; > 0 if str1
+ * is greater than str2, and 0 if they are
+ * equal.
+ */
+#!---!#
+
+/**
+ * Binary safe string comparison of the first n characters
+ * @link https://php.net/manual/en/function.strncmp.php
+ * @param string $str1
+ * The first string.
+ *
+ * @param string $str2
+ * The second string.
+ *
+ * @param int $len
+ * Number of characters to use in the comparison.
+ *
+ * @return int < 0 if str1 is less than
+ * str2; > 0 if str1
+ * is greater than str2, and 0 if they are
+ * equal.
+ */
+#!---!#
+
+/**
+ * Binary safe case-insensitive string comparison
+ * @link https://php.net/manual/en/function.strcasecmp.php
+ * @param string $str1
+ * The first string
+ *
+ * @param string $str2
+ * The second string
+ *
+ * @return int < 0 if str1 is less than
+ * str2; > 0 if str1
+ * is greater than str2, and 0 if they are
+ * equal.
+ */
+#!---!#
+
+/**
+ * Binary safe case-insensitive string comparison of the first n characters
+ * @link https://php.net/manual/en/function.strncasecmp.php
+ * @param string $str1
+ * The first string.
+ *
+ * @param string $str2
+ * The second string.
+ *
+ * @param int $len
+ * The length of strings to be used in the comparison.
+ *
+ * @return int < 0 if str1 is less than
+ * str2; > 0 if str1 is
+ * greater than str2, and 0 if they are equal.
+ */
+#!---!#
+
+/**
+ * The function returns {@see true} if the passed $haystack starts from the
+ * $needle string or {@see false} otherwise.
+ *
+ * @param string $haystack
+ * @param string $needle
+ * @return bool
+ * @since 8.0
+ */
+#!---!#
+
+/**
+ * The function returns {@see true} if the passed $haystack ends with the
+ * $needle string or {@see false} otherwise.
+ *
+ * @param string $haystack
+ * @param string $needle
+ * @return bool
+ * @since 8.0
+ */
+#!---!#
+
+/**
+ * Checks if $needle is found in $haystack and returns a boolean value
+ * (true/false) whether or not the $needle was found.
+ *
+ * @param string $haystack
+ * @param string $needle
+ * @return bool
+ * @since 8.0
+ */
+#!---!#
+
+/**
+ * Return the current key and value pair from an array and advance the array cursor
+ * @link https://php.net/manual/en/function.each.php
+ * @param array|ArrayObject &$array
+ * The input array.
+ *
+ * @return array the current key and value pair from the array
+ * array. This pair is returned in a four-element
+ * array, with the keys 0, 1,
+ * key, and value. Elements
+ * 0 and key contain the key name of
+ * the array element, and 1 and value
+ * contain the data.
+ *
+ *
+ * If the internal pointer for the array points past the end of the
+ * array contents, each returns
+ * false.
+ * @deprecated 7.2 Use a foreach loop instead.
+ * @removed 8.0
+ */
+#!---!#
+
+/**
+ * Sets which PHP errors are reported
+ * @link https://php.net/manual/en/function.error-reporting.php
+ * @param int $level [optional]
+ * The new error_reporting
+ * level. It takes on either a bitmask, or named constants. Using named
+ * constants is strongly encouraged to ensure compatibility for future
+ * versions. As error levels are added, the range of integers increases,
+ * so older integer-based error levels will not always behave as expected.
+ *
+ *
+ * The available error level constants and the actual
+ * meanings of these error levels are described in the
+ * predefined constants.
+ *
+ * error_reporting level constants and bit values
+ *
+ * value |
+ * constant |
+ *
+ *
+ * 1 |
+ *
+ * E_ERROR
+ * |
+ *
+ *
+ * 2 |
+ *
+ * E_WARNING
+ * |
+ *
+ *
+ * 4 |
+ *
+ * E_PARSE
+ * |
+ *
+ *
+ * 8 |
+ *
+ * E_NOTICE
+ * |
+ *
+ *
+ * 16 |
+ *
+ * E_CORE_ERROR
+ * |
+ *
+ *
+ * 32 |
+ *
+ * E_CORE_WARNING
+ * |
+ *
+ *
+ * 64 |
+ *
+ * E_COMPILE_ERROR
+ * |
+ *
+ *
+ * 128 |
+ *
+ * E_COMPILE_WARNING
+ * |
+ *
+ *
+ * 256 |
+ *
+ * E_USER_ERROR
+ * |
+ *
+ *
+ * 512 |
+ *
+ * E_USER_WARNING
+ * |
+ *
+ *
+ * 1024 |
+ *
+ * E_USER_NOTICE
+ * |
+ *
+ *
+ * 32767 |
+ *
+ * E_ALL
+ * |
+ *
+ *
+ * 2048 |
+ *
+ * E_STRICT
+ * |
+ *
+ *
+ * 4096 |
+ *
+ * E_RECOVERABLE_ERROR
+ * |
+ *
+ *
+ * 8192 |
+ *
+ * E_DEPRECATED
+ * |
+ *
+ *
+ * 16384 |
+ *
+ * E_USER_DEPRECATED
+ * |
+ *
+ *
+ *
+ * @return int the old error_reporting
+ * level or the current level if no level parameter is
+ * given.
+ */
+#!---!#
+
+/**
+ * Defines a named constant
+ * @link https://php.net/manual/en/function.define.php
+ * @param string $name
+ * The name of the constant.
+ *
+ * @param mixed $value
+ * The value of the constant.
+ * In PHP 5, value must be a scalar value (integer, float, string, boolean, or null).
+ * In PHP 7, array values are also accepted.
+ * It is possible to define resource constants,
+ * however it is not recommended and may cause unpredictable behavior.
+ *
+ * @param bool $case_insensitive [optional]
+ * If set to true, the constant will be defined case-insensitive.
+ * The default behavior is case-sensitive; i.e.
+ * CONSTANT and Constant represent
+ * different values.
+ * Defining case-insensitive constants is deprecated as of PHP 7.3.0.
+ *
+ *
+ * Case-insensitive constants are stored as lower-case.
+ *
+ * @return bool true on success or false on failure.
+ */
+#!---!#
+
+/**
+ * Checks whether a given named constant exists
+ * @link https://php.net/manual/en/function.defined.php
+ * @param string $name
+ * The constant name.
+ *
+ * @return bool true if the named constant given by name
+ * has been defined, false otherwise.
+ */
+#!---!#
+
+/**
+ * Returns the name of the class of an object
+ * @link https://php.net/manual/en/function.get-class.php
+ * @param object $object [optional]
+ * The tested object. This parameter may be omitted when inside a class.
+ *
+ * @return string|false The name of the class of which object is an
+ * instance. Returns false if object is not an
+ * object.
+ *
+ *
+ * If object is omitted when inside a class, the
+ * name of that class is returned.
+ */
+#!---!#
+
+/**
+ * the "Late Static Binding" class name
+ * @link https://php.net/manual/en/function.get-called-class.php
+ * @return string|false The class name. Returns false if called from outside a class.
+ */
+#!---!#
+
+/**
+ * Retrieves the parent class name for object or class
+ * @link https://php.net/manual/en/function.get-parent-class.php
+ * @param mixed $object [optional]
+ * The tested object or class name
+ *
+ * @return string|false The name of the parent class of the class of which
+ * object is an instance or the name.
+ *
+ *
+ * If the object does not have a parent false will be returned.
+ *
+ *
+ * If called without parameter outside object, this function returns false.
+ */
+#!---!#
+
+/**
+ * Checks if the class method exists
+ * @link https://php.net/manual/en/function.method-exists.php
+ * @param mixed $object
+ * An object instance or a class name
+ *
+ * @param string $method_name
+ * The method name
+ *
+ * @return bool true if the method given by method_name
+ * has been defined for the given object, false
+ * otherwise.
+ */
+#!---!#
+
+/**
+ * Checks if the object or class has a property
+ * @link https://php.net/manual/en/function.property-exists.php
+ * @param mixed $class
+ * The class name or an object of the class to test for
+ *
+ * @param string $property
+ * The name of the property
+ *
+ * @return bool true if the property exists, false if it doesn't exist or
+ * null in case of an error.
+ */
+#!---!#
+
+/**
+ * Checks if the trait exists
+ * @param string $traitname Name of the trait to check
+ * @param bool $autoload [optional] Whether to autoload if not already loaded.
+ * @return bool Returns TRUE if trait exists, FALSE if not, NULL in case of an error.
+ * @link https://secure.php.net/manual/en/function.trait-exists.php
+ * @since 5.4
+ */
+#!---!#
+
+/**
+ * Checks if the class has been defined
+ * @link https://php.net/manual/en/function.class-exists.php
+ * @param string $class_name
+ * The class name. The name is matched in a case-insensitive manner.
+ *
+ * @param bool $autoload [optional]
+ * Whether or not to call autoload by default.
+ *
+ * @return bool true if class_name is a defined class,
+ * false otherwise.
+ */
+#!---!#
+
+/**
+ * Checks if the interface has been defined
+ * @link https://php.net/manual/en/function.interface-exists.php
+ * @param string $interface_name
+ * The interface name
+ *
+ * @param bool $autoload [optional]
+ * Whether to call autoload or not by default.
+ *
+ * @return bool true if the interface given by
+ * interface_name has been defined, false otherwise.
+ * @since 5.0.2
+ */
+#!---!#
+
+/**
+ * Return true if the given function has been defined
+ * @link https://php.net/manual/en/function.function-exists.php
+ * @param string $function_name
+ * The function name, as a string.
+ *
+ * @return bool true if function_name exists and is a
+ * function, false otherwise.
+ *
+ *
+ * This function will return false for constructs, such as
+ * include_once and echo.
+ */
+#!---!#
+
+/**
+ * Creates an alias for a class
+ * @link https://php.net/manual/en/function.class-alias.php
+ * @param string $original The original class.
+ * @param string $alias The alias name for the class.
+ * @param bool $autoload [optional] Whether to autoload if the original class is not found.
+ * @return bool true on success or false on failure.
+ */
+#!---!#
+
+/**
+ * Returns an array with the names of included or required files
+ * @link https://php.net/manual/en/function.get-included-files.php
+ * @return string[] an array of the names of all files.
+ *
+ *
+ * The script originally called is considered an "included file," so it will
+ * be listed together with the files referenced by
+ * include and family.
+ *
+ *
+ * Files that are included or required multiple times only show up once in
+ * the returned array.
+ */
+#!---!#
+
+/**
+ * Alias of get_included_files
+ * @link https://php.net/manual/en/function.get-required-files.php
+ * @return string[]
+ */
+#!---!#
+
+/**
+ * Checks if the object has this class as one of its parents
+ * @link https://php.net/manual/en/function.is-subclass-of.php
+ * @param mixed $object
+ * A class name or an object instance
+ *
+ * @param string $class_name
+ * The class name
+ *
+ * @param bool $allow_string [optional]
+ * If this parameter set to false, string class name as object is not allowed.
+ * This also prevents from calling autoloader if the class doesn't exist.
+ *
+ * @return bool This function returns true if the object object,
+ * belongs to a class which is a subclass of
+ * class_name, false otherwise.
+ */
+#!---!#
+
+/**
+ * Checks if the object is of this class or has this class as one of its parents
+ * @link https://php.net/manual/en/function.is-a.php
+ * @param object|string $object
+ * The tested object
+ *
+ * @param string $class_name
+ * The class name
+ *
+ * @param bool $allow_string [optional]
+ * If this parameter set to FALSE, string class name as object
+ * is not allowed. This also prevents from calling autoloader if the class doesn't exist.
+ *
+ * @return bool TRUE if the object is of this class or has this class as one of
+ * its parents, FALSE otherwise.
+ */
+#!---!#
+
+/**
+ * Get the default properties of the class
+ * @link https://php.net/manual/en/function.get-class-vars.php
+ * @param string $class_name
+ * The class name
+ *
+ * @return array an associative array of declared properties visible from the
+ * current scope, with their default value.
+ * The resulting array elements are in the form of
+ * varname => value.
+ */
+#!---!#
+
+/**
+ * Gets the properties of the given object
+ * @link https://php.net/manual/en/function.get-object-vars.php
+ * @param object $object
+ * An object instance.
+ *
+ * @return array an associative array of defined object accessible non-static properties
+ * for the specified object in scope. If a property have
+ * not been assigned a value, it will be returned with a null value.
+ */
+#!---!#
+
+/**
+ * Gets the class methods' names
+ * @link https://php.net/manual/en/function.get-class-methods.php
+ * @param mixed $class_name
+ * The class name or an object instance
+ *
+ * @return array an array of method names defined for the class specified by
+ * class_name. In case of an error, it returns null.
+ */
+#!---!#
+
+/**
+ * Generates a user-level error/warning/notice message
+ * @link https://php.net/manual/en/function.trigger-error.php
+ * @param string $error_msg
+ * The designated error message for this error. It's limited to 1024
+ * characters in length. Any additional characters beyond 1024 will be
+ * truncated.
+ *
+ * @param int $error_type [optional]
+ * The designated error type for this error. It only works with the E_USER
+ * family of constants, and will default to E_USER_NOTICE.
+ *
+ * @return bool This function returns false if wrong error_type is
+ * specified, true otherwise.
+ */
+#!---!#
+
+/**
+ * Alias of trigger_error
+ * @link https://php.net/manual/en/function.user-error.php
+ * @param string $message
+ * @param int $error_type [optional]
+ * @return bool This function returns false if wrong error_type is
+ * specified, true otherwise.
+ */
+#!---!#
+
+/**
+ * Sets a user-defined error handler function
+ * @link https://php.net/manual/en/function.set-error-handler.php
+ * @param callable|null $error_handler
+ * The user function needs to accept two parameters: the error code, and a
+ * string describing the error. Then there are three optional parameters
+ * that may be supplied: the filename in which the error occurred, the
+ * line number in which the error occurred, and the context in which the
+ * error occurred (an array that points to the active symbol table at the
+ * point the error occurred). The function can be shown as:
+ *
+ *
+ * handler
+ * interrno
+ * stringerrstr
+ * stringerrfile
+ * interrline
+ * arrayerrcontext
+ * errno
+ * The first parameter, errno, contains the
+ * level of the error raised, as an integer.
+ * @param int $error_types [optional]
+ * Can be used to mask the triggering of the
+ * error_handler function just like the error_reporting ini setting
+ * controls which errors are shown. Without this mask set the
+ * error_handler will be called for every error
+ * regardless to the setting of the error_reporting setting.
+ *
+ * @return callable|null a string containing the previously defined error handler (if any). If
+ * the built-in error handler is used null is returned. null is also returned
+ * in case of an error such as an invalid callback. If the previous error handler
+ * was a class method, this function will return an indexed array with the class
+ * and the method name.
+ */
+#!---!#
+
+/**
+ * Restores the previous error handler function
+ * @link https://php.net/manual/en/function.restore-error-handler.php
+ * @return bool This function always returns true.
+ */
+#!---!#
+
+/**
+ * Sets a user-defined exception handler function
+ * @link https://php.net/manual/en/function.set-exception-handler.php
+ * @param callable|null $exception_handler
+ * Name of the function to be called when an uncaught exception occurs.
+ * This function must be defined before calling
+ * set_exception_handler. This handler function
+ * needs to accept one parameter, which will be the exception object that
+ * was thrown.
+ * NULL may be passed instead, to reset this handler to its default state.
+ *
+ * @return callable|null the name of the previously defined exception handler, or null on error. If
+ * no previous handler was defined, null is also returned.
+ */
+#!---!#
+
+/**
+ * Restores the previously defined exception handler function
+ * @link https://php.net/manual/en/function.restore-exception-handler.php
+ * @return bool This function always returns true.
+ */
+#!---!#
+
+/**
+ * Returns an array with the name of the defined classes
+ * @link https://php.net/manual/en/function.get-declared-classes.php
+ * @return array an array of the names of the declared classes in the current
+ * script.
+ *
+ *
+ * Note that depending on what extensions you have compiled or
+ * loaded into PHP, additional classes could be present. This means that
+ * you will not be able to define your own classes using these
+ * names. There is a list of predefined classes in the Predefined Classes section of
+ * the appendices.
+ */
+#!---!#
+
+/**
+ * Returns an array of all declared interfaces
+ * @link https://php.net/manual/en/function.get-declared-interfaces.php
+ * @return array an array of the names of the declared interfaces in the current
+ * script.
+ */
+#!---!#
+
+/**
+ * Returns an array of all declared traits
+ * @return array with names of all declared traits in values. Returns NULL in case of a failure.
+ * @link https://secure.php.net/manual/en/function.get-declared-traits.php
+ * @see class_uses()
+ * @since 5.4
+ */
+#!---!#
+
+/**
+ * Returns an array of all defined functions
+ * @link https://php.net/manual/en/function.get-defined-functions.php
+ * @param bool $exclude_disabled [optional] Whether disabled functions should be excluded from the return value.
+ * @return array an multidimensional array containing a list of all defined
+ * functions, both built-in (internal) and user-defined. The internal
+ * functions will be accessible via $arr["internal"], and
+ * the user defined ones using $arr["user"] (see example
+ * below).
+ */
+#!---!#
+
+/**
+ * Returns an array of all defined variables
+ * @link https://php.net/manual/en/function.get-defined-vars.php
+ * @return array A multidimensional array with all the variables.
+ */
+#!---!#
+
+/**
+ * Create an anonymous (lambda-style) function
+ * @link https://php.net/manual/en/function.create-function.php
+ * @param string $args
+ * The function arguments.
+ *
+ * @param string $code
+ * The function code.
+ *
+ * @return string|false a unique function name as a string, or false on error.
+ * @deprecated 7.2 Use anonymous functions instead.
+ * @removed 8.0
+ */
+#!---!#
+
+/**
+ * Returns the resource type
+ * @link https://php.net/manual/en/function.get-resource-type.php
+ * @param resource $handle
+ * The evaluated resource handle.
+ *
+ * @return string If the given handle is a resource, this function
+ * will return a string representing its type. If the type is not identified
+ * by this function, the return value will be the string
+ * Unknown.
+ *
+ *
+ * This function will return false and generate an error if
+ * handle is not a resource.
+ */
+#!---!#
+
+/**
+ * Returns an array with the names of all modules compiled and loaded
+ * @link https://php.net/manual/en/function.get-loaded-extensions.php
+ * @param bool $zend_extensions [optional]
+ * Only return Zend extensions, if not then regular extensions, like
+ * mysqli are listed. Defaults to false (return regular extensions).
+ *
+ * @return array an indexed array of all the modules names.
+ */
+#!---!#
+
+/**
+ * Find out whether an extension is loaded
+ * @link https://php.net/manual/en/function.extension-loaded.php
+ * @param string $name
+ * The extension name.
+ *
+ *
+ * You can see the names of various extensions by using
+ * phpinfo or if you're using the
+ * CGI or CLI version of
+ * PHP you can use the -m switch to
+ * list all available extensions:
+ *
+ * $ php -m
+ * [PHP Modules]
+ * xml
+ * tokenizer
+ * standard
+ * sockets
+ * session
+ * posix
+ * pcre
+ * overload
+ * mysql
+ * mbstring
+ * ctype
+ * [Zend Modules]
+ *
+ *
+ * @return bool true if the extension identified by name
+ * is loaded, false otherwise.
+ */
+#!---!#
+
+/**
+ * Returns an array with the names of the functions of a module
+ * @link https://php.net/manual/en/function.get-extension-funcs.php
+ * @param string $module_name
+ * The module name.
+ *
+ *
+ * This parameter must be in lowercase.
+ *
+ * @return string[]|false an array with all the functions, or false if
+ * module_name is not a valid extension.
+ */
+#!---!#
+
+/**
+ * Returns an associative array with the names of all the constants and their values
+ * @link https://php.net/manual/en/function.get-defined-constants.php
+ * @param bool $categorize [optional]
+ * Causing this function to return a multi-dimensional
+ * array with categories in the keys of the first dimension and constants
+ * and their values in the second dimension.
+ *
+ * define("MY_CONSTANT", 1);
+ * print_r(get_defined_constants(true));
+ *
+ * The above example will output something similar to:
+ *
+ * Array
+ * (
+ * [Core] => Array
+ * (
+ * [E_ERROR] => 1
+ * [E_WARNING] => 2
+ * [E_PARSE] => 4
+ * [E_NOTICE] => 8
+ * [E_CORE_ERROR] => 16
+ * [E_CORE_WARNING] => 32
+ * [E_COMPILE_ERROR] => 64
+ * [E_COMPILE_WARNING] => 128
+ * [E_USER_ERROR] => 256
+ * [E_USER_WARNING] => 512
+ * [E_USER_NOTICE] => 1024
+ * [E_STRICT] => 2048
+ * [E_RECOVERABLE_ERROR] => 4096
+ * [E_DEPRECATED] => 8192
+ * [E_USER_DEPRECATED] => 16384
+ * [E_ALL] => 32767
+ * [TRUE] => 1
+ * )
+ * [pcre] => Array
+ * (
+ * [PREG_PATTERN_ORDER] => 1
+ * [PREG_SET_ORDER] => 2
+ * [PREG_OFFSET_CAPTURE] => 256
+ * [PREG_SPLIT_NO_EMPTY] => 1
+ * [PREG_SPLIT_DELIM_CAPTURE] => 2
+ * [PREG_SPLIT_OFFSET_CAPTURE] => 4
+ * [PREG_GREP_INVERT] => 1
+ * )
+ * [user] => Array
+ * (
+ * [MY_CONSTANT] => 1
+ * )
+ * )
+ *
+ *
+ * @return array
+ */
+#!---!#
+
+/**
+ * Generates a backtrace
+ * @link https://php.net/manual/en/function.debug-backtrace.php
+ * @param int $options [optional]
+ * As of 5.3.6, this parameter is a bitmask for the following options:
+ *
+ * debug_backtrace options
+ *
+ * DEBUG_BACKTRACE_PROVIDE_OBJECT |
+ *
+ * Whether or not to populate the "object" index.
+ * |
+ *
+ *
+ * DEBUG_BACKTRACE_IGNORE_ARGS |
+ *
+ * Whether or not to omit the "args" index, and thus all the function/method arguments,
+ * to save memory.
+ * |
+ *
+ *
+ * Before 5.3.6, the only values recognized are true or false, which are the same as
+ * setting or not setting the DEBUG_BACKTRACE_PROVIDE_OBJECT option respectively.
+ *
+ * @param int $limit [optional]
+ * As of 5.4.0, this parameter can be used to limit the number of stack frames returned.
+ * By default (limit=0) it returns all stack frames.
+ *
+ * @return array an array of associative arrays. The possible returned elements
+ * are as follows:
+ *
+ *
+ *
+ * Possible returned elements from debug_backtrace
+ *
+ * &Name; |
+ * &Type; |
+ * Description |
+ *
+ *
+ * function |
+ * string |
+ *
+ * The current function name. See also
+ * __FUNCTION__.
+ * |
+ *
+ *
+ * line |
+ * integer |
+ *
+ * The current line number. See also
+ * __LINE__.
+ * |
+ *
+ *
+ * file |
+ * string |
+ *
+ * The current file name. See also
+ * __FILE__.
+ * |
+ *
+ *
+ * class |
+ * string |
+ *
+ * The current class name. See also
+ * __CLASS__
+ * |
+ *
+ *
+ * object |
+ * object |
+ *
+ * The current object.
+ * |
+ *
+ *
+ * type |
+ * string |
+ *
+ * The current call type. If a method call, "->" is returned. If a static
+ * method call, "::" is returned. If a function call, nothing is returned.
+ * |
+ *
+ *
+ * args |
+ * array |
+ *
+ * If inside a function, this lists the functions arguments. If
+ * inside an included file, this lists the included file name(s).
+ * |
+ *
+ *
+ */
+#!---!#
+
+/**
+ * Prints a backtrace
+ * @link https://php.net/manual/en/function.debug-print-backtrace.php
+ * @param int $options [optional]
+ * As of 5.3.6, this parameter is a bitmask for the following options:
+ *
+ * debug_print_backtrace options
+ *
+ * DEBUG_BACKTRACE_IGNORE_ARGS |
+ *
+ * Whether or not to omit the "args" index, and thus all the function/method arguments,
+ * to save memory.
+ * |
+ *
+ *
+ *
+ * @param int $limit [optional]
+ * As of 5.4.0, this parameter can be used to limit the number of stack frames printed.
+ * By default (limit=0) it prints all stack frames.
+ *
+ * @return void
+ */
+#!---!#
+
+/**
+ * Forces collection of any existing garbage cycles
+ * @link https://php.net/manual/en/function.gc-collect-cycles.php
+ * @return int number of collected cycles.
+ */
+#!---!#
+
+/**
+ * Returns status of the circular reference collector
+ * @link https://php.net/manual/en/function.gc-enabled.php
+ * @return bool true if the garbage collector is enabled, false otherwise.
+ */
+#!---!#
+
+/**
+ * Activates the circular reference collector
+ * @link https://php.net/manual/en/function.gc-enable.php
+ * @return void
+ */
+#!---!#
+
+/**
+ * Deactivates the circular reference collector
+ * @link https://php.net/manual/en/function.gc-disable.php
+ * @return void
+ */
+#!---!#
+
+/**
+ * Gets information about the garbage collector
+ * @link https://php.net/manual/en/function.gc-status.php
+ * @return array associative array with the following elements:
+ *
+ * - "runs"
+ * - "collected"
+ * - "threshold"
+ * - "roots"
+ *
+ * @since 7.3
+ */
+#!---!#
+
+/**
+ * Reclaims memory used by the Zend Engine memory manager
+ * @link https://php.net/manual/en/function.gc-mem-caches.php
+ * @return int Returns the number of bytes freed.
+ * @since 7.0
+ */
+#!---!#
+
+/**
+ * Returns active resources
+ * @link https://php.net/manual/en/function.get-resources.php
+ * @param string $type [optional]
+ *
+ * If defined, this will cause get_resources() to only return resources of the given type. A list of resource types is available.
+ *
+ * If the string Unknown is provided as the type, then only resources that are of an unknown type will be returned.
+ *
+ * If omitted, all resources will be returned.
+ *
+ * @return array Returns an array of currently active resources, indexed by resource number.
+ * @since 7.0
+ */
+#!---!#
diff --git a/tests/Printer/examples/php_core1.test b/tests/Printer/examples/php_core1.test
new file mode 100644
index 00000000..ee1be1ec
--- /dev/null
+++ b/tests/Printer/examples/php_core1.test
@@ -0,0 +1,51 @@
+/**
+ * Sets a user-defined error handler function
+ * @link https://php.net/manual/en/function.set-error-handler.php
+ * @param callable|null $error_handler
+ * The user function needs to accept two parameters...
+ *
+ *
+ * handler
+ * arrayerrcontext
+ * errno
+ * The first parameter, errno, contains the
+ * level of the error raised, as an integer.
+ * @param int $error_types [optional]
+ * Can be used to mask the triggering of the
+ * error_handler function just like the error_reporting ini setting
+ * controls which errors are shown. Without this mask set the
+ * error_handler will be called for every error
+ * regardless to the setting of the error_reporting setting.
+ *
+ * @return callable|null a string containing the previously defined error handler (if any). If
+ * the built-in error handler is used null is returned. null is also returned
+ * in case of an error such as an invalid callback. If the previous error handler
+ * was a class method, this function will return an indexed array with the class
+ * and the method name.
+ */
+ ---
+/**
+ * Sets a user-defined error handler function
+ * UnknownTag https://php.net/manual/en/function.set-error-handler.php
+ * ParamNode(UnionNode(ClassNode(callable)|NullNode()),VariableNode($error_handler),TextNode( ))
+ * The user function needs to accept two parameters...
+ *
+ *
+ * handler
+ * arrayerrcontext
+ * errno
+ * The first parameter, errno, contains the
+ * level of the error raised, as an integer.
+ * @param int $error_types [optional]
+ * Can be used to mask the triggering of the
+ * error_handler function just like the error_reporting ini setting
+ * controls which errors are shown. Without this mask set the
+ * error_handler will be called for every error
+ * regardless to the setting of the error_reporting setting.
+ *
+ * ReturnNode(UnionNode(ClassNode(callable)|NullNode()),TextNode(a string containing the previously defined error handler (if any). If))
+ * the built-in error handler is used null is returned. null is also returned
+ * in case of an error such as an invalid callback. If the previous error handler
+ * was a class method, this function will return an indexed array with the class
+ * and the method name.
+ */
From 8cf1de297f67908ee1aef655ff646d6537f031d3 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 22:20:24 +0000
Subject: [PATCH 38/63] Support method parameters
---
lib/Ast/MethodNode.php | 65 ++++++++++++++++++++++++++-
lib/Ast/ParameterList.php | 42 +++++++++++++++++
lib/Ast/ParameterNode.php | 31 +++++++++++++
lib/Lexer.php | 10 +++--
lib/Parser.php | 57 +++++++++++++++++++++--
lib/Printer/TestPrinter.php | 43 ++++++++++++++++++
lib/Token.php | 2 +
tests/Printer/examples/method1.test | 2 +-
tests/Printer/examples/method2.test | 7 +++
tests/Printer/examples/method3.test | 7 +++
tests/Printer/examples/php_core1.test | 6 +--
11 files changed, 259 insertions(+), 13 deletions(-)
create mode 100644 lib/Ast/ParameterList.php
create mode 100644 lib/Ast/ParameterNode.php
create mode 100644 tests/Printer/examples/method2.test
create mode 100644 tests/Printer/examples/method3.test
diff --git a/lib/Ast/MethodNode.php b/lib/Ast/MethodNode.php
index 26002a02..4a35dbfe 100644
--- a/lib/Ast/MethodNode.php
+++ b/lib/Ast/MethodNode.php
@@ -16,10 +16,48 @@ class MethodNode extends TagNode
*/
private $name;
- public function __construct(?TypeNode $type, ?Token $name)
+ /**
+ * @var Token|null
+ */
+ private $static;
+
+ /**
+ * @var ParameterList|null
+ */
+ private $parameters;
+
+ /**
+ * @var TextNode|null
+ */
+ private $text;
+
+ /**
+ * @var Token|null
+ */
+ private $parenOpen;
+
+ /**
+ * @var Token|null
+ */
+ private $parenClose;
+
+ public function __construct(
+ ?TypeNode $type,
+ ?Token $name,
+ ?Token $static,
+ ?Token $parenOpen,
+ ?ParameterList $parameters,
+ ?Token $parenClose,
+ ?TextNode $text
+ )
{
$this->type = $type;
$this->name = $name;
+ $this->static = $static;
+ $this->parameters = $parameters;
+ $this->text = $text;
+ $this->parenOpen = $parenOpen;
+ $this->parenClose = $parenClose;
}
public function name(): ?Token
@@ -31,4 +69,29 @@ public function type(): ?TypeNode
{
return $this->type;
}
+
+ public function static(): ?Token
+ {
+ return $this->static;
+ }
+
+ public function parameters(): ?ParameterList
+ {
+ return $this->parameters;
+ }
+
+ public function text(): ?TextNode
+ {
+ return $this->text;
+ }
+
+ public function parenOpen(): ?Token
+ {
+ return $this->parenOpen;
+ }
+
+ public function parenClose(): ?Token
+ {
+ return $this->parenClose;
+ }
}
diff --git a/lib/Ast/ParameterList.php b/lib/Ast/ParameterList.php
new file mode 100644
index 00000000..1c89f370
--- /dev/null
+++ b/lib/Ast/ParameterList.php
@@ -0,0 +1,42 @@
+
+ */
+class ParameterList implements IteratorAggregate, Countable
+{
+ /**
+ * @var ParameterNode[]
+ */
+ private $parameterList;
+
+ /**
+ * @param ParameterNode[] $parameterList
+ */
+ public function __construct(array $parameterList)
+ {
+ $this->parameterList = $parameterList;
+ }
+
+ /**
+ * @return ArrayIterator
+ */
+ public function getIterator(): ArrayIterator
+ {
+ return new ArrayIterator($this->parameterList);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function count()
+ {
+ return count($this->parameterList);
+ }
+}
diff --git a/lib/Ast/ParameterNode.php b/lib/Ast/ParameterNode.php
new file mode 100644
index 00000000..1489ab8e
--- /dev/null
+++ b/lib/Ast/ParameterNode.php
@@ -0,0 +1,31 @@
+type = $type;
+ $this->name = $name;
+ }
+
+ public function name(): ?VariableNode
+ {
+ return $this->name;
+ }
+
+ public function type(): ?TypeNode
+ {
+ return $this->type;
+ }
+}
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 74e1769d..2d3a2d55 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -8,7 +8,7 @@ final class Lexer
* @var string[]
*/
private const PATTERNS = [
- '^/\*+', // start tag
+ '/\*+', // start tag
'\*/', // close tag
'\*', // leading tag
'\[\]', //tag
@@ -17,9 +17,9 @@ final class Lexer
'\s+', // whitespace
',', // comma
'\|', // bar (union)
- '\{', '\}', '\[', '\]', '<', '>', // brackets
+ '(', ')', '\{', '\}', '\[', '\]', '<', '>', // brackets
'\$[a-zA-Z0-9_\x80-\xff]+', // variable
- '[^a-zA-Z0-9_\x80-\xff\\\]+', // label
+ '[a-zA-Z0-9_\\\]+', // label
];
private const TOKEN_VALUE_MAP = [
@@ -29,6 +29,8 @@ final class Lexer
'<' => Token::T_BRACKET_ANGLE_OPEN,
'{' => Token::T_BRACKET_CURLY_OPEN,
'}' => Token::T_BRACKET_CURLY_CLOSE,
+ '(' => Token::T_PAREN_OPEN,
+ ')' => Token::T_PAREN_CLOSE,
',' => Token::T_COMMA,
'[]' => Token::T_LIST,
'?' => Token::T_NULLABLE,
@@ -39,7 +41,7 @@ final class Lexer
* @var string[]
*/
private const IGNORE_PATTERNS = [
- '\s+'
+ '\s+',
];
/**
diff --git a/lib/Parser.php b/lib/Parser.php
index 07c2f029..4b4378f6 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -7,6 +7,8 @@
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\MixinNode;
+use Phpactor\Docblock\Ast\ParameterList;
+use Phpactor\Docblock\Ast\ParameterNode;
use Phpactor\Docblock\Ast\PropertyNode;
use Phpactor\Docblock\Ast\ReturnNode;
use Phpactor\Docblock\Ast\TextNode;
@@ -34,7 +36,7 @@ final class Parser
private $tokens;
private const SCALAR_TYPES = [
- 'int', 'float', 'bool', 'string'
+ 'int', 'float', 'bool', 'string', 'mixed', 'callable'
];
public function parse(Tokens $tokens): Node
@@ -115,15 +117,30 @@ private function parseVar(): VarNode
private function parseMethod(): MethodNode
{
$this->tokens->chomp(Token::T_TAG);
- $type = $name = null;
+ $type = $name = $parameterList = $open = $close = null;
+ $static = null;
+
+ if ($this->tokens->ifNextIs(Token::T_LABEL)) {
+ if ($this->tokens->current->value === 'static') {
+ $static = $this->tokens->chomp();
+ }
+ }
+
if ($this->ifType()) {
$type = $this->parseTypes();
}
- if ($this->tokens->ifNextIs(Token::T_LABEL)) {
+
+ if ($this->tokens->if(Token::T_LABEL)) {
$name = $this->tokens->chomp();
}
- return new MethodNode($type, $name);
+ if ($this->tokens->if(Token::T_PAREN_OPEN)) {
+ $open = $this->tokens->chomp(Token::T_PAREN_OPEN);
+ $parameterList = $this->parseParameterList();
+ $close = $this->tokens->chomp(Token::T_PAREN_CLOSE);
+ }
+
+ return new MethodNode($type, $name, $static, $open, $parameterList, $close, $this->parseText());
}
private function parseProperty(): PropertyNode
@@ -241,6 +258,37 @@ private function parseTypeList(string $delimiter = ','): TypeList
return new TypeList($types);
}
+ private function parseParameterList(): ?ParameterList
+ {
+ if ($this->tokens->if(Token::T_PAREN_CLOSE)) {
+ return null;
+ }
+
+ $parameters = [];
+ while (true) {
+ $parameters[] = $this->parseParameter();
+ if ($this->tokens->if(Token::T_COMMA)) {
+ $this->tokens->chomp();
+ continue;
+ }
+ break;
+ }
+
+ return new ParameterList($parameters);
+ }
+
+ private function parseParameter(): ParameterNode
+ {
+ $type = $name = null;
+ if ($this->tokens->if(Token::T_LABEL)) {
+ $type = $this->parseTypes();
+ }
+ if ($this->tokens->if(Token::T_VARIABLE)) {
+ $name = $this->parseVariable();
+ }
+ return new ParameterNode($type, $name);
+ }
+
private function parseDeprecated(): DeprecatedNode
{
$this->tokens->chomp();
@@ -307,4 +355,5 @@ private function ifType(): bool
{
return $this->tokens->if(Token::T_LABEL) || $this->tokens->if(Token::T_NULLABLE);
}
+
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 59ab9e07..b61fb088 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -7,6 +7,8 @@
use Phpactor\Docblock\Ast\Element;
use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\MixinNode;
+use Phpactor\Docblock\Ast\ParameterList;
+use Phpactor\Docblock\Ast\ParameterNode;
use Phpactor\Docblock\Ast\PropertyNode;
use Phpactor\Docblock\Ast\ReturnNode;
use Phpactor\Docblock\Ast\TextNode;
@@ -140,6 +142,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof ParameterNode) {
+ $this->renderParameter($node);
+ return;
+ }
+
throw new RuntimeException(sprintf(
'Do not know how to render "%s"',
get_class($node)
@@ -241,6 +248,17 @@ private function renderMethod(MethodNode $node): void
$this->out[] = ',';
$this->render($node->name());
}
+ if ($node->static()) {
+ $this->out[] = ',static';
+ }
+ if ($node->parameters()) {
+ $this->out[] = ',';
+ $this->renderParameterList($node->parameters());
+ }
+ if ($node->text()) {
+ $this->out[] = ',';
+ $this->render($node->text());
+ }
$this->out[] = ')';
}
@@ -286,4 +304,29 @@ private function renderUnion(UnionNode $node): void
$this->renderTypeList($node->types(), '|');
$this->out[] = ')';
}
+
+ private function renderParameterList(ParameterList $list): void
+ {
+ $this->out[] = 'ParameterList(';
+ foreach ($list as $i => $parameter) {
+ $this->render($parameter);
+ if ($i + 1 !== $list->count()) {
+ $this->out[] = ',';
+ }
+ }
+ $this->out[] = ')';
+ }
+
+ private function renderParameter(ParameterNode $node): void
+ {
+ $this->out[] = $node->shortName() . '(';
+ if ($node->name()) {
+ $this->render($node->name());
+ }
+ if ($node->type()) {
+ $this->out[] = ',';
+ $this->render($node->type());
+ }
+ $this->out[] = ')';
+ }
}
diff --git a/lib/Token.php b/lib/Token.php
index 5ebb40c8..6fc4e26c 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -24,6 +24,8 @@ final class Token implements Element
public const T_BRACKET_ANGLE_CLOSE = 'BRACKET_ANGLE_CLOSE';
public const T_BRACKET_CURLY_OPEN = 'BRACKET_CURLY_OPEN';
public const T_BRACKET_CURLY_CLOSE = 'BRACKET_CURLY_CLOSE';
+ public const T_PAREN_OPEN = 'PAREN_OPEN';
+ public const T_PAREN_CLOSE = 'PAREN_CLOSE';
/**
* @var int
diff --git a/tests/Printer/examples/method1.test b/tests/Printer/examples/method1.test
index 63ba6fae..826a9b5a 100644
--- a/tests/Printer/examples/method1.test
+++ b/tests/Printer/examples/method1.test
@@ -3,5 +3,5 @@
*/
---
/**
- * MethodNode(ClassNode(Foobar),foobar)()
+ * MethodNode(ClassNode(Foobar),foobar)
*/
diff --git a/tests/Printer/examples/method2.test b/tests/Printer/examples/method2.test
new file mode 100644
index 00000000..ab66ad1f
--- /dev/null
+++ b/tests/Printer/examples/method2.test
@@ -0,0 +1,7 @@
+/**
+ * @method static Foobar foobar()
+ */
+---
+/**
+ * MethodNode(ClassNode(Foobar),foobar,static)
+ */
diff --git a/tests/Printer/examples/method3.test b/tests/Printer/examples/method3.test
new file mode 100644
index 00000000..8600ba44
--- /dev/null
+++ b/tests/Printer/examples/method3.test
@@ -0,0 +1,7 @@
+/**
+ * @method static bool allAlnum(mixed $value) Assert that value is alphanumeric for all values.
+ */
+---
+/**
+ * MethodNode(ScalarNode(bool),allAlnum,static,ParameterList(ParameterNode(VariableNode($value),ScalarNode(mixed))),TextNode(Assert that value is alphanumeric for all values.))
+ */
diff --git a/tests/Printer/examples/php_core1.test b/tests/Printer/examples/php_core1.test
index ee1be1ec..e555336e 100644
--- a/tests/Printer/examples/php_core1.test
+++ b/tests/Printer/examples/php_core1.test
@@ -27,7 +27,7 @@
/**
* Sets a user-defined error handler function
* UnknownTag https://php.net/manual/en/function.set-error-handler.php
- * ParamNode(UnionNode(ClassNode(callable)|NullNode()),VariableNode($error_handler),TextNode( ))
+ * ParamNode(UnionNode(ScalarNode(callable)|NullNode()),VariableNode($error_handler),TextNode(
))
* The user function needs to accept two parameters...
*
*
@@ -36,14 +36,14 @@
* errno
* The first parameter, errno, contains the
* level of the error raised, as an integer.
- * @param int $error_types [optional]
+ * ParamNode(ScalarNode(int),VariableNode($error_types),TextNode( [optional]
))
* Can be used to mask the triggering of the
* error_handler function just like the error_reporting ini setting
* controls which errors are shown. Without this mask set the
* error_handler will be called for every error
* regardless to the setting of the error_reporting setting.
*
- * ReturnNode(UnionNode(ClassNode(callable)|NullNode()),TextNode(a string containing the previously defined error handler (if any). If))
+ * ReturnNode(UnionNode(ScalarNode(callable)|NullNode()),TextNode(a string containing the previously defined error handler (if any). If))
* the built-in error handler is used null is returned. null is also returned
* in case of an error such as an invalid callback. If the previous error handler
* was a class method, this function will return an indexed array with the class
From 0e38ec911efc171a009317fafa1f0b66ffb6c2fb Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sat, 23 Jan 2021 23:01:24 +0000
Subject: [PATCH 39/63] Support for default method values
---
composer.json | 3 +-
lib/Ast/Node.php | 2 +-
lib/Ast/ParameterNode.php | 13 +-
lib/Ast/Value/NullValue.php | 32 ++++
lib/Ast/Value/UnkownValue.php | 0
lib/Ast/ValueNode.php | 11 ++
lib/Lexer.php | 4 +-
lib/Parser.php | 23 ++-
lib/Printer/TestPrinter.php | 15 ++
lib/Token.php | 1 +
lib/Tokens.php | 12 ++
tests/Benchmark/AbstractParserBenchCase.php | 10 +-
tests/Benchmark/examples/assert.example | 186 ++++++++++++++++++++
tests/Printer/examples/method4.test | 7 +
tests/Printer/examples/method5.test | 7 +
tests/Printer/examples/method6.test | 7 +
16 files changed, 325 insertions(+), 8 deletions(-)
create mode 100644 lib/Ast/Value/NullValue.php
create mode 100644 lib/Ast/Value/UnkownValue.php
create mode 100644 lib/Ast/ValueNode.php
create mode 100644 tests/Benchmark/examples/assert.example
create mode 100644 tests/Printer/examples/method4.test
create mode 100644 tests/Printer/examples/method5.test
create mode 100644 tests/Printer/examples/method6.test
diff --git a/composer.json b/composer.json
index 93f4220e..bd7deaf2 100644
--- a/composer.json
+++ b/composer.json
@@ -20,7 +20,8 @@
"phpunit/phpunit": "^9.0",
"phpspec/prophecy-phpunit": "^2.0",
"symfony/var-dumper": "^5.2",
- "phpstan/phpdoc-parser": "^0.4.10"
+ "phpstan/phpdoc-parser": "^0.4.10",
+ "jetbrains/phpstorm-stubs": "^2020.2"
},
"extra": {
"branch-alias": {
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index 8ecbfb98..740956d2 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -2,7 +2,7 @@
namespace Phpactor\Docblock\Ast;
-class Node implements Element
+abstract class Node implements Element
{
public function shortName(): string
{
diff --git a/lib/Ast/ParameterNode.php b/lib/Ast/ParameterNode.php
index 1489ab8e..ee2c49da 100644
--- a/lib/Ast/ParameterNode.php
+++ b/lib/Ast/ParameterNode.php
@@ -13,10 +13,16 @@ class ParameterNode extends Node
*/
private $name;
- public function __construct(?TypeNode $type, ?VariableNode $name)
+ /**
+ * @var ValueNode|null
+ */
+ private $default;
+
+ public function __construct(?TypeNode $type, ?VariableNode $name, ?ValueNode $default)
{
$this->type = $type;
$this->name = $name;
+ $this->default = $default;
}
public function name(): ?VariableNode
@@ -28,4 +34,9 @@ public function type(): ?TypeNode
{
return $this->type;
}
+
+ public function default(): ?ValueNode
+ {
+ return $this->default;
+ }
}
diff --git a/lib/Ast/Value/NullValue.php b/lib/Ast/Value/NullValue.php
new file mode 100644
index 00000000..50e444fc
--- /dev/null
+++ b/lib/Ast/Value/NullValue.php
@@ -0,0 +1,32 @@
+null = $null;
+ }
+
+ public function null(): Token
+ {
+ return $this->null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function value()
+ {
+ return null;
+ }
+}
diff --git a/lib/Ast/Value/UnkownValue.php b/lib/Ast/Value/UnkownValue.php
new file mode 100644
index 00000000..e69de29b
diff --git a/lib/Ast/ValueNode.php b/lib/Ast/ValueNode.php
new file mode 100644
index 00000000..7fe42ebd
--- /dev/null
+++ b/lib/Ast/ValueNode.php
@@ -0,0 +1,11 @@
+', // brackets
'\$[a-zA-Z0-9_\x80-\xff]+', // variable
'[a-zA-Z0-9_\\\]+', // label
@@ -35,6 +36,7 @@ final class Lexer
'[]' => Token::T_LIST,
'?' => Token::T_NULLABLE,
'|' => Token::T_BAR,
+ '=' => Token::T_EQUALS,
];
/**
@@ -112,7 +114,7 @@ private function resolveType(string $value, ?array $prevChunk = null): string
return Token::T_WHITESPACE;
}
- if (ctype_alpha($value) || false !== strpos($value, '\\')) {
+ if (ctype_alpha($value[0]) || $value[0] === '\\') {
return Token::T_LABEL;
}
diff --git a/lib/Parser.php b/lib/Parser.php
index 4b4378f6..d0b83e3f 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -25,6 +25,8 @@
use Phpactor\Docblock\Ast\Type\ScalarNode;
use Phpactor\Docblock\Ast\Type\UnionNode;
use Phpactor\Docblock\Ast\UnknownTag;
+use Phpactor\Docblock\Ast\ValueNode;
+use Phpactor\Docblock\Ast\Value\NullValue;
use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
@@ -137,7 +139,7 @@ private function parseMethod(): MethodNode
if ($this->tokens->if(Token::T_PAREN_OPEN)) {
$open = $this->tokens->chomp(Token::T_PAREN_OPEN);
$parameterList = $this->parseParameterList();
- $close = $this->tokens->chomp(Token::T_PAREN_CLOSE);
+ $close = $this->tokens->chompIf(Token::T_PAREN_CLOSE);
}
return new MethodNode($type, $name, $static, $open, $parameterList, $close, $this->parseText());
@@ -279,14 +281,18 @@ private function parseParameterList(): ?ParameterList
private function parseParameter(): ParameterNode
{
- $type = $name = null;
+ $type = $name = $default = null;
if ($this->tokens->if(Token::T_LABEL)) {
$type = $this->parseTypes();
}
if ($this->tokens->if(Token::T_VARIABLE)) {
$name = $this->parseVariable();
}
- return new ParameterNode($type, $name);
+ if ($this->tokens->if(Token::T_EQUALS)) {
+ $equals = $this->tokens->chomp();
+ $default = $this->parseValue();
+ }
+ return new ParameterNode($type, $name, $default);
}
private function parseDeprecated(): DeprecatedNode
@@ -356,4 +362,15 @@ private function ifType(): bool
return $this->tokens->if(Token::T_LABEL) || $this->tokens->if(Token::T_NULLABLE);
}
+ private function parseValue(): ?ValueNode
+ {
+ if ($this->tokens->if(Token::T_LABEL)) {
+ if (strtolower($this->tokens->current->value) === 'null') {
+ return new NullValue($this->tokens->chomp());
+ }
+ }
+
+ return null;
+ }
+
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index b61fb088..7b95da6a 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -23,6 +23,7 @@
use Phpactor\Docblock\Ast\Type\NullableNode;
use Phpactor\Docblock\Ast\Type\UnionNode;
use Phpactor\Docblock\Ast\UnknownTag;
+use Phpactor\Docblock\Ast\ValueNode;
use Phpactor\Docblock\Ast\VarNode;
use Phpactor\Docblock\Ast\VariableNode;
use Phpactor\Docblock\Printer;
@@ -147,6 +148,11 @@ private function render(?Element $node): void
return;
}
+ if ($node instanceof ValueNode) {
+ $this->renderValue($node);
+ return;
+ }
+
throw new RuntimeException(sprintf(
'Do not know how to render "%s"',
get_class($node)
@@ -327,6 +333,15 @@ private function renderParameter(ParameterNode $node): void
$this->out[] = ',';
$this->render($node->type());
}
+ if ($node->default()) {
+ $this->out[] = ',';
+ $this->render($node->default());
+ }
$this->out[] = ')';
}
+
+ private function renderValue(ValueNode $node)
+ {
+ $this->out[] = json_encode($node->value());
+ }
}
diff --git a/lib/Token.php b/lib/Token.php
index 6fc4e26c..9c288387 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -14,6 +14,7 @@ final class Token implements Element
public const T_NULLABLE = 'NULLABLE';
public const T_BAR = 'BAR';
public const T_TAG = 'TAG';
+ public const T_EQUALS = 'EQUALS';
public const T_COMMA = 'COMMA';
public const T_LIST = 'LIST';
public const T_LABEL = 'LABEL';
diff --git a/lib/Tokens.php b/lib/Tokens.php
index c5bba974..94814575 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -85,6 +85,18 @@ public function chomp(?string $type = null): ?Token
return $token;
}
+ /**
+ * Chomp only if the current node is the given type
+ */
+ public function chompIf(string $type): ?Token
+ {
+ if ($this->current->type === $type) {
+ return $this->chomp($type);
+ }
+
+ return null;
+ }
+
public function ifNextIs(string $type): bool
{
if ($this->next()->type === $type) {
diff --git a/tests/Benchmark/AbstractParserBenchCase.php b/tests/Benchmark/AbstractParserBenchCase.php
index 93db977b..9f201f21 100644
--- a/tests/Benchmark/AbstractParserBenchCase.php
+++ b/tests/Benchmark/AbstractParserBenchCase.php
@@ -34,16 +34,24 @@ public function benchPhpCore(array $params): void
$this->parse(trim($params['doc']));
}
+ /**
+ * @return Generator
+ */
public function provideCoreDocs(): Generator
{
$contents = file_get_contents(__DIR__ . '/examples/php_core.example');
foreach (explode('#!---!#', $contents) as $doc) {
- yield [
+ yield str_replace("\n", '', substr($doc, 0, 10)) => [
'doc' => $doc
];
}
}
+ public function benchAssert(): void
+ {
+ $this->parse(file_get_contents(__DIR__ . '/examples/assert.example'));
+ }
+
abstract public function setUp(): void;
abstract public function parse(string $doc): void;
}
diff --git a/tests/Benchmark/examples/assert.example b/tests/Benchmark/examples/assert.example
new file mode 100644
index 00000000..ddbbb345
--- /dev/null
+++ b/tests/Benchmark/examples/assert.example
@@ -0,0 +1,186 @@
+/**
+ * Assert library.
+ *
+ * @author Benjamin Eberlei
+ *
+ * @method static bool allAlnum(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values.
+ * @method static bool allBase64(string $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values.
+ * @method static bool allBetween(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit for all values.
+ * @method static bool allBetweenExclusive(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit for all values.
+ * @method static bool allBetweenLength(mixed $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths for all values.
+ * @method static bool allBoolean(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean for all values.
+ * @method static bool allChoice(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices for all values.
+ * @method static bool allChoicesNotEmpty(array $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content for all values.
+ * @method static bool allClassExists(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists for all values.
+ * @method static bool allContains(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars for all values.
+ * @method static bool allCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count for all values.
+ * @method static bool allDate(string $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format for all values.
+ * @method static bool allDefined(mixed $constant, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values.
+ * @method static bool allDigit(mixed $value, string|callable $message = null, string $propertyPath = null) Validates if an integer or integerish is a digit for all values.
+ * @method static bool allDirectory(string $value, string|callable $message = null, string $propertyPath = null) Assert that a directory exists for all values.
+ * @method static bool allE164(string $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number for all values.
+ * @method static bool allEmail(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) for all values.
+ * @method static bool allEndsWith(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars for all values.
+ * @method static bool allEq(mixed $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) for all values.
+ * @method static bool allEqArraySubset(mixed $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset for all values.
+ * @method static bool allExtensionLoaded(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded for all values.
+ * @method static bool allExtensionVersion(string $extension, string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded and a specific version is installed for all values.
+ * @method static bool allFalse(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean False for all values.
+ * @method static bool allFile(string $value, string|callable $message = null, string $propertyPath = null) Assert that a file exists for all values.
+ * @method static bool allFloat(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php float for all values.
+ * @method static bool allGreaterOrEqualThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater or equal than given limit for all values.
+ * @method static bool allGreaterThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater than given limit for all values.
+ * @method static bool allImplementsInterface(mixed $class, string $interfaceName, string|callable $message = null, string $propertyPath = null) Assert that the class implements the interface for all values.
+ * @method static bool allInArray(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices. This is an alias of Assertion::choice() for all values.
+ * @method static bool allInteger(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer for all values.
+ * @method static bool allIntegerish(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer'ish for all values.
+ * @method static bool allInterfaceExists(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the interface exists for all values.
+ * @method static bool allIp(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 or IPv6 address for all values.
+ * @method static bool allIpv4(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address for all values.
+ * @method static bool allIpv6(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv6 address for all values.
+ * @method static bool allIsArray(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array for all values.
+ * @method static bool allIsArrayAccessible(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or an array-accessible object for all values.
+ * @method static bool allIsCallable(mixed $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is callable for all values.
+ * @method static bool allIsCountable(array|Countable|ResourceBundle|SimpleXMLElement $value, string|callable $message = null, string $propertyPath = null) Assert that value is countable for all values.
+ * @method static bool allIsInstanceOf(mixed $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is instance of given class-name for all values.
+ * @method static bool allIsJsonString(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid json string for all values.
+ * @method static bool allIsObject(mixed $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is an object for all values.
+ * @method static bool allIsResource(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a resource for all values.
+ * @method static bool allIsTraversable(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or a traversable object for all values.
+ * @method static bool allKeyExists(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array for all values.
+ * @method static bool allKeyIsset(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object using isset() for all values.
+ * @method static bool allKeyNotExists(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key does not exist in an array for all values.
+ * @method static bool allLength(mixed $value, int $length, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string has a given length for all values.
+ * @method static bool allLessOrEqualThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less or equal than given limit for all values.
+ * @method static bool allLessThan(mixed $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less than given limit for all values.
+ * @method static bool allMax(mixed $value, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that a number is smaller as a given limit for all values.
+ * @method static bool allMaxCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at most $count elements for all values.
+ * @method static bool allMaxLength(mixed $value, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string value is not longer than $maxLength chars for all values.
+ * @method static bool allMethodExists(string $value, mixed $object, string|callable $message = null, string $propertyPath = null) Determines that the named method is defined in the provided object for all values.
+ * @method static bool allMin(mixed $value, mixed $minValue, string|callable $message = null, string $propertyPath = null) Assert that a value is at least as big as a given limit for all values.
+ * @method static bool allMinCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at least $count elements for all values.
+ * @method static bool allMinLength(mixed $value, int $minLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that a string is at least $minLength chars long for all values.
+ * @method static bool allNoContent(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is empty for all values.
+ * @method static bool allNotBlank(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is not blank for all values.
+ * @method static bool allNotContains(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string does not contains a sequence of chars for all values.
+ * @method static bool allNotEmpty(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is not empty for all values.
+ * @method static bool allNotEmptyKey(mixed $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object and its value is not empty for all values.
+ * @method static bool allNotEq(mixed $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not equal (using ==) for all values.
+ * @method static bool allNotInArray(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is not in array of choices for all values.
+ * @method static bool allNotIsInstanceOf(mixed $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is not instance of given class-name for all values.
+ * @method static bool allNotNull(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is not null for all values.
+ * @method static bool allNotRegex(mixed $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value does not match a regex for all values.
+ * @method static bool allNotSame(mixed $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not the same (using ===) for all values.
+ * @method static bool allNull(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is null for all values.
+ * @method static bool allNumeric(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is numeric for all values.
+ * @method static bool allObjectOrClass(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the value is an object, or a class that exists for all values.
+ * @method static bool allPhpVersion(string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert on PHP version for all values.
+ * @method static bool allPropertiesExist(mixed $value, array $properties, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the properties all exist for all values.
+ * @method static bool allPropertyExists(mixed $value, string $property, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the property exists for all values.
+ * @method static bool allRange(mixed $value, mixed $minValue, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that value is in range of numbers for all values.
+ * @method static bool allReadable(string $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something readable for all values.
+ * @method static bool allRegex(mixed $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value matches a regex for all values.
+ * @method static bool allSame(mixed $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are the same (using ===) for all values.
+ * @method static bool allSatisfy(mixed $value, callable $callback, string|callable $message = null, string $propertyPath = null) Assert that the provided value is valid according to a callback for all values.
+ * @method static bool allScalar(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a PHP scalar for all values.
+ * @method static bool allStartsWith(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string starts with a sequence of chars for all values.
+ * @method static bool allString(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is a string for all values.
+ * @method static bool allSubclassOf(mixed $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is subclass of given class-name for all values.
+ * @method static bool allTrue(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean True for all values.
+ * @method static bool allUniqueValues(array $values, string|callable $message = null, string $propertyPath = null) Assert that values in array are unique (using strict equality) for all values.
+ * @method static bool allUrl(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is an URL for all values.
+ * @method static bool allUuid(string $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid UUID for all values.
+ * @method static bool allVersion(string $version1, string $operator, string $version2, string|callable $message = null, string $propertyPath = null) Assert comparison of two versions for all values.
+ * @method static bool allWriteable(string $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something writeable for all values.
+ * @method static bool nullOrAlnum(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric or that the value is null.
+ * @method static bool nullOrBase64(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined or that the value is null.
+ * @method static bool nullOrBetween(mixed|null $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit or that the value is null.
+ * @method static bool nullOrBetweenExclusive(mixed|null $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit or that the value is null.
+ * @method static bool nullOrBetweenLength(mixed|null $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths or that the value is null.
+ * @method static bool nullOrBoolean(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean or that the value is null.
+ * @method static bool nullOrChoice(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices or that the value is null.
+ * @method static bool nullOrChoicesNotEmpty(array|null $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content or that the value is null.
+ * @method static bool nullOrClassExists(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists or that the value is null.
+ * @method static bool nullOrContains(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars or that the value is null.
+ * @method static bool nullOrCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count or that the value is null.
+ * @method static bool nullOrDate(string|null $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format or that the value is null.
+ * @method static bool nullOrDefined(mixed|null $constant, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined or that the value is null.
+ * @method static bool nullOrDigit(mixed|null $value, string|callable $message = null, string $propertyPath = null) Validates if an integer or integerish is a digit or that the value is null.
+ * @method static bool nullOrDirectory(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a directory exists or that the value is null.
+ * @method static bool nullOrE164(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number or that the value is null.
+ * @method static bool nullOrEmail(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) or that the value is null.
+ * @method static bool nullOrEndsWith(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars or that the value is null.
+ * @method static bool nullOrEq(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) or that the value is null.
+ * @method static bool nullOrEqArraySubset(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset or that the value is null.
+ * @method static bool nullOrExtensionLoaded(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded or that the value is null.
+ * @method static bool nullOrExtensionVersion(string|null $extension, string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded and a specific version is installed or that the value is null.
+ * @method static bool nullOrFalse(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean False or that the value is null.
+ * @method static bool nullOrFile(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a file exists or that the value is null.
+ * @method static bool nullOrFloat(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php float or that the value is null.
+ * @method static bool nullOrGreaterOrEqualThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater or equal than given limit or that the value is null.
+ * @method static bool nullOrGreaterThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater than given limit or that the value is null.
+ * @method static bool nullOrImplementsInterface(mixed|null $class, string $interfaceName, string|callable $message = null, string $propertyPath = null) Assert that the class implements the interface or that the value is null.
+ * @method static bool nullOrInArray(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices. This is an alias of Assertion::choice() or that the value is null.
+ * @method static bool nullOrInteger(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer or that the value is null.
+ * @method static bool nullOrIntegerish(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer'ish or that the value is null.
+ * @method static bool nullOrInterfaceExists(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the interface exists or that the value is null.
+ * @method static bool nullOrIp(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 or IPv6 address or that the value is null.
+ * @method static bool nullOrIpv4(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address or that the value is null.
+ * @method static bool nullOrIpv6(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv6 address or that the value is null.
+ * @method static bool nullOrIsArray(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or that the value is null.
+ * @method static bool nullOrIsArrayAccessible(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or an array-accessible object or that the value is null.
+ * @method static bool nullOrIsCallable(mixed|null $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is callable or that the value is null.
+ * @method static bool nullOrIsCountable(array|Countable|ResourceBundle|SimpleXMLElement|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is countable or that the value is null.
+ * @method static bool nullOrIsInstanceOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is instance of given class-name or that the value is null.
+ * @method static bool nullOrIsJsonString(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid json string or that the value is null.
+ * @method static bool nullOrIsObject(mixed|null $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is an object or that the value is null.
+ * @method static bool nullOrIsResource(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a resource or that the value is null.
+ * @method static bool nullOrIsTraversable(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or a traversable object or that the value is null.
+ * @method static bool nullOrKeyExists(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array or that the value is null.
+ * @method static bool nullOrKeyIsset(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object using isset() or that the value is null.
+ * @method static bool nullOrKeyNotExists(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key does not exist in an array or that the value is null.
+ * @method static bool nullOrLength(mixed|null $value, int $length, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string has a given length or that the value is null.
+ * @method static bool nullOrLessOrEqualThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less or equal than given limit or that the value is null.
+ * @method static bool nullOrLessThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less than given limit or that the value is null.
+ * @method static bool nullOrMax(mixed|null $value, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that a number is smaller as a given limit or that the value is null.
+ * @method static bool nullOrMaxCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at most $count elements or that the value is null.
+ * @method static bool nullOrMaxLength(mixed|null $value, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string value is not longer than $maxLength chars or that the value is null.
+ * @method static bool nullOrMethodExists(string|null $value, mixed $object, string|callable $message = null, string $propertyPath = null) Determines that the named method is defined in the provided object or that the value is null.
+ * @method static bool nullOrMin(mixed|null $value, mixed $minValue, string|callable $message = null, string $propertyPath = null) Assert that a value is at least as big as a given limit or that the value is null.
+ * @method static bool nullOrMinCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at least $count elements or that the value is null.
+ * @method static bool nullOrMinLength(mixed|null $value, int $minLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that a string is at least $minLength chars long or that the value is null.
+ * @method static bool nullOrNoContent(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is empty or that the value is null.
+ * @method static bool nullOrNotBlank(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not blank or that the value is null.
+ * @method static bool nullOrNotContains(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string does not contains a sequence of chars or that the value is null.
+ * @method static bool nullOrNotEmpty(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not empty or that the value is null.
+ * @method static bool nullOrNotEmptyKey(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object and its value is not empty or that the value is null.
+ * @method static bool nullOrNotEq(mixed|null $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not equal (using ==) or that the value is null.
+ * @method static bool nullOrNotInArray(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is not in array of choices or that the value is null.
+ * @method static bool nullOrNotIsInstanceOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is not instance of given class-name or that the value is null.
+ * @method static bool nullOrNotNull(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not null or that the value is null.
+ * @method static bool nullOrNotRegex(mixed|null $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value does not match a regex or that the value is null.
+ * @method static bool nullOrNotSame(mixed|null $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not the same (using ===) or that the value is null.
+ * @method static bool nullOrNull(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is null or that the value is null.
+ * @method static bool nullOrNumeric(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is numeric or that the value is null.
+ * @method static bool nullOrObjectOrClass(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is an object, or a class that exists or that the value is null.
+ * @method static bool nullOrPhpVersion(string|null $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert on PHP version or that the value is null.
+ * @method static bool nullOrPropertiesExist(mixed|null $value, array $properties, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the properties all exist or that the value is null.
+ * @method static bool nullOrPropertyExists(mixed|null $value, string $property, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the property exists or that the value is null.
+ * @method static bool nullOrRange(mixed|null $value, mixed $minValue, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that value is in range of numbers or that the value is null.
+ * @method static bool nullOrReadable(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something readable or that the value is null.
+ * @method static bool nullOrRegex(mixed|null $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value matches a regex or that the value is null.
+ * @method static bool nullOrSame(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are the same (using ===) or that the value is null.
+ * @method static bool nullOrSatisfy(mixed|null $value, callable $callback, string|callable $message = null, string $propertyPath = null) Assert that the provided value is valid according to a callback or that the value is null.
+ * @method static bool nullOrScalar(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a PHP scalar or that the value is null.
+ * @method static bool nullOrStartsWith(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string starts with a sequence of chars or that the value is null.
+ * @method static bool nullOrString(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a string or that the value is null.
+ * @method static bool nullOrSubclassOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is subclass of given class-name or that the value is null.
+ * @method static bool nullOrTrue(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean True or that the value is null.
+ * @method static bool nullOrUniqueValues(array|null $values, string|callable $message = null, string $propertyPath = null) Assert that values in array are unique (using strict equality) or that the value is null.
+ * @method static bool nullOrUrl(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an URL or that the value is null.
+ * @method static bool nullOrUuid(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid UUID or that the value is null.
+ * @method static bool nullOrVersion(string|null $version1, string $operator, string $version2, string|callable $message = null, string $propertyPath = null) Assert comparison of two versions or that the value is null.
+ * @method static bool nullOrWriteable(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something writeable or that the value is null.
+ */
+ ---
+
diff --git a/tests/Printer/examples/method4.test b/tests/Printer/examples/method4.test
new file mode 100644
index 00000000..c6a80786
--- /dev/null
+++ b/tests/Printer/examples/method4.test
@@ -0,0 +1,7 @@
+/**
+ * @method bool is(string $value = null)
+ */
+---
+/**
+ * MethodNode(ScalarNode(bool),is,ParameterList(ParameterNode(VariableNode($value),ScalarNode(string),null)))
+ */
diff --git a/tests/Printer/examples/method5.test b/tests/Printer/examples/method5.test
new file mode 100644
index 00000000..ee5656aa
--- /dev/null
+++ b/tests/Printer/examples/method5.test
@@ -0,0 +1,7 @@
+/**
+ * @method static bool allAlnum(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values.
+ */
+---
+/**
+ * MethodNode(ScalarNode(bool),allAlnum,static,ParameterList(ParameterNode(VariableNode($value),ScalarNode(mixed)),ParameterNode(VariableNode($message),UnionNode(ScalarNode(string)|ScalarNode(callable)),null),ParameterNode(VariableNode($propertyPath),ScalarNode(string),null)),TextNode(Assert that value is alphanumeric for all values.))
+ */
diff --git a/tests/Printer/examples/method6.test b/tests/Printer/examples/method6.test
new file mode 100644
index 00000000..60452406
--- /dev/null
+++ b/tests/Printer/examples/method6.test
@@ -0,0 +1,7 @@
+/**
+ * @method static bool allIpv4(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address for all values.
+ */
+---
+/**
+ * MethodNode(ScalarNode(bool),allIpv4,static,ParameterList(ParameterNode(VariableNode($value),ScalarNode(string)),ParameterNode(VariableNode($flag),ScalarNode(int),null),ParameterNode(VariableNode($message),UnionNode(ScalarNode(string)|ScalarNode(callable)),null),ParameterNode(VariableNode($propertyPath),ScalarNode(string),null)),TextNode(Assert that value is an IPv4 address for all values.))
+ */
From 28df9f567340c46c90892690e8a68f8589ff8abf Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sun, 24 Jan 2021 18:55:39 +0000
Subject: [PATCH 40/63] Updated phpcs
---
.php_cs.dist | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/.php_cs.dist b/.php_cs.dist
index a3d5fa36..2820b890 100644
--- a/.php_cs.dist
+++ b/.php_cs.dist
@@ -4,7 +4,7 @@ $finder = PhpCsFixer\Finder::create()
->in('lib')
->in('tests')
->exclude([
- 'tests/Workspace'
+ 'tests/Workspace',
])
;
@@ -13,6 +13,11 @@ return PhpCsFixer\Config::create()
'@PSR2' => true,
'no_unused_imports' => true,
'array_syntax' => ['syntax' => 'short'],
+ 'void_return' => true,
+ 'ordered_class_elements' => true,
+ 'single_quote' => true,
+ 'heredoc_indentation' => true,
+ 'global_namespace_import' => true,
])
->setFinder($finder)
;
From eccad5be16f33dbda9452d6f222a681b98395fb3 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sun, 24 Jan 2021 18:56:03 +0000
Subject: [PATCH 41/63] Apply CS fixes
---
lib/Ast/MethodNode.php | 3 +-
lib/Ast/TypeNode.php | 2 -
lib/Ast/VariableNode.php | 1 -
lib/Lexer.php | 1 -
lib/Parser.php | 11 ++--
lib/Printer/TestPrinter.php | 5 +-
lib/Tokens.php | 11 ++--
tests/Benchmark/AbstractParserBenchCase.php | 17 +++--
tests/Benchmark/PhpactorParserBench.php | 1 -
tests/Benchmark/PhpstanParserBench.php | 1 -
tests/Unit/LexerTest.php | 70 ++++++++++-----------
tests/Unit/ParserTest.php | 3 -
12 files changed, 55 insertions(+), 71 deletions(-)
diff --git a/lib/Ast/MethodNode.php b/lib/Ast/MethodNode.php
index 4a35dbfe..16b7cee3 100644
--- a/lib/Ast/MethodNode.php
+++ b/lib/Ast/MethodNode.php
@@ -49,8 +49,7 @@ public function __construct(
?ParameterList $parameters,
?Token $parenClose,
?TextNode $text
- )
- {
+ ) {
$this->type = $type;
$this->name = $name;
$this->static = $static;
diff --git a/lib/Ast/TypeNode.php b/lib/Ast/TypeNode.php
index 2ca7a693..4cefe3ea 100644
--- a/lib/Ast/TypeNode.php
+++ b/lib/Ast/TypeNode.php
@@ -2,8 +2,6 @@
namespace Phpactor\Docblock\Ast;
-use Phpactor\Docblock\Token;
-
abstract class TypeNode extends Node
{
}
diff --git a/lib/Ast/VariableNode.php b/lib/Ast/VariableNode.php
index 6bcca641..9e55e617 100644
--- a/lib/Ast/VariableNode.php
+++ b/lib/Ast/VariableNode.php
@@ -4,7 +4,6 @@
use Phpactor\Docblock\Token;
-
class VariableNode extends Node
{
/**
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 308cfadc..13bfb232 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -120,5 +120,4 @@ private function resolveType(string $value, ?array $prevChunk = null): string
return Token::T_UNKNOWN;
}
-
}
diff --git a/lib/Parser.php b/lib/Parser.php
index d0b83e3f..4ed76538 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -2,7 +2,6 @@
namespace Phpactor\Docblock;
-use PhpParser\Node\NullableType;
use Phpactor\Docblock\Ast\DeprecatedNode;
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\MethodNode;
@@ -32,15 +31,14 @@
final class Parser
{
+ private const SCALAR_TYPES = [
+ 'int', 'float', 'bool', 'string', 'mixed', 'callable'
+ ];
/**
* @var Tokens
*/
private $tokens;
- private const SCALAR_TYPES = [
- 'int', 'float', 'bool', 'string', 'mixed', 'callable'
- ];
-
public function parse(Tokens $tokens): Node
{
$this->tokens = $tokens;
@@ -332,7 +330,7 @@ private function parseText(): ?TextNode
{
$text = [];
if (
- $this->tokens->current->type === Token::T_WHITESPACE &&
+ $this->tokens->current->type === Token::T_WHITESPACE &&
$this->tokens->next()->type === Token::T_LABEL
) {
$this->tokens->chomp();
@@ -372,5 +370,4 @@ private function parseValue(): ?ValueNode
return null;
}
-
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 7b95da6a..c3b6ad68 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -14,7 +14,6 @@
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
-use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Ast\ParamNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
@@ -43,7 +42,7 @@ public function print(Node $node): string
$this->render($node);
- return implode("", $this->out);
+ return implode('', $this->out);
}
private function render(?Element $node): void
@@ -340,7 +339,7 @@ private function renderParameter(ParameterNode $node): void
$this->out[] = ')';
}
- private function renderValue(ValueNode $node)
+ private function renderValue(ValueNode $node): void
{
$this->out[] = json_encode($node->value());
}
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 94814575..1876e366 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -8,15 +8,15 @@
final class Tokens implements IteratorAggregate
{
- /**
- * @var Token[]
- */
- private $tokens;
/**
* @var ?Token
*/
public $current;
+ /**
+ * @var Token[]
+ */
+ private $tokens;
/**
* @var int
@@ -75,7 +75,8 @@ public function chomp(?string $type = null): ?Token
if (null !== $type && $token->type !== $type) {
throw new RuntimeException(sprintf(
'Expected type "%s" at position "%s": "%s"',
- $type, $this->position,
+ $type,
+ $this->position,
implode('', array_map(function (Token $token) {
return $token->value;
}, $this->tokens))
diff --git a/tests/Benchmark/AbstractParserBenchCase.php b/tests/Benchmark/AbstractParserBenchCase.php
index 9f201f21..9cdb1639 100644
--- a/tests/Benchmark/AbstractParserBenchCase.php
+++ b/tests/Benchmark/AbstractParserBenchCase.php
@@ -3,8 +3,6 @@
namespace Phpactor\Docblock\Tests\Benchmark;
use Generator;
-use Phpactor\Docblock\Lexer;
-use Phpactor\Docblock\Parser;
/**
* @Iterations(33)
@@ -14,15 +12,16 @@
*/
abstract class AbstractParserBenchCase
{
+ abstract public function setUp(): void;
public function benchParse(): void
{
$doc = <<<'EOT'
-/**
- * @param Foobar $foobar
- * @var Foobar $bafoo
- * @param string $baz
- */
-EOT;
+ /**
+ * @param Foobar $foobar
+ * @var Foobar $bafoo
+ * @param string $baz
+ */
+ EOT;
$this->parse($doc);
}
@@ -51,7 +50,5 @@ public function benchAssert(): void
{
$this->parse(file_get_contents(__DIR__ . '/examples/assert.example'));
}
-
- abstract public function setUp(): void;
abstract public function parse(string $doc): void;
}
diff --git a/tests/Benchmark/PhpactorParserBench.php b/tests/Benchmark/PhpactorParserBench.php
index 94a7b4f8..f7d4cd5a 100644
--- a/tests/Benchmark/PhpactorParserBench.php
+++ b/tests/Benchmark/PhpactorParserBench.php
@@ -4,7 +4,6 @@
use Phpactor\Docblock\Lexer;
use Phpactor\Docblock\Parser;
-use Phpactor\Docblock\Tests\Benchmark\AbstractParserBenchCase;
class PhpactorParserBench extends AbstractParserBenchCase
{
diff --git a/tests/Benchmark/PhpstanParserBench.php b/tests/Benchmark/PhpstanParserBench.php
index dcf0884a..665f4718 100644
--- a/tests/Benchmark/PhpstanParserBench.php
+++ b/tests/Benchmark/PhpstanParserBench.php
@@ -7,7 +7,6 @@
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
-use Phpactor\Docblock\Tests\Benchmark\AbstractParserBenchCase;
class PhpstanParserBench extends AbstractParserBenchCase
{
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index d5ff9478..150c7333 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -34,72 +34,72 @@ public function provideLex(): Generator
yield [ '', [] ];
yield [
<<<'EOT'
-/**
- * Hello this is
- * Multi
- */
-EOT
+ /**
+ * Hello this is
+ * Multi
+ */
+ EOT
,[
- [Token::T_PHPDOC_OPEN, "/**"],
+ [Token::T_PHPDOC_OPEN, '/**'],
[Token::T_WHITESPACE, "\n "],
- [Token::T_PHPDOC_LEADING, "*"],
- [Token::T_WHITESPACE, " "],
- [Token::T_LABEL, "Hello"],
- [Token::T_WHITESPACE, " "],
- [Token::T_LABEL, "this"],
- [Token::T_WHITESPACE, " "],
- [Token::T_LABEL, "is"],
+ [Token::T_PHPDOC_LEADING, '*'],
+ [Token::T_WHITESPACE, ' '],
+ [Token::T_LABEL, 'Hello'],
+ [Token::T_WHITESPACE, ' '],
+ [Token::T_LABEL, 'this'],
+ [Token::T_WHITESPACE, ' '],
+ [Token::T_LABEL, 'is'],
[Token::T_WHITESPACE, "\n "],
- [Token::T_PHPDOC_LEADING, "*"],
- [Token::T_WHITESPACE, " "],
- [Token::T_LABEL, "Multi"],
+ [Token::T_PHPDOC_LEADING, '*'],
+ [Token::T_WHITESPACE, ' '],
+ [Token::T_LABEL, 'Multi'],
[Token::T_WHITESPACE, "\n "],
- [Token::T_PHPDOC_CLOSE, "*/"],
+ [Token::T_PHPDOC_CLOSE, '*/'],
]
];
yield [
'Foobar',
[
- [Token::T_LABEL, "Foobar"],
+ [Token::T_LABEL, 'Foobar'],
]
];
yield [
'Foobar[]',
[
- [Token::T_LABEL, "Foobar"],
- [Token::T_LIST, "[]"],
+ [Token::T_LABEL, 'Foobar'],
+ [Token::T_LIST, '[]'],
]
];
yield [
'Foobar',
[
- [Token::T_LABEL, "Foobar"],
- [Token::T_BRACKET_ANGLE_OPEN, "<"],
- [Token::T_LABEL, "Barfoo"],
- [Token::T_BRACKET_ANGLE_CLOSE, ">"],
+ [Token::T_LABEL, 'Foobar'],
+ [Token::T_BRACKET_ANGLE_OPEN, '<'],
+ [Token::T_LABEL, 'Barfoo'],
+ [Token::T_BRACKET_ANGLE_CLOSE, '>'],
]
];
yield [
'Foobar',
[
- [Token::T_LABEL, "Foobar"],
- [Token::T_BRACKET_ANGLE_OPEN, "<"],
- [Token::T_LABEL, "Barfoo"],
- [Token::T_BRACKET_ANGLE_CLOSE, ">"],
+ [Token::T_LABEL, 'Foobar'],
+ [Token::T_BRACKET_ANGLE_OPEN, '<'],
+ [Token::T_LABEL, 'Barfoo'],
+ [Token::T_BRACKET_ANGLE_CLOSE, '>'],
]
];
yield [
'Foobar{Barfoo, Foobar}',
[
- [Token::T_LABEL, "Foobar"],
- [Token::T_BRACKET_CURLY_OPEN, "{"],
- [Token::T_LABEL, "Barfoo"],
- [Token::T_COMMA, ","],
- [Token::T_WHITESPACE, " "],
- [Token::T_LABEL, "Foobar"],
- [Token::T_BRACKET_CURLY_CLOSE, "}"],
+ [Token::T_LABEL, 'Foobar'],
+ [Token::T_BRACKET_CURLY_OPEN, '{'],
+ [Token::T_LABEL, 'Barfoo'],
+ [Token::T_COMMA, ','],
+ [Token::T_WHITESPACE, ' '],
+ [Token::T_LABEL, 'Foobar'],
+ [Token::T_BRACKET_CURLY_CLOSE, '}'],
]
];
}
diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php
index dd223d7b..26949747 100644
--- a/tests/Unit/ParserTest.php
+++ b/tests/Unit/ParserTest.php
@@ -5,10 +5,7 @@
use Generator;
use PHPUnit\Framework\TestCase;
use Phpactor\Docblock\Ast\Docblock;
-use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Node;
-use Phpactor\Docblock\Ast\ParamNode;
-use Phpactor\Docblock\Ast\VariableNode;
use Phpactor\Docblock\Lexer;
use Phpactor\Docblock\Parser;
use Phpactor\Docblock\Token;
From c3ffc0576f178bf8653ad2ea76f08be73a373b94 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sun, 24 Jan 2021 21:00:59 +0000
Subject: [PATCH 42/63] Add lexer bench
---
tests/Benchmark/LexerBench.php | 55 ++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 tests/Benchmark/LexerBench.php
diff --git a/tests/Benchmark/LexerBench.php b/tests/Benchmark/LexerBench.php
new file mode 100644
index 00000000..a2726f75
--- /dev/null
+++ b/tests/Benchmark/LexerBench.php
@@ -0,0 +1,55 @@
+lex($docblock['docblock']);
+ }
+
+ public function provideDocblock(): Generator
+ {
+ yield [
+ 'docblock' => <<<'EOT'
+ /**
+ * This is some complicated method
+ * @since 5.2
+ *
+ * @param Foobar $barfoo Does a barfoo and then returns
+ * @param Barfoo $foobar Performs a foobar and then runs away.
+ *
+ * @return Baz
+ */
+ EOT
+ ];
+ yield [
+ 'docblock' => <<<'EOT'
+ /**
+ * Assert library.
+ *
+ * @author Benjamin Eberlei
+ *
+ * @method static bool allAlnum(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values.
+ * @method static bool allBase64(string $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values.
+ * @method static bool allBetween(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit for all values.
+ * @method static bool allBetweenExclusive(mixed $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit for all values.
+ * @method static bool allBetweenLength(mixed $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths for all values.
+ * @method static bool allBoolean(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean for all values.
+ * @method static bool allChoice(mixed $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices for all values.
+ * @method static bool allChoicesNotEmpty(array $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content for all values.
+ * @method static bool allClassExists(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists for all values.
+ * @method static bool allContains(mixed $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars for all values.
+ * @method static bool allCount(array|Countable|ResourceBundle|SimpleXMLElement $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count for all values.
+ * @method static bool allDate(string $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format for all values.
+ EOT
+ ];
+ }
+}
From 0bfc82138fae6745477d8d0b3fa2d02b0b461aec Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Mon, 25 Jan 2021 18:45:31 +0000
Subject: [PATCH 43/63] Adding traversal API
---
lib/Ast/DeprecatedNode.php | 4 ++
lib/Ast/Docblock.php | 15 ++++---
lib/Ast/ElementList.php | 66 +++++++++++++++++++++++++++++++
lib/Ast/MethodNode.php | 25 ++++++++----
lib/Ast/Node.php | 54 +++++++++++++++++++++++++
lib/Parser.php | 4 ++
tests/Unit/Ast/MethodNodeTest.php | 21 ++++++++++
tests/Unit/Ast/NodeTestCase.php | 29 ++++++++++++++
8 files changed, 206 insertions(+), 12 deletions(-)
create mode 100644 lib/Ast/ElementList.php
create mode 100644 tests/Unit/Ast/MethodNodeTest.php
create mode 100644 tests/Unit/Ast/NodeTestCase.php
diff --git a/lib/Ast/DeprecatedNode.php b/lib/Ast/DeprecatedNode.php
index ea2604fe..f9918262 100644
--- a/lib/Ast/DeprecatedNode.php
+++ b/lib/Ast/DeprecatedNode.php
@@ -4,6 +4,10 @@
class DeprecatedNode extends TagNode
{
+ public const CHILD_NAMES = [
+ 'text',
+ ];
+
/**
* @var TextNode
*/
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
index 1aec2f21..bf63d68c 100644
--- a/lib/Ast/Docblock.php
+++ b/lib/Ast/Docblock.php
@@ -4,23 +4,28 @@
class Docblock extends Node
{
+ protected const CHILD_NAMES = [
+ 'children'
+ ];
+
/**
- * @var Element[]
+ * @var ElementList
*/
- private $children = [];
+ public $children = [];
/**
* @param Element[] $children
*/
public function __construct(array $children)
{
- $this->children = $children;
+ $this->children = new ElementList($children);
}
/**
- * @return Element[]
+ * @return ElementList
*/
- public function children(): array
+ public function children(): ElementList
+
{
return $this->children;
}
diff --git a/lib/Ast/ElementList.php b/lib/Ast/ElementList.php
new file mode 100644
index 00000000..7e67f267
--- /dev/null
+++ b/lib/Ast/ElementList.php
@@ -0,0 +1,66 @@
+
+ */
+class ElementList extends Node implements IteratorAggregate
+{
+ protected const CHILD_NAMES = [
+ 'elements',
+ ];
+
+ /**
+ * @var T[]
+ */
+ public $elements;
+
+ /**
+ * @param T[] $elements
+ */
+ public function __construct(array $elements)
+ {
+ $this->elements = $elements;
+ }
+
+ /**
+ * @return ArrayIterator
+ */
+ public function getIterator(): Iterator
+ {
+ return new ArrayIterator($this->elements);
+ }
+
+ /**
+ * @template T
+ * @param class-string $classFqn
+ * @return ElementList
+ */
+ public function byClass(string $classFqn): ElementList
+ {
+ return new self(array_filter($this->elements, function (Element $element) use ($classFqn): bool {
+ return get_class($element) === $classFqn;
+ }));
+ }
+
+ public function byName(string $name): ElementList
+ {
+ return new self(array_filter($this->elements, function (Element $element) use ($classFqn): bool {
+ return get_class($element) === $classFqn;
+ }));
+ }
+
+ /**
+ * @return Element[]
+ */
+ public function toArray(): array
+ {
+ return $this->elements;
+ }
+}
diff --git a/lib/Ast/MethodNode.php b/lib/Ast/MethodNode.php
index 16b7cee3..84cf60a9 100644
--- a/lib/Ast/MethodNode.php
+++ b/lib/Ast/MethodNode.php
@@ -6,40 +6,51 @@
class MethodNode extends TagNode
{
+ public const CHILD_NAMES = [
+ 'type',
+ 'static',
+ 'type',
+ 'name',
+ 'parenOpen',
+ 'parameters',
+ 'parenClose',
+ 'text'
+ ];
+
/**
* @var TypeNode|null
*/
- private $type;
+ public $type;
/**
* @var Token|null
*/
- private $name;
+ public $name;
/**
* @var Token|null
*/
- private $static;
+ public $static;
/**
* @var ParameterList|null
*/
- private $parameters;
+ public $parameters;
/**
* @var TextNode|null
*/
- private $text;
+ public $text;
/**
* @var Token|null
*/
- private $parenOpen;
+ public $parenOpen;
/**
* @var Token|null
*/
- private $parenClose;
+ public $parenClose;
public function __construct(
?TypeNode $type,
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index 740956d2..4d37cf32 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -2,10 +2,64 @@
namespace Phpactor\Docblock\Ast;
+use Generator;
+use Phpactor\Docblock\Token;
+
abstract class Node implements Element
{
+ protected const CHILD_NAMES = [
+ ];
+
public function shortName(): string
{
return substr(get_class($this), strrpos(get_class($this), '\\') + 1);
}
+
+ /**
+ * @return Generator
+ */
+ public function getDescendantNodes(): Generator
+ {
+ yield $this;
+ yield from $this->walkNodes($this->getChildNodes());
+ }
+
+ /**
+ * @param iterable> $nodes
+ *
+ * @return Generator
+ */
+ private function walkNodes(iterable $nodes): Generator
+ {
+ $result = [];
+ foreach ($nodes as $child) {
+ if (is_array($child)) {
+ yield from $this->walkNodes($child);
+ continue;
+ }
+
+ if ($child instanceof Node) {
+ yield from $child->getDescendantNodes();
+ continue;
+ }
+
+ if ($child instanceof Token) {
+ yield $child;
+ continue;
+ }
+ }
+ }
+
+ /**
+ * @return Generator
+ */
+ private function getChildNodes(): Generator
+ {
+ foreach (static::CHILD_NAMES as $name) {
+ $child = $this->$name;
+ if (null !== $child) {
+ yield $child;
+ }
+ }
+ }
}
diff --git a/lib/Parser.php b/lib/Parser.php
index 4ed76538..eba6bb2d 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -328,6 +328,10 @@ private function parseReturn(): ReturnNode
private function parseText(): ?TextNode
{
+ if (!$this->tokens->current) {
+ return null;
+ }
+
$text = [];
if (
$this->tokens->current->type === Token::T_WHITESPACE &&
diff --git a/tests/Unit/Ast/MethodNodeTest.php b/tests/Unit/Ast/MethodNodeTest.php
new file mode 100644
index 00000000..98ece977
--- /dev/null
+++ b/tests/Unit/Ast/MethodNodeTest.php
@@ -0,0 +1,21 @@
+
+ */
+ public function provideNode(): Generator
+ {
+ yield [
+ '@method static Baz\Bar bar(string $boo, string $baz)',
+ function (MethodNode $methodNode) {
+ }
+ ];
+ }
+}
diff --git a/tests/Unit/Ast/NodeTestCase.php b/tests/Unit/Ast/NodeTestCase.php
new file mode 100644
index 00000000..31ec905d
--- /dev/null
+++ b/tests/Unit/Ast/NodeTestCase.php
@@ -0,0 +1,29 @@
+parse((new Lexer())->lex($doc));
+ $nodes = iterator_to_array($node->getDescendantNodes(), false);
+ self::assertIsIterable($nodes);
+ }
+
+ /**
+ * @return Generator
+ */
+ abstract public function provideNode(): Generator;
+}
From 3f6fdee3492031ababe0a34b86152978fa89cdef Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Mon, 25 Jan 2021 19:53:13 +0000
Subject: [PATCH 44/63] Added test for method node
---
lib/Ast/Element.php | 14 +++++++++
lib/Ast/MethodNode.php | 8 +++++
lib/Ast/Node.php | 49 +++++++++++++++++++++++++++++++
lib/Ast/ParameterList.php | 24 ++++++++++-----
lib/Ast/ParameterNode.php | 13 ++++++--
lib/Ast/Type/ClassNode.php | 7 ++++-
lib/Ast/Type/GenericNode.php | 15 +++++++---
lib/Ast/Type/ListNode.php | 9 ++++--
lib/Ast/Type/NullNode.php | 6 +++-
lib/Ast/Type/NullableNode.php | 9 ++++--
lib/Ast/Type/ScalarNode.php | 6 +++-
lib/Ast/Type/UnionNode.php | 6 +++-
lib/Ast/TypeNode.php | 2 ++
lib/Ast/VariableNode.php | 6 +++-
lib/Parser.php | 13 ++++++--
lib/Token.php | 18 +++++++++++-
tests/Unit/Ast/MethodNodeTest.php | 10 +++++++
tests/Unit/Ast/NodeTestCase.php | 1 +
18 files changed, 189 insertions(+), 27 deletions(-)
diff --git a/lib/Ast/Element.php b/lib/Ast/Element.php
index 94ed2a77..38369be3 100644
--- a/lib/Ast/Element.php
+++ b/lib/Ast/Element.php
@@ -4,4 +4,18 @@
interface Element
{
+ /**
+ * Return the string aggregation of all tokens in this element
+ */
+ public function toString(): string;
+
+ /**
+ * Return the start byte offset, starting at 0
+ */
+ public function start(): int;
+
+ /**
+ * Return the end byte offset, starting at 0
+ */
+ public function end(): int;
}
diff --git a/lib/Ast/MethodNode.php b/lib/Ast/MethodNode.php
index 84cf60a9..fde3a1d5 100644
--- a/lib/Ast/MethodNode.php
+++ b/lib/Ast/MethodNode.php
@@ -7,6 +7,7 @@
class MethodNode extends TagNode
{
public const CHILD_NAMES = [
+ 'tag',
'type',
'static',
'type',
@@ -52,7 +53,13 @@ class MethodNode extends TagNode
*/
public $parenClose;
+ /**
+ * @var Token|null
+ */
+ public $tag;
+
public function __construct(
+ ?Token $tag,
?TypeNode $type,
?Token $name,
?Token $static,
@@ -68,6 +75,7 @@ public function __construct(
$this->text = $text;
$this->parenOpen = $parenOpen;
$this->parenClose = $parenClose;
+ $this->tag = $tag;
}
public function name(): ?Token
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index 4d37cf32..4ea360f1 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -10,6 +10,32 @@ abstract class Node implements Element
protected const CHILD_NAMES = [
];
+ public function toString(): string
+ {
+ $parts = [];
+ foreach (static::CHILD_NAMES as $childName) {
+ $child = $this->$childName;
+
+ if (is_array($child)) {
+ $parts = array_merge(array_map(function (Element $element) {
+ return $element->toString();
+ }, $child));
+ continue;
+ }
+
+ if ($child instanceof Token) {
+ $parts[] = $child->value;
+ continue;
+ }
+ if ($child instanceof Node) {
+ $parts[] = $child->toString();
+ continue;
+ }
+ }
+
+ return implode(' ', $parts);
+ }
+
public function shortName(): string
{
return substr(get_class($this), strrpos(get_class($this), '\\') + 1);
@@ -62,4 +88,27 @@ private function getChildNodes(): Generator
}
}
}
+
+ public function start(): int
+ {
+ $first = $this->getChildNodes()->current();
+ if (null === $first) {
+ return 0;
+ }
+
+ return $first->start();
+ }
+
+ public function end(): int
+ {
+ foreach (array_reverse(static::CHILD_NAMES) as $childName) {
+ $element = $this->$childName;
+ if (null === $element) {
+ continue;
+ }
+
+ return $element->end();
+ }
+ return 0;
+ }
}
diff --git a/lib/Ast/ParameterList.php b/lib/Ast/ParameterList.php
index 1c89f370..f684cdd9 100644
--- a/lib/Ast/ParameterList.php
+++ b/lib/Ast/ParameterList.php
@@ -9,19 +9,22 @@
/**
* @implements IteratorAggregate
*/
-class ParameterList implements IteratorAggregate, Countable
+class ParameterList extends Node implements IteratorAggregate, Countable
{
+ protected const CHILD_NAMES = [
+ 'list'
+ ];
/**
* @var ParameterNode[]
*/
- private $parameterList;
+ public $list;
/**
- * @param ParameterNode[] $parameterList
+ * @param ParameterNode[] $list
*/
- public function __construct(array $parameterList)
+ public function __construct(array $list)
{
- $this->parameterList = $parameterList;
+ $this->list = $list;
}
/**
@@ -29,7 +32,7 @@ public function __construct(array $parameterList)
*/
public function getIterator(): ArrayIterator
{
- return new ArrayIterator($this->parameterList);
+ return new ArrayIterator($this->list);
}
/**
@@ -37,6 +40,13 @@ public function getIterator(): ArrayIterator
*/
public function count()
{
- return count($this->parameterList);
+ return count($this->list);
+ }
+
+ public function toString(): string
+ {
+ return implode(', ', array_map(function (Element $element) {
+ return $element->toString();
+ }, $this->list));
}
}
diff --git a/lib/Ast/ParameterNode.php b/lib/Ast/ParameterNode.php
index ee2c49da..8b4ed04b 100644
--- a/lib/Ast/ParameterNode.php
+++ b/lib/Ast/ParameterNode.php
@@ -4,19 +4,26 @@
class ParameterNode extends Node
{
+ protected const CHILD_NAMES = [
+ 'type',
+ 'name',
+ 'default',
+ ];
+
/**
* @var TypeNode|null
*/
- private $type;
+ public $type;
+
/**
* @var VariableNode|null
*/
- private $name;
+ public $name;
/**
* @var ValueNode|null
*/
- private $default;
+ public $default;
public function __construct(?TypeNode $type, ?VariableNode $name, ?ValueNode $default)
{
diff --git a/lib/Ast/Type/ClassNode.php b/lib/Ast/Type/ClassNode.php
index da21bdba..6d9b1382 100644
--- a/lib/Ast/Type/ClassNode.php
+++ b/lib/Ast/Type/ClassNode.php
@@ -7,10 +7,15 @@
class ClassNode extends TypeNode
{
+ protected const CHILD_NAMES = [
+ 'name'
+ ];
+
+
/**
* @var Token
*/
- private $name;
+ public $name;
public function __construct(Token $name)
{
diff --git a/lib/Ast/Type/GenericNode.php b/lib/Ast/Type/GenericNode.php
index 66fd2bf2..b84e3158 100644
--- a/lib/Ast/Type/GenericNode.php
+++ b/lib/Ast/Type/GenericNode.php
@@ -8,25 +8,32 @@
class GenericNode extends TypeNode
{
+ protected const CHILD_NAMES = [
+ 'open',
+ 'type',
+ 'open',
+ 'parameters',
+ 'close'
+ ];
/**
* @var Token
*/
- private $open;
+ public $open;
/**
* @var Token
*/
- private $close;
+ public $close;
/**
* @var TypeList
*/
- private $parameters;
+ public $parameters;
/**
* @var TypeNode
*/
- private $type;
+ public $type;
public function __construct(Token $open, TypeNode $type, TypeList $parameters, Token $close)
{
diff --git a/lib/Ast/Type/ListNode.php b/lib/Ast/Type/ListNode.php
index aef076f2..f7a35f92 100644
--- a/lib/Ast/Type/ListNode.php
+++ b/lib/Ast/Type/ListNode.php
@@ -7,15 +7,20 @@
class ListNode extends TypeNode
{
+ protected const CHILD_NAMES = [
+ 'type',
+ 'listChars',
+ ];
+
/**
* @var TypeNode
*/
- private $type;
+ public $type;
/**
* @var Token
*/
- private $listChars;
+ public $listChars;
public function __construct(TypeNode $type, Token $listChars)
{
diff --git a/lib/Ast/Type/NullNode.php b/lib/Ast/Type/NullNode.php
index 0802c711..370cca45 100644
--- a/lib/Ast/Type/NullNode.php
+++ b/lib/Ast/Type/NullNode.php
@@ -7,10 +7,14 @@
class NullNode extends TypeNode
{
+ protected const CHILD_NAMES = [
+ 'null',
+ ];
+
/**
* @var Token
*/
- private $null;
+ public $null;
public function __construct(Token $null)
{
diff --git a/lib/Ast/Type/NullableNode.php b/lib/Ast/Type/NullableNode.php
index ec6ce250..795f867f 100644
--- a/lib/Ast/Type/NullableNode.php
+++ b/lib/Ast/Type/NullableNode.php
@@ -7,15 +7,20 @@
class NullableNode extends TypeNode
{
+ protected const CHILD_NAMES = [
+ 'nullable',
+ 'type',
+ ];
+
/**
* @var Token
*/
- private $nullable;
+ public $nullable;
/**
* @var TypeNode
*/
- private $type;
+ public $type;
public function __construct(Token $nullable, TypeNode $type)
{
diff --git a/lib/Ast/Type/ScalarNode.php b/lib/Ast/Type/ScalarNode.php
index f7e41397..d83fc6db 100644
--- a/lib/Ast/Type/ScalarNode.php
+++ b/lib/Ast/Type/ScalarNode.php
@@ -7,10 +7,14 @@
class ScalarNode extends TypeNode
{
+ protected const CHILD_NAMES = [
+ 'name',
+ ];
+
/**
* @var Token
*/
- private $name;
+ public $name;
public function __construct(Token $name)
{
diff --git a/lib/Ast/Type/UnionNode.php b/lib/Ast/Type/UnionNode.php
index 4d9946bf..5cba708b 100644
--- a/lib/Ast/Type/UnionNode.php
+++ b/lib/Ast/Type/UnionNode.php
@@ -7,10 +7,14 @@
class UnionNode extends TypeNode
{
+ protected const CHILD_NAMES = [
+ 'types',
+ ];
+
/**
* @var TypeList
*/
- private $types;
+ public $types;
public function __construct(TypeList $types)
{
diff --git a/lib/Ast/TypeNode.php b/lib/Ast/TypeNode.php
index 4cefe3ea..2ca7a693 100644
--- a/lib/Ast/TypeNode.php
+++ b/lib/Ast/TypeNode.php
@@ -2,6 +2,8 @@
namespace Phpactor\Docblock\Ast;
+use Phpactor\Docblock\Token;
+
abstract class TypeNode extends Node
{
}
diff --git a/lib/Ast/VariableNode.php b/lib/Ast/VariableNode.php
index 9e55e617..5049ac5f 100644
--- a/lib/Ast/VariableNode.php
+++ b/lib/Ast/VariableNode.php
@@ -6,10 +6,14 @@
class VariableNode extends Node
{
+ protected const CHILD_NAMES = [
+ 'name'
+ ];
+
/**
* @var Token
*/
- private $name;
+ public $name;
public function __construct(Token $name)
{
diff --git a/lib/Parser.php b/lib/Parser.php
index eba6bb2d..f0a7482f 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -41,8 +41,8 @@ final class Parser
public function parse(Tokens $tokens): Node
{
- $this->tokens = $tokens;
$children = [];
+ $this->tokens = $tokens;
while ($tokens->hasCurrent()) {
if ($tokens->current->type === Token::T_TAG) {
@@ -52,6 +52,13 @@ public function parse(Tokens $tokens): Node
$children[] = $tokens->chomp();
}
+ if (count($children) === 1) {
+ $node = reset($children);
+ if ($node instanceof Node) {
+ return $node;
+ }
+ }
+
return new Docblock($children);
}
@@ -116,7 +123,7 @@ private function parseVar(): VarNode
private function parseMethod(): MethodNode
{
- $this->tokens->chomp(Token::T_TAG);
+ $tag = $this->tokens->chomp(Token::T_TAG);
$type = $name = $parameterList = $open = $close = null;
$static = null;
@@ -140,7 +147,7 @@ private function parseMethod(): MethodNode
$close = $this->tokens->chompIf(Token::T_PAREN_CLOSE);
}
- return new MethodNode($type, $name, $static, $open, $parameterList, $close, $this->parseText());
+ return new MethodNode($tag, $type, $name, $static, $open, $parameterList, $close, $this->parseText());
}
private function parseProperty(): PropertyNode
diff --git a/lib/Token.php b/lib/Token.php
index 9c288387..23851fde 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -50,8 +50,24 @@ public function __construct(int $byteOffset, string $type, string $value)
$this->value = $value;
}
- public function __toString(): string
+ public function toString(): string
{
return $this->value;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public function start(): int
+ {
+ return $this->byteOffset;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function end(): int
+ {
+ return $this->byteOffset + strlen($this->value);
+ }
}
diff --git a/tests/Unit/Ast/MethodNodeTest.php b/tests/Unit/Ast/MethodNodeTest.php
index 98ece977..123bfc0d 100644
--- a/tests/Unit/Ast/MethodNodeTest.php
+++ b/tests/Unit/Ast/MethodNodeTest.php
@@ -4,6 +4,7 @@
use Generator;
use PHPUnit\Framework\TestCase;
+use Phpactor\Docblock\Ast\MethodNode;
class MethodNodeTest extends NodeTestCase
{
@@ -15,6 +16,15 @@ public function provideNode(): Generator
yield [
'@method static Baz\Bar bar(string $boo, string $baz)',
function (MethodNode $methodNode) {
+ self::assertEquals('static', $methodNode->static->value);
+ self::assertEquals('Baz\Bar', $methodNode->type->toString());
+ self::assertEquals('bar', $methodNode->name->toString());
+ self::assertEquals('(', $methodNode->parenOpen->toString());
+ self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
+ self::assertEquals(')', $methodNode->parenClose->toString());
+ self::assertEquals(0, $methodNode->start());
+ self::assertEquals(52, $methodNode->end());
+ self::assertEquals('@method Baz\Bar static Baz\Bar bar ( string $boo, string $baz )', $methodNode->toString());
}
];
}
diff --git a/tests/Unit/Ast/NodeTestCase.php b/tests/Unit/Ast/NodeTestCase.php
index 31ec905d..2ca502ea 100644
--- a/tests/Unit/Ast/NodeTestCase.php
+++ b/tests/Unit/Ast/NodeTestCase.php
@@ -20,6 +20,7 @@ public function testNode(string $doc, ?Closure $assertion = null): void
$node = (new Parser())->parse((new Lexer())->lex($doc));
$nodes = iterator_to_array($node->getDescendantNodes(), false);
self::assertIsIterable($nodes);
+ $assertion($node);
}
/**
From 9d1e7117b060ec706a76a2d5a36abe4744ced08d Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Mon, 25 Jan 2021 20:45:35 +0000
Subject: [PATCH 45/63] Aiming towards 100% isomorphism
---
lib/Ast/MethodNode.php | 1 -
lib/Ast/Node.php | 47 +++++++++++++++++++------------
lib/Ast/ParameterList.php | 8 +-----
lib/Parser.php | 13 ++++++++-
lib/Token.php | 1 +
lib/Tokens.php | 12 ++++++--
tests/Unit/Ast/MethodNodeTest.php | 4 +--
tests/Unit/Ast/NodeTestCase.php | 36 ++++++++++++++++++++++-
8 files changed, 88 insertions(+), 34 deletions(-)
diff --git a/lib/Ast/MethodNode.php b/lib/Ast/MethodNode.php
index fde3a1d5..0569adc4 100644
--- a/lib/Ast/MethodNode.php
+++ b/lib/Ast/MethodNode.php
@@ -8,7 +8,6 @@ class MethodNode extends TagNode
{
public const CHILD_NAMES = [
'tag',
- 'type',
'static',
'type',
'name',
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index 4ea360f1..fc65d554 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -12,28 +12,39 @@ abstract class Node implements Element
public function toString(): string
{
- $parts = [];
- foreach (static::CHILD_NAMES as $childName) {
- $child = $this->$childName;
+ return implode(' ', array_map(function (Token $token) {
+ return $token->value;
+ }, iterator_to_array($this->tokens(), false)));
+ }
- if (is_array($child)) {
- $parts = array_merge(array_map(function (Element $element) {
- return $element->toString();
- }, $child));
+ /**
+ * @return Generator
+ */
+ public function tokens(): Generator
+ {
+ yield from $this->findTokens($this->getChildElements());
+ }
+
+ /**
+ * @return Generator
+ * @param iterable> $nodes
+ */
+ private function findTokens(iterable $nodes): Generator
+ {
+ foreach ($nodes as $node) {
+ if ($node instanceof Token) {
+ yield $node;
continue;
}
- if ($child instanceof Token) {
- $parts[] = $child->value;
- continue;
+ if ($node instanceof Node) {
+ yield from $node->tokens();
}
- if ($child instanceof Node) {
- $parts[] = $child->toString();
- continue;
+
+ if (is_array($node)) {
+ yield from $this->findTokens($node);
}
}
-
- return implode(' ', $parts);
}
public function shortName(): string
@@ -47,7 +58,7 @@ public function shortName(): string
public function getDescendantNodes(): Generator
{
yield $this;
- yield from $this->walkNodes($this->getChildNodes());
+ yield from $this->walkNodes($this->getChildElements());
}
/**
@@ -79,7 +90,7 @@ private function walkNodes(iterable $nodes): Generator
/**
* @return Generator
*/
- private function getChildNodes(): Generator
+ public function getChildElements(): Generator
{
foreach (static::CHILD_NAMES as $name) {
$child = $this->$name;
@@ -91,7 +102,7 @@ private function getChildNodes(): Generator
public function start(): int
{
- $first = $this->getChildNodes()->current();
+ $first = $this->getChildElements()->current();
if (null === $first) {
return 0;
}
diff --git a/lib/Ast/ParameterList.php b/lib/Ast/ParameterList.php
index f684cdd9..f01f2f6e 100644
--- a/lib/Ast/ParameterList.php
+++ b/lib/Ast/ParameterList.php
@@ -14,6 +14,7 @@ class ParameterList extends Node implements IteratorAggregate, Countable
protected const CHILD_NAMES = [
'list'
];
+
/**
* @var ParameterNode[]
*/
@@ -42,11 +43,4 @@ public function count()
{
return count($this->list);
}
-
- public function toString(): string
- {
- return implode(', ', array_map(function (Element $element) {
- return $element->toString();
- }, $this->list));
- }
}
diff --git a/lib/Parser.php b/lib/Parser.php
index f0a7482f..9585e184 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -100,6 +100,7 @@ private function parseParam(): ParamNode
if ($this->ifType()) {
$type = $this->parseTypes();
}
+
if ($this->tokens->ifNextIs(Token::T_VARIABLE)) {
$variable = $this->parseVariable();
}
@@ -189,14 +190,24 @@ private function parseTypes(): ?TypeNode
return new UnionNode(new TypeList($types));
}
+
private function parseType(): ?TypeNode
{
+ if (null === $this->tokens->current) {
+ return null;
+ }
+
if ($this->tokens->current->type === Token::T_NULLABLE) {
$nullable = $this->tokens->chomp();
return new NullableNode($nullable, $this->parseTypes());
}
$type = $this->tokens->chomp(Token::T_LABEL);
+
+ if (null === $this->tokens->current) {
+ return null;
+ }
+
$isList = false;
if ($this->tokens->current->type === Token::T_LIST) {
@@ -275,7 +286,7 @@ private function parseParameterList(): ?ParameterList
while (true) {
$parameters[] = $this->parseParameter();
if ($this->tokens->if(Token::T_COMMA)) {
- $this->tokens->chomp();
+ $parameters[] = $this->tokens->chomp();
continue;
}
break;
diff --git a/lib/Token.php b/lib/Token.php
index 23851fde..368deb02 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -27,6 +27,7 @@ final class Token implements Element
public const T_BRACKET_CURLY_CLOSE = 'BRACKET_CURLY_CLOSE';
public const T_PAREN_OPEN = 'PAREN_OPEN';
public const T_PAREN_CLOSE = 'PAREN_CLOSE';
+ public const T_INVALID = 'INVALID';
/**
* @var int
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 1876e366..7b3b8427 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -8,11 +8,11 @@
final class Tokens implements IteratorAggregate
{
-
/**
* @var ?Token
*/
public $current;
+
/**
* @var Token[]
*/
@@ -100,7 +100,8 @@ public function chompIf(string $type): ?Token
public function ifNextIs(string $type): bool
{
- if ($this->next()->type === $type) {
+ $next = $this->next();
+ if ($next && $next->type === $type) {
$this->current = @$this->tokens[++$this->position];
return true;
}
@@ -114,6 +115,10 @@ public function ifNextIs(string $type): bool
*/
public function if(string $type): bool
{
+ if (null === $this->current) {
+ return false;
+ }
+
if ($this->current->type === $type) {
return true;
}
@@ -122,7 +127,8 @@ public function if(string $type): bool
return false;
}
- if ($this->next()->type === $type) {
+ $next = $this->next();
+ if ($next && $this->next()->type === $type) {
$this->current = $this->tokens[++$this->position];
return true;
}
diff --git a/tests/Unit/Ast/MethodNodeTest.php b/tests/Unit/Ast/MethodNodeTest.php
index 123bfc0d..7b59651a 100644
--- a/tests/Unit/Ast/MethodNodeTest.php
+++ b/tests/Unit/Ast/MethodNodeTest.php
@@ -22,9 +22,7 @@ function (MethodNode $methodNode) {
self::assertEquals('(', $methodNode->parenOpen->toString());
self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
self::assertEquals(')', $methodNode->parenClose->toString());
- self::assertEquals(0, $methodNode->start());
- self::assertEquals(52, $methodNode->end());
- self::assertEquals('@method Baz\Bar static Baz\Bar bar ( string $boo, string $baz )', $methodNode->toString());
+ self::assertEquals('@method static Baz\Bar bar ( string $boo, string $baz )', $methodNode->toString());
}
];
}
diff --git a/tests/Unit/Ast/NodeTestCase.php b/tests/Unit/Ast/NodeTestCase.php
index 2ca502ea..001ed0b6 100644
--- a/tests/Unit/Ast/NodeTestCase.php
+++ b/tests/Unit/Ast/NodeTestCase.php
@@ -5,6 +5,7 @@
use Closure;
use Generator;
use PHPUnit\Framework\TestCase;
+use Phpactor\Docblock\Ast\Element;
use Phpactor\Docblock\Ast\ElementList;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Lexer;
@@ -17,14 +18,47 @@ abstract class NodeTestCase extends TestCase
*/
public function testNode(string $doc, ?Closure $assertion = null): void
{
- $node = (new Parser())->parse((new Lexer())->lex($doc));
+ $node = $this->parse($doc);
$nodes = iterator_to_array($node->getDescendantNodes(), false);
self::assertIsIterable($nodes);
+ self::assertEquals(0, $node->start());
+ self::assertEquals(strlen($doc), $node->end());
+
$assertion($node);
}
+ /**
+ * @dataProvider provideNode
+ */
+ public function testPartialParse(string $doc): void
+ {
+ $node = $this->parse($doc);
+ $partial = [];
+ foreach ($node->getChildElements() as $child) {
+ $partial[] = $child->toString();
+ $node = $this->parse(implode(' ', $partial));
+ self::assertInstanceOf(Element::class, $node);
+ }
+ }
+
+ /**
+ * @dataProvider provideNode
+ */
+ public function testIsomorphism(string $doc): void
+ {
+ $one = $this->parse($doc);
+ $two = $this->parse($one->toString());
+ self::assertEquals($one, $two);
+ }
+
/**
* @return Generator
*/
abstract public function provideNode(): Generator;
+
+ private function parse(string $doc): Node
+ {
+ $node = (new Parser())->parse((new Lexer())->lex($doc));
+ return $node;
+ }
}
From 104e91f95c0437b42ec87a3f30ca8a03ddb6dff3 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Mon, 25 Jan 2021 21:16:17 +0000
Subject: [PATCH 46/63] Method node is isomorphic
---
lib/Ast/Node.php | 52 +++++++++++++++++++++++++------
lib/Printer/TestPrinter.php | 3 --
lib/Token.php | 5 +++
lib/Tokens.php | 4 +++
tests/Unit/Ast/MethodNodeTest.php | 4 +--
5 files changed, 54 insertions(+), 14 deletions(-)
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index fc65d554..d488ff0c 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -12,9 +12,13 @@ abstract class Node implements Element
public function toString(): string
{
- return implode(' ', array_map(function (Token $token) {
- return $token->value;
- }, iterator_to_array($this->tokens(), false)));
+ $out = str_repeat(' ', $this->length());;
+ $start = $this->start();
+ foreach ($this->tokens() as $token) {
+ $out = substr_replace($out, $token->value, $token->start() - $start, $token->length());
+ }
+
+ return $out;
}
/**
@@ -102,24 +106,54 @@ public function getChildElements(): Generator
public function start(): int
{
- $first = $this->getChildElements()->current();
- if (null === $first) {
- return 0;
+ return $this->startOf($this->getChildElements());
+ }
+
+ /**
+ * @param iterable> $elements
+ */
+ public function startOf(iterable $elements): int
+ {
+ foreach ($elements as $element) {
+ if ($element instanceof Element) {
+ return $element->start();
+ }
+ if (is_array($element)) {
+ return $this->startOf($element);
+ }
}
- return $first->start();
+ return 0;
}
public function end(): int
{
- foreach (array_reverse(static::CHILD_NAMES) as $childName) {
- $element = $this->$childName;
+ return $this->endOf(array_reverse(iterator_to_array($this->getChildElements(), false)));
+ }
+
+ /**
+ * @param iterable> $elements
+ */
+ private function endOf(iterable $elements): int
+ {
+ foreach ($elements as $element) {
+
if (null === $element) {
continue;
}
+ if (is_array($element)) {
+ return $this->endOf(array_reverse($element));
+ }
+
return $element->end();
}
+
return 0;
}
+
+ private function length(): int
+ {
+ return $this->end() - $this->start();
+ }
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index c3b6ad68..f68553db 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -315,9 +315,6 @@ private function renderParameterList(ParameterList $list): void
$this->out[] = 'ParameterList(';
foreach ($list as $i => $parameter) {
$this->render($parameter);
- if ($i + 1 !== $list->count()) {
- $this->out[] = ',';
- }
}
$this->out[] = ')';
}
diff --git a/lib/Token.php b/lib/Token.php
index 368deb02..d0ecb32b 100644
--- a/lib/Token.php
+++ b/lib/Token.php
@@ -71,4 +71,9 @@ public function end(): int
{
return $this->byteOffset + strlen($this->value);
}
+
+ public function length(): int
+ {
+ return $this->end() - $this->start();
+ }
}
diff --git a/lib/Tokens.php b/lib/Tokens.php
index 7b3b8427..b6159194 100644
--- a/lib/Tokens.php
+++ b/lib/Tokens.php
@@ -91,6 +91,10 @@ public function chomp(?string $type = null): ?Token
*/
public function chompIf(string $type): ?Token
{
+ if ($this->current === null) {
+ return null;
+ }
+
if ($this->current->type === $type) {
return $this->chomp($type);
}
diff --git a/tests/Unit/Ast/MethodNodeTest.php b/tests/Unit/Ast/MethodNodeTest.php
index 7b59651a..68d9371f 100644
--- a/tests/Unit/Ast/MethodNodeTest.php
+++ b/tests/Unit/Ast/MethodNodeTest.php
@@ -16,13 +16,13 @@ public function provideNode(): Generator
yield [
'@method static Baz\Bar bar(string $boo, string $baz)',
function (MethodNode $methodNode) {
+ self::assertEquals('@method static Baz\Bar bar(string $boo, string $baz)', $methodNode->toString());
+ self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
self::assertEquals('static', $methodNode->static->value);
self::assertEquals('Baz\Bar', $methodNode->type->toString());
self::assertEquals('bar', $methodNode->name->toString());
self::assertEquals('(', $methodNode->parenOpen->toString());
- self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
self::assertEquals(')', $methodNode->parenClose->toString());
- self::assertEquals('@method static Baz\Bar bar ( string $boo, string $baz )', $methodNode->toString());
}
];
}
From 0baa83b4c0e3c9c36a9d455da3ea55e9f521ac04 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Mon, 25 Jan 2021 21:25:35 +0000
Subject: [PATCH 47/63] Param node
---
lib/Ast/ParamNode.php | 23 +++++++++++++++++++----
lib/Parser.php | 4 ++--
tests/Unit/Ast/NodeTest.php | 24 ++++++++++++++++++++++++
3 files changed, 45 insertions(+), 6 deletions(-)
create mode 100644 tests/Unit/Ast/NodeTest.php
diff --git a/lib/Ast/ParamNode.php b/lib/Ast/ParamNode.php
index 0ed5dade..62261274 100644
--- a/lib/Ast/ParamNode.php
+++ b/lib/Ast/ParamNode.php
@@ -2,28 +2,43 @@
namespace Phpactor\Docblock\Ast;
+use Phpactor\Docblock\Token;
+
class ParamNode extends TagNode
{
+ protected const CHILD_NAMES = [
+ 'tag',
+ 'type',
+ 'variable',
+ 'text',
+ ];
+
/**
* @var ?TypeNode
*/
- private $type;
+ public $type;
/**
* @var ?VariableNode
*/
- private $variable;
+ public $variable;
/**
* @var TextNode|null
*/
- private $text;
+ public $text;
+
+ /**
+ * @var Token
+ */
+ public $tag;
- public function __construct(?TypeNode $type, ?VariableNode $variable, ?TextNode $text = null)
+ public function __construct(Token $tag, ?TypeNode $type, ?VariableNode $variable, ?TextNode $text = null)
{
$this->type = $type;
$this->variable = $variable;
$this->text = $text;
+ $this->tag = $tag;
}
public function type(): ?TypeNode
diff --git a/lib/Parser.php b/lib/Parser.php
index 9585e184..638fe767 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -95,7 +95,7 @@ private function parseTag(): TagNode
private function parseParam(): ParamNode
{
$type = $variable = $textNode = null;
- $this->tokens->chomp(Token::T_TAG);
+ $tag = $this->tokens->chomp(Token::T_TAG);
if ($this->ifType()) {
$type = $this->parseTypes();
@@ -105,7 +105,7 @@ private function parseParam(): ParamNode
$variable = $this->parseVariable();
}
- return new ParamNode($type, $variable, $this->parseText());
+ return new ParamNode($tag, $type, $variable, $this->parseText());
}
private function parseVar(): VarNode
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
new file mode 100644
index 00000000..89d6d2cd
--- /dev/null
+++ b/tests/Unit/Ast/NodeTest.php
@@ -0,0 +1,24 @@
+
+ */
+ public function provideNode(): Generator
+ {
+ yield [
+ '@param Baz\Bar $foobar',
+ function (ParamNode $methodNode) {
+ }
+ ];
+ }
+}
From 5cd1571e2bc0aa5b080c65b197fef3f4d0644100 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Mon, 25 Jan 2021 21:42:41 +0000
Subject: [PATCH 48/63] Return node
---
lib/Ast/Node.php | 2 +-
lib/Ast/ReturnNode.php | 20 ++++++++++++--
lib/Ast/Type/ClassNode.php | 1 -
lib/Ast/VarNode.php | 19 +++++++++++--
lib/Parser.php | 10 +++----
tests/Unit/Ast/MethodNodeTest.php | 29 -------------------
tests/Unit/Ast/NodeTest.php | 46 +++++++++++++++++++++++++++++--
tests/Unit/Ast/NodeTestCase.php | 17 ++++--------
8 files changed, 89 insertions(+), 55 deletions(-)
delete mode 100644 tests/Unit/Ast/MethodNodeTest.php
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index d488ff0c..21e30298 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -132,7 +132,7 @@ public function end(): int
}
/**
- * @param iterable> $elements
+ * @param iterable> $elements
*/
private function endOf(iterable $elements): int
{
diff --git a/lib/Ast/ReturnNode.php b/lib/Ast/ReturnNode.php
index f6dea30a..c90de1d2 100644
--- a/lib/Ast/ReturnNode.php
+++ b/lib/Ast/ReturnNode.php
@@ -2,22 +2,36 @@
namespace Phpactor\Docblock\Ast;
+use Phpactor\Docblock\Token;
+
class ReturnNode extends TagNode
{
+ protected const CHILD_NAMES = [
+ 'tag',
+ 'type',
+ 'text',
+ ];
+
/**
* @var TypeNode|null
*/
- private $type;
+ public $type;
/**
* @var TextNode|null
*/
- private $text;
+ public $text;
+
+ /**
+ * @var Token
+ */
+ public $tag;
- public function __construct(?TypeNode $type, ?TextNode $text = null)
+ public function __construct(Token $tag, ?TypeNode $type, ?TextNode $text = null)
{
$this->type = $type;
$this->text = $text;
+ $this->tag = $tag;
}
public function type(): ?TypeNode
diff --git a/lib/Ast/Type/ClassNode.php b/lib/Ast/Type/ClassNode.php
index 6d9b1382..c3cd5072 100644
--- a/lib/Ast/Type/ClassNode.php
+++ b/lib/Ast/Type/ClassNode.php
@@ -11,7 +11,6 @@ class ClassNode extends TypeNode
'name'
];
-
/**
* @var Token
*/
diff --git a/lib/Ast/VarNode.php b/lib/Ast/VarNode.php
index aa25ee69..56d0c5b8 100644
--- a/lib/Ast/VarNode.php
+++ b/lib/Ast/VarNode.php
@@ -2,22 +2,35 @@
namespace Phpactor\Docblock\Ast;
+use Phpactor\Docblock\Token;
+
class VarNode extends TagNode
{
+ protected const CHILD_NAMES = [
+ 'tag',
+ 'type',
+ 'variable',
+ ];
/**
* @var ?TypeNode
*/
- private $type;
+ public $type;
/**
* @var ?VariableNode
*/
- private $variable;
+ public $variable;
+
+ /**
+ * @var Token
+ */
+ public $tag;
- public function __construct(?TypeNode $type, ?VariableNode $variable)
+ public function __construct(Token $tag, ?TypeNode $type, ?VariableNode $variable)
{
$this->type = $type;
$this->variable = $variable;
+ $this->tag = $tag;
}
public function type(): ?TypeNode
diff --git a/lib/Parser.php b/lib/Parser.php
index 638fe767..d8d9c4c8 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -110,7 +110,7 @@ private function parseParam(): ParamNode
private function parseVar(): VarNode
{
- $this->tokens->chomp(Token::T_TAG);
+ $tag = $this->tokens->chomp(Token::T_TAG);
$type = $variable = null;
if ($this->ifType()) {
$type = $this->parseTypes();
@@ -119,7 +119,7 @@ private function parseVar(): VarNode
$variable = $this->parseVariable();
}
- return new VarNode($type, $variable);
+ return new VarNode($tag, $type, $variable);
}
private function parseMethod(): MethodNode
@@ -205,7 +205,7 @@ private function parseType(): ?TypeNode
$type = $this->tokens->chomp(Token::T_LABEL);
if (null === $this->tokens->current) {
- return null;
+ return $this->createTypeFromToken($type);
}
$isList = false;
@@ -334,14 +334,14 @@ private function parseMixin(): MixinNode
private function parseReturn(): ReturnNode
{
- $this->tokens->chomp();
+ $tag = $this->tokens->chomp(Token::T_TAG);
$type = null;
if ($this->tokens->if(Token::T_LABEL)) {
$type = $this->parseTypes();
}
- return new ReturnNode($type, $this->parseText());
+ return new ReturnNode($tag, $type, $this->parseText());
}
private function parseText(): ?TextNode
diff --git a/tests/Unit/Ast/MethodNodeTest.php b/tests/Unit/Ast/MethodNodeTest.php
deleted file mode 100644
index 68d9371f..00000000
--- a/tests/Unit/Ast/MethodNodeTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
- */
- public function provideNode(): Generator
- {
- yield [
- '@method static Baz\Bar bar(string $boo, string $baz)',
- function (MethodNode $methodNode) {
- self::assertEquals('@method static Baz\Bar bar(string $boo, string $baz)', $methodNode->toString());
- self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
- self::assertEquals('static', $methodNode->static->value);
- self::assertEquals('Baz\Bar', $methodNode->type->toString());
- self::assertEquals('bar', $methodNode->name->toString());
- self::assertEquals('(', $methodNode->parenOpen->toString());
- self::assertEquals(')', $methodNode->parenClose->toString());
- }
- ];
- }
-}
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index 89d6d2cd..4aefdbad 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -14,11 +14,53 @@ class NodeTest extends NodeTestCase
* @return Generator
*/
public function provideNode(): Generator
+ {
+ yield from $this->methodTag();
+ yield from $this->paramTag();
+ yield from $this->varTag();
+ yield from $this->returnTag();
+ }
+
+ /**
+ * @return Generator
+ */
+ private function methodTag(): Generator
{
yield [
- '@param Baz\Bar $foobar',
- function (ParamNode $methodNode) {
+ '@method static Baz\Bar bar(string $boo, string $baz)',
+ function (MethodNode $methodNode) {
+ self::assertEquals('@method static Baz\Bar bar(string $boo, string $baz)', $methodNode->toString());
+ self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
+ self::assertEquals('static', $methodNode->static->value);
+ self::assertEquals('Baz\Bar', $methodNode->type->toString());
+ self::assertEquals('bar', $methodNode->name->toString());
+ self::assertEquals('(', $methodNode->parenOpen->toString());
+ self::assertEquals(')', $methodNode->parenClose->toString());
}
];
}
+
+ /**
+ * @return Generator
+ */
+ private function paramTag(): Generator
+ {
+ yield ['@param Baz\Bar $foobar'];
+ }
+
+ /**
+ * @return Generator
+ */
+ private function varTag(): Generator
+ {
+ yield ['@var Baz\Bar $foobar'];
+ }
+
+ /**
+ * @return Generator
+ */
+ private function returnTag(): Generator
+ {
+ yield ['@return Baz\Bar'];
+ }
}
diff --git a/tests/Unit/Ast/NodeTestCase.php b/tests/Unit/Ast/NodeTestCase.php
index 001ed0b6..dbd9fb1a 100644
--- a/tests/Unit/Ast/NodeTestCase.php
+++ b/tests/Unit/Ast/NodeTestCase.php
@@ -3,15 +3,13 @@
namespace Phpactor\Docblock\Tests\Unit\Ast;
use Closure;
-use Generator;
use PHPUnit\Framework\TestCase;
use Phpactor\Docblock\Ast\Element;
-use Phpactor\Docblock\Ast\ElementList;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Lexer;
use Phpactor\Docblock\Parser;
-abstract class NodeTestCase extends TestCase
+class NodeTestCase extends TestCase
{
/**
* @dataProvider provideNode
@@ -21,10 +19,12 @@ public function testNode(string $doc, ?Closure $assertion = null): void
$node = $this->parse($doc);
$nodes = iterator_to_array($node->getDescendantNodes(), false);
self::assertIsIterable($nodes);
- self::assertEquals(0, $node->start());
- self::assertEquals(strlen($doc), $node->end());
+ self::assertEquals(0, $node->start(), 'Start offset');
+ self::assertEquals(strlen($doc), $node->end(), 'End offset');
- $assertion($node);
+ if ($assertion) {
+ $assertion($node);
+ }
}
/**
@@ -50,11 +50,6 @@ public function testIsomorphism(string $doc): void
$two = $this->parse($one->toString());
self::assertEquals($one, $two);
}
-
- /**
- * @return Generator
- */
- abstract public function provideNode(): Generator;
private function parse(string $doc): Node
{
From 9e6a73330db9d624c3d14963a3d08cf49e376b43 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Tue, 26 Jan 2021 21:08:36 +0000
Subject: [PATCH 49/63] Finish adding node tests
---
lib/Ast/DeprecatedNode.php | 13 +++++--
lib/Ast/MixinNode.php | 16 +++++++--
lib/Ast/TextNode.php | 6 +++-
lib/Ast/Type/GenericNode.php | 1 -
lib/Ast/Type/UnionNode.php | 5 ---
lib/Ast/TypeList.php | 51 ++++++++++++++++++++++------
lib/Parser.php | 30 ++++++++++-------
tests/Unit/Ast/NodeTest.php | 60 +++++++++++++++++++++------------
tests/Unit/Ast/NodeTestCase.php | 2 +-
9 files changed, 127 insertions(+), 57 deletions(-)
diff --git a/lib/Ast/DeprecatedNode.php b/lib/Ast/DeprecatedNode.php
index f9918262..90f7479a 100644
--- a/lib/Ast/DeprecatedNode.php
+++ b/lib/Ast/DeprecatedNode.php
@@ -2,20 +2,29 @@
namespace Phpactor\Docblock\Ast;
+use Phpactor\Docblock\Token;
+
class DeprecatedNode extends TagNode
{
public const CHILD_NAMES = [
+ 'token',
'text',
];
/**
* @var TextNode
*/
- private $text;
+ public $text;
+
+ /**
+ * @var Token
+ */
+ public $token;
- public function __construct(?TextNode $text)
+ public function __construct(Token $token, ?TextNode $text)
{
$this->text = $text;
+ $this->token = $token;
}
public function text(): ?TextNode
diff --git a/lib/Ast/MixinNode.php b/lib/Ast/MixinNode.php
index 6ebb834b..e38d6eec 100644
--- a/lib/Ast/MixinNode.php
+++ b/lib/Ast/MixinNode.php
@@ -3,17 +3,29 @@
namespace Phpactor\Docblock\Ast;
use Phpactor\Docblock\Ast\Type\ClassNode;
+use Phpactor\Docblock\Token;
class MixinNode extends TagNode
{
+ protected const CHILD_NAMES = [
+ 'tag',
+ 'class'
+ ];
+
/**
* @var ClassNode|null
*/
- private $class;
+ public $class;
+
+ /**
+ * @var Token
+ */
+ public $tag;
- public function __construct(?ClassNode $class)
+ public function __construct(Token $tag, ?ClassNode $class)
{
$this->class = $class;
+ $this->tag = $tag;
}
public function class(): ?ClassNode
diff --git a/lib/Ast/TextNode.php b/lib/Ast/TextNode.php
index 3855ebe3..625f5b2b 100644
--- a/lib/Ast/TextNode.php
+++ b/lib/Ast/TextNode.php
@@ -6,10 +6,14 @@
class TextNode extends Node
{
+ protected const CHILD_NAMES = [
+ 'tokens',
+ ];
+
/**
* @var Token[]
*/
- private $tokens;
+ public $tokens;
/**
* @param Token[] $tokens
diff --git a/lib/Ast/Type/GenericNode.php b/lib/Ast/Type/GenericNode.php
index b84e3158..1305d74d 100644
--- a/lib/Ast/Type/GenericNode.php
+++ b/lib/Ast/Type/GenericNode.php
@@ -9,7 +9,6 @@
class GenericNode extends TypeNode
{
protected const CHILD_NAMES = [
- 'open',
'type',
'open',
'parameters',
diff --git a/lib/Ast/Type/UnionNode.php b/lib/Ast/Type/UnionNode.php
index 5cba708b..ad96e744 100644
--- a/lib/Ast/Type/UnionNode.php
+++ b/lib/Ast/Type/UnionNode.php
@@ -20,9 +20,4 @@ public function __construct(TypeList $types)
{
$this->types = $types;
}
-
- public function types(): TypeList
- {
- return $this->types;
- }
}
diff --git a/lib/Ast/TypeList.php b/lib/Ast/TypeList.php
index 811f4f40..a477b13c 100644
--- a/lib/Ast/TypeList.php
+++ b/lib/Ast/TypeList.php
@@ -5,31 +5,38 @@
use ArrayIterator;
use Countable;
use IteratorAggregate;
+use Phpactor\Docblock\Token;
+use RuntimeException;
/**
- * @implements IteratorAggregate
+ * @template T of Element
+ * @implements IteratorAggregate
*/
-class TypeList implements IteratorAggregate, Countable
+class TypeList extends Node implements IteratorAggregate, Countable
{
+ protected const CHILD_NAMES = [
+ 'list'
+ ];
+
/**
- * @var TypeNode[]
+ * @var array
*/
- private $typeList;
+ public $list;
/**
- * @param TypeNode[] $typeList
+ * @param array $list
*/
- public function __construct(array $typeList)
+ public function __construct(array $list)
{
- $this->typeList = $typeList;
+ $this->list = $list;
}
/**
- * @return ArrayIterator
+ * @return ArrayIterator
*/
public function getIterator(): ArrayIterator
{
- return new ArrayIterator($this->typeList);
+ return new ArrayIterator($this->list);
}
/**
@@ -37,6 +44,30 @@ public function getIterator(): ArrayIterator
*/
public function count()
{
- return count($this->typeList);
+ return count($this->list);
+ }
+
+ /**
+ * @return T
+ */
+ public function first(): Element
+ {
+ if (!isset($this->list[0])) {
+ throw new RuntimeException(sprintf(
+ 'List has no first element'
+ ));
+ }
+
+ return $this->list[0];
+ }
+
+ /**
+ * @return TypeList
+ */
+ public function types(): self
+ {
+ return new self(array_filter($this->list, function (Element $element) {
+ return $element instanceof TypeNode;
+ }));
}
}
diff --git a/lib/Parser.php b/lib/Parser.php
index d8d9c4c8..d59e33eb 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -32,7 +32,7 @@
final class Parser
{
private const SCALAR_TYPES = [
- 'int', 'float', 'bool', 'string', 'mixed', 'callable'
+ 'int', 'float', 'bool', 'string', 'mixed'
];
/**
* @var Tokens
@@ -171,12 +171,12 @@ private function parseTypes(): ?TypeNode
if (null === $type) {
return $type;
}
- $types = [$type];
+ $elements = [$type];
while (true) {
if ($this->tokens->if(Token::T_BAR)) {
- $this->tokens->chomp();
- $types[] = $this->parseType();
+ $elements[] = $this->tokens->chomp();
+ $elements[] = $this->parseType();
if (null !== $type) {
continue;
}
@@ -184,11 +184,11 @@ private function parseTypes(): ?TypeNode
break;
}
- if (count($types) === 1) {
- return $types[0];
+ if (count($elements) === 1) {
+ return $elements[0];
}
- return new UnionNode(new TypeList($types));
+ return new UnionNode(new TypeList($elements));
}
private function parseType(): ?TypeNode
@@ -267,7 +267,7 @@ private function parseTypeList(string $delimiter = ','): TypeList
$types[] = $this->parseTypes();
}
if ($this->tokens->if(Token::T_COMMA)) {
- $this->tokens->chomp();
+ $types[] = $this->tokens->chomp();
continue;
}
break;
@@ -313,13 +313,15 @@ private function parseParameter(): ParameterNode
private function parseDeprecated(): DeprecatedNode
{
- $this->tokens->chomp();
- return new DeprecatedNode($this->parseText());
+ return new DeprecatedNode(
+ $this->tokens->chomp(Token::T_TAG),
+ $this->parseText()
+ );
}
private function parseMixin(): MixinNode
{
- $this->tokens->chomp();
+ $tag = $this->tokens->chomp(Token::T_TAG);
$type = null;
if ($this->tokens->if(Token::T_LABEL)) {
@@ -329,7 +331,7 @@ private function parseMixin(): MixinNode
}
}
- return new MixinNode($type);
+ return new MixinNode($tag, $type);
}
private function parseReturn(): ReturnNode
@@ -346,17 +348,19 @@ private function parseReturn(): ReturnNode
private function parseText(): ?TextNode
{
- if (!$this->tokens->current) {
+ if (null === $this->tokens->current) {
return null;
}
$text = [];
+
if (
$this->tokens->current->type === Token::T_WHITESPACE &&
$this->tokens->next()->type === Token::T_LABEL
) {
$this->tokens->chomp();
}
+
while ($this->tokens->current) {
if ($this->tokens->current->type === Token::T_PHPDOC_CLOSE) {
break;
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index 4aefdbad..b3730ee4 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -7,6 +7,10 @@
use Phpactor\Docblock\Ast\MethodNode;
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Ast\ParamNode;
+use Phpactor\Docblock\Ast\ReturnNode;
+use Phpactor\Docblock\Ast\Type\GenericNode;
+use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\Type\UnionNode;
class NodeTest extends NodeTestCase
{
@@ -15,17 +19,19 @@ class NodeTest extends NodeTestCase
*/
public function provideNode(): Generator
{
- yield from $this->methodTag();
- yield from $this->paramTag();
- yield from $this->varTag();
- yield from $this->returnTag();
+ yield from $this->provideTags();
+ yield from $this->provideTypes();
}
/**
* @return Generator
*/
- private function methodTag(): Generator
+ private function provideTags()
{
+ yield [ '@deprecated This is deprecated'];
+ yield [ '/** This is docblock @deprecated Foo */'];
+ yield [ '@mixin Foo\Bar'];
+ yield [ '@param string $foo This is a parameter'];
yield [
'@method static Baz\Bar bar(string $boo, string $baz)',
function (MethodNode $methodNode) {
@@ -38,29 +44,39 @@ function (MethodNode $methodNode) {
self::assertEquals(')', $methodNode->parenClose->toString());
}
];
- }
-
- /**
- * @return Generator
- */
- private function paramTag(): Generator
- {
- yield ['@param Baz\Bar $foobar'];
- }
-
- /**
- * @return Generator
- */
- private function varTag(): Generator
- {
+ yield ['@param Baz\Bar $foobar This is a parameter'];
yield ['@var Baz\Bar $foobar'];
+ yield ['@return Baz\Bar'];
}
/**
* @return Generator
*/
- private function returnTag(): Generator
+ private function provideTypes(): Generator
{
- yield ['@return Baz\Bar'];
+ yield 'scalar' => ['string'];
+ yield 'union' => [
+ '@return string|int|bool|float|mixed',
+ function (ReturnNode $return) {
+ $type = $return->type;
+ assert($type instanceof UnionNode);
+ self::assertInstanceOf(UnionNode::class, $type);
+ self::assertEquals('string', $type->types->types()->first()->toString());
+ self::assertCount(5, $type->types->types());
+ }
+ ];
+ yield 'list' => [
+ '@return Foo[]',
+ function (ReturnNode $return) {
+ self::assertInstanceOf(ListNode::class, $return->type);
+ }
+ ];
+ yield 'generic' => [
+ '@return Foo, Baz|Bar>',
+ function (ReturnNode $return) {
+ self::assertInstanceOf(GenericNode::class, $return->type);
+ }
+ ];
}
+
}
diff --git a/tests/Unit/Ast/NodeTestCase.php b/tests/Unit/Ast/NodeTestCase.php
index dbd9fb1a..bdde0e85 100644
--- a/tests/Unit/Ast/NodeTestCase.php
+++ b/tests/Unit/Ast/NodeTestCase.php
@@ -48,7 +48,7 @@ public function testIsomorphism(string $doc): void
{
$one = $this->parse($doc);
$two = $this->parse($one->toString());
- self::assertEquals($one, $two);
+ self::assertEquals($one, $two, $one->toString());
}
private function parse(string $doc): Node
From c616bcbd8f628d779ce84486be7de2725c96c021 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Tue, 26 Jan 2021 21:13:12 +0000
Subject: [PATCH 50/63] Namespaced tag nodes
---
lib/Ast/Node.php | 2 +-
.../DeprecatedTag.php} | 13 ++---
lib/Ast/{MethodNode.php => Tag/MethodTag.php} | 45 +++--------------
lib/Ast/{MixinNode.php => Tag/MixinTag.php} | 7 +--
lib/Ast/{ParamNode.php => Tag/ParamTag.php} | 10 ++--
.../ParameterTag.php} | 9 +++-
.../{PropertyNode.php => Tag/PropertyTag.php} | 8 +--
lib/Ast/{ReturnNode.php => Tag/ReturnTag.php} | 9 ++--
lib/Ast/{VarNode.php => Tag/VarTag.php} | 9 ++--
lib/Ast/TextNode.php | 2 +-
lib/{ => Ast}/Token.php | 2 +-
lib/{ => Ast}/Tokens.php | 3 +-
lib/Ast/Type/ClassNode.php | 2 +-
lib/Ast/Type/GenericNode.php | 2 +-
lib/Ast/Type/ListNode.php | 2 +-
lib/Ast/Type/NullNode.php | 2 +-
lib/Ast/Type/NullableNode.php | 2 +-
lib/Ast/Type/ScalarNode.php | 2 +-
lib/Ast/TypeList.php | 2 +-
lib/Ast/TypeNode.php | 2 +-
lib/Ast/UnknownTag.php | 2 +-
lib/Ast/Value/NullValue.php | 2 +-
lib/Ast/VariableNode.php | 2 +-
lib/Lexer.php | 3 ++
lib/Parser.php | 50 ++++++++++---------
lib/Printer/TestPrinter.php | 50 +++++++++----------
tests/Unit/Ast/NodeTest.php | 14 +++---
tests/Unit/LexerTest.php | 2 +-
tests/Unit/ParserTest.php | 2 +-
29 files changed, 126 insertions(+), 136 deletions(-)
rename lib/Ast/{DeprecatedNode.php => Tag/DeprecatedTag.php} (65%)
rename lib/Ast/{MethodNode.php => Tag/MethodTag.php} (64%)
rename lib/Ast/{MixinNode.php => Tag/MixinTag.php} (77%)
rename lib/Ast/{ParamNode.php => Tag/ParamTag.php} (77%)
rename lib/Ast/{ParameterNode.php => Tag/ParameterTag.php} (77%)
rename lib/Ast/{PropertyNode.php => Tag/PropertyTag.php} (70%)
rename lib/Ast/{ReturnNode.php => Tag/ReturnTag.php} (74%)
rename lib/Ast/{VarNode.php => Tag/VarTag.php} (75%)
rename lib/{ => Ast}/Token.php (98%)
rename lib/{ => Ast}/Tokens.php (97%)
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index 21e30298..deb35bef 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -3,7 +3,7 @@
namespace Phpactor\Docblock\Ast;
use Generator;
-use Phpactor\Docblock\Token;
+use Phpactor\Docblock\Ast\Token;
abstract class Node implements Element
{
diff --git a/lib/Ast/DeprecatedNode.php b/lib/Ast/Tag/DeprecatedTag.php
similarity index 65%
rename from lib/Ast/DeprecatedNode.php
rename to lib/Ast/Tag/DeprecatedTag.php
index 90f7479a..81500dab 100644
--- a/lib/Ast/DeprecatedNode.php
+++ b/lib/Ast/Tag/DeprecatedTag.php
@@ -1,10 +1,12 @@
text = $text;
$this->token = $token;
}
-
- public function text(): ?TextNode
- {
- return $this->text;
- }
}
diff --git a/lib/Ast/MethodNode.php b/lib/Ast/Tag/MethodTag.php
similarity index 64%
rename from lib/Ast/MethodNode.php
rename to lib/Ast/Tag/MethodTag.php
index 0569adc4..f2657f3a 100644
--- a/lib/Ast/MethodNode.php
+++ b/lib/Ast/Tag/MethodTag.php
@@ -1,10 +1,14 @@
parenClose = $parenClose;
$this->tag = $tag;
}
-
- public function name(): ?Token
- {
- return $this->name;
- }
-
- public function type(): ?TypeNode
- {
- return $this->type;
- }
-
- public function static(): ?Token
- {
- return $this->static;
- }
-
- public function parameters(): ?ParameterList
- {
- return $this->parameters;
- }
-
- public function text(): ?TextNode
- {
- return $this->text;
- }
-
- public function parenOpen(): ?Token
- {
- return $this->parenOpen;
- }
-
- public function parenClose(): ?Token
- {
- return $this->parenClose;
- }
}
diff --git a/lib/Ast/MixinNode.php b/lib/Ast/Tag/MixinTag.php
similarity index 77%
rename from lib/Ast/MixinNode.php
rename to lib/Ast/Tag/MixinTag.php
index e38d6eec..48dbf7bd 100644
--- a/lib/Ast/MixinNode.php
+++ b/lib/Ast/Tag/MixinTag.php
@@ -1,11 +1,12 @@
tokens->chomp());
}
- private function parseParam(): ParamNode
+ private function parseParam(): ParamTag
{
$type = $variable = $textNode = null;
$tag = $this->tokens->chomp(Token::T_TAG);
@@ -105,10 +107,10 @@ private function parseParam(): ParamNode
$variable = $this->parseVariable();
}
- return new ParamNode($tag, $type, $variable, $this->parseText());
+ return new ParamTag($tag, $type, $variable, $this->parseText());
}
- private function parseVar(): VarNode
+ private function parseVar(): VarTag
{
$tag = $this->tokens->chomp(Token::T_TAG);
$type = $variable = null;
@@ -119,10 +121,10 @@ private function parseVar(): VarNode
$variable = $this->parseVariable();
}
- return new VarNode($tag, $type, $variable);
+ return new VarTag($tag, $type, $variable);
}
- private function parseMethod(): MethodNode
+ private function parseMethod(): MethodTag
{
$tag = $this->tokens->chomp(Token::T_TAG);
$type = $name = $parameterList = $open = $close = null;
@@ -148,10 +150,10 @@ private function parseMethod(): MethodNode
$close = $this->tokens->chompIf(Token::T_PAREN_CLOSE);
}
- return new MethodNode($tag, $type, $name, $static, $open, $parameterList, $close, $this->parseText());
+ return new MethodTag($tag, $type, $name, $static, $open, $parameterList, $close, $this->parseText());
}
- private function parseProperty(): PropertyNode
+ private function parseProperty(): PropertyTag
{
$this->tokens->chomp(Token::T_TAG);
$type = $name = null;
@@ -162,7 +164,7 @@ private function parseProperty(): PropertyNode
$name = $this->tokens->chomp();
}
- return new PropertyNode($type, $name);
+ return new PropertyTag($type, $name);
}
private function parseTypes(): ?TypeNode
@@ -295,7 +297,7 @@ private function parseParameterList(): ?ParameterList
return new ParameterList($parameters);
}
- private function parseParameter(): ParameterNode
+ private function parseParameter(): ParameterTag
{
$type = $name = $default = null;
if ($this->tokens->if(Token::T_LABEL)) {
@@ -308,18 +310,18 @@ private function parseParameter(): ParameterNode
$equals = $this->tokens->chomp();
$default = $this->parseValue();
}
- return new ParameterNode($type, $name, $default);
+ return new ParameterTag($type, $name, $default);
}
- private function parseDeprecated(): DeprecatedNode
+ private function parseDeprecated(): DeprecatedTag
{
- return new DeprecatedNode(
+ return new DeprecatedTag(
$this->tokens->chomp(Token::T_TAG),
$this->parseText()
);
}
- private function parseMixin(): MixinNode
+ private function parseMixin(): MixinTag
{
$tag = $this->tokens->chomp(Token::T_TAG);
$type = null;
@@ -331,10 +333,10 @@ private function parseMixin(): MixinNode
}
}
- return new MixinNode($tag, $type);
+ return new MixinTag($tag, $type);
}
- private function parseReturn(): ReturnNode
+ private function parseReturn(): ReturnTag
{
$tag = $this->tokens->chomp(Token::T_TAG);
$type = null;
@@ -343,7 +345,7 @@ private function parseReturn(): ReturnNode
$type = $this->parseTypes();
}
- return new ReturnNode($tag, $type, $this->parseText());
+ return new ReturnTag($tag, $type, $this->parseText());
}
private function parseText(): ?TextNode
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index f68553db..829fc615 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -2,20 +2,20 @@
namespace Phpactor\Docblock\Printer;
-use Phpactor\Docblock\Ast\DeprecatedNode;
+use Phpactor\Docblock\Ast\Tag\DeprecatedTag;
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Element;
-use Phpactor\Docblock\Ast\MethodNode;
-use Phpactor\Docblock\Ast\MixinNode;
+use Phpactor\Docblock\Ast\Tag\MethodTag;
+use Phpactor\Docblock\Ast\Tag\MixinTag;
use Phpactor\Docblock\Ast\ParameterList;
-use Phpactor\Docblock\Ast\ParameterNode;
-use Phpactor\Docblock\Ast\PropertyNode;
-use Phpactor\Docblock\Ast\ReturnNode;
+use Phpactor\Docblock\Ast\Tag\ParameterTag;
+use Phpactor\Docblock\Ast\Tag\PropertyTag;
+use Phpactor\Docblock\Ast\Tag\ReturnTag;
use Phpactor\Docblock\Ast\TextNode;
use Phpactor\Docblock\Ast\TypeList;
use Phpactor\Docblock\Ast\TypeNode;
use Phpactor\Docblock\Ast\Node;
-use Phpactor\Docblock\Ast\ParamNode;
+use Phpactor\Docblock\Ast\Tag\ParamTag;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
use Phpactor\Docblock\Ast\Type\NullNode;
@@ -23,10 +23,10 @@
use Phpactor\Docblock\Ast\Type\UnionNode;
use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\ValueNode;
-use Phpactor\Docblock\Ast\VarNode;
+use Phpactor\Docblock\Ast\Tag\VarTag;
use Phpactor\Docblock\Ast\VariableNode;
use Phpactor\Docblock\Printer;
-use Phpactor\Docblock\Token;
+use Phpactor\Docblock\Ast\Token;
use RuntimeException;
final class TestPrinter implements Printer
@@ -62,12 +62,12 @@ private function render(?Element $node): void
return;
}
- if ($node instanceof ParamNode) {
+ if ($node instanceof ParamTag) {
$this->renderParam($node);
return;
}
- if ($node instanceof VarNode) {
+ if ($node instanceof VarTag) {
$this->renderVar($node);
return;
}
@@ -117,32 +117,32 @@ private function render(?Element $node): void
return;
}
- if ($node instanceof DeprecatedNode) {
+ if ($node instanceof DeprecatedTag) {
$this->renderDeprecated($node);
return;
}
- if ($node instanceof MethodNode) {
+ if ($node instanceof MethodTag) {
$this->renderMethod($node);
return;
}
- if ($node instanceof PropertyNode) {
+ if ($node instanceof PropertyTag) {
$this->renderProperty($node);
return;
}
- if ($node instanceof MixinNode) {
+ if ($node instanceof MixinTag) {
$this->renderMixin($node);
return;
}
- if ($node instanceof ReturnNode) {
+ if ($node instanceof ReturnTag) {
$this->renderReturn($node);
return;
}
- if ($node instanceof ParameterNode) {
+ if ($node instanceof ParameterTag) {
$this->renderParameter($node);
return;
}
@@ -165,7 +165,7 @@ private function renderDocblock(Docblock $node): void
}
}
- private function renderParam(ParamNode $node): void
+ private function renderParam(ParamTag $node): void
{
$this->out[] = $node->shortName() . '(';
$this->render($node->type());
@@ -178,7 +178,7 @@ private function renderParam(ParamNode $node): void
$this->out[] = ')';
}
- private function renderVar(VarNode $node): void
+ private function renderVar(VarTag $node): void
{
$this->out[] = $node->shortName() . '(';
$this->render($node->type());
@@ -236,7 +236,7 @@ private function renderTextNode(TextNode $node): void
$this->out[] = ')';
}
- private function renderDeprecated(DeprecatedNode $node): void
+ private function renderDeprecated(DeprecatedTag $node): void
{
$this->out[] = $node->shortName() . '(';
if ($node->text()) {
@@ -245,7 +245,7 @@ private function renderDeprecated(DeprecatedNode $node): void
$this->out[] = ')';
}
- private function renderMethod(MethodNode $node): void
+ private function renderMethod(MethodTag $node): void
{
$this->out[] = $node->shortName() . '(';
$this->render($node->type());
@@ -267,7 +267,7 @@ private function renderMethod(MethodNode $node): void
$this->out[] = ')';
}
- private function renderMixin(MixinNode $node): void
+ private function renderMixin(MixinTag $node): void
{
$this->out[] = $node->shortName() . '(';
$this->render($node->class());
@@ -281,7 +281,7 @@ private function renderNullable(NullableNode $node): void
$this->out[] = ')';
}
- private function renderProperty(PropertyNode $node): void
+ private function renderProperty(PropertyTag $node): void
{
$this->out[] = $node->shortName() . '(';
$this->render($node->type());
@@ -292,7 +292,7 @@ private function renderProperty(PropertyNode $node): void
$this->out[] = ')';
}
- private function renderReturn(ReturnNode $node): void
+ private function renderReturn(ReturnTag $node): void
{
$this->out[] = $node->shortName() . '(';
$this->render($node->type());
@@ -319,7 +319,7 @@ private function renderParameterList(ParameterList $list): void
$this->out[] = ')';
}
- private function renderParameter(ParameterNode $node): void
+ private function renderParameter(ParameterTag $node): void
{
$this->out[] = $node->shortName() . '(';
if ($node->name()) {
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index b3730ee4..05b9616e 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -4,10 +4,10 @@
use Generator;
use PHPUnit\Framework\TestCase;
-use Phpactor\Docblock\Ast\MethodNode;
+use Phpactor\Docblock\Ast\Tag\MethodTag;
use Phpactor\Docblock\Ast\Node;
-use Phpactor\Docblock\Ast\ParamNode;
-use Phpactor\Docblock\Ast\ReturnNode;
+use Phpactor\Docblock\Ast\Tag\ParamTag;
+use Phpactor\Docblock\Ast\Tag\ReturnTag;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
use Phpactor\Docblock\Ast\Type\UnionNode;
@@ -34,7 +34,7 @@ private function provideTags()
yield [ '@param string $foo This is a parameter'];
yield [
'@method static Baz\Bar bar(string $boo, string $baz)',
- function (MethodNode $methodNode) {
+ function (MethodTag $methodNode) {
self::assertEquals('@method static Baz\Bar bar(string $boo, string $baz)', $methodNode->toString());
self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
self::assertEquals('static', $methodNode->static->value);
@@ -57,7 +57,7 @@ private function provideTypes(): Generator
yield 'scalar' => ['string'];
yield 'union' => [
'@return string|int|bool|float|mixed',
- function (ReturnNode $return) {
+ function (ReturnTag $return) {
$type = $return->type;
assert($type instanceof UnionNode);
self::assertInstanceOf(UnionNode::class, $type);
@@ -67,13 +67,13 @@ function (ReturnNode $return) {
];
yield 'list' => [
'@return Foo[]',
- function (ReturnNode $return) {
+ function (ReturnTag $return) {
self::assertInstanceOf(ListNode::class, $return->type);
}
];
yield 'generic' => [
'@return Foo, Baz|Bar>',
- function (ReturnNode $return) {
+ function (ReturnTag $return) {
self::assertInstanceOf(GenericNode::class, $return->type);
}
];
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 150c7333..4835aa8e 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -5,7 +5,7 @@
use Generator;
use PHPUnit\Framework\TestCase;
use Phpactor\Docblock\Lexer;
-use Phpactor\Docblock\Token;
+use Phpactor\Docblock\Ast\Token;
class LexerTest extends TestCase
{
diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php
index 26949747..ae68a0e0 100644
--- a/tests/Unit/ParserTest.php
+++ b/tests/Unit/ParserTest.php
@@ -8,7 +8,7 @@
use Phpactor\Docblock\Ast\Node;
use Phpactor\Docblock\Lexer;
use Phpactor\Docblock\Parser;
-use Phpactor\Docblock\Token;
+use Phpactor\Docblock\Ast\Token;
class ParserTest extends TestCase
{
From eaec94d067506c3ab115262bd54c1125ee7a3546 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Tue, 26 Jan 2021 21:40:20 +0000
Subject: [PATCH 51/63] Migrate printer tests
---
lib/Ast/Node.php | 4 +-
lib/Parser.php | 5 +-
lib/Printer/TestPrinter.php | 309 ++----------------------
tests/Printer/examples/classname1.test | 8 +-
tests/Printer/examples/classname2.test | 8 +-
tests/Printer/examples/deprecated1.test | 6 +-
tests/Printer/examples/deprecated2.test | 7 +-
tests/Printer/examples/generic1.test | 12 +-
tests/Printer/examples/generic2.test | 13 +-
tests/Printer/examples/generic3.test | 16 +-
tests/Printer/examples/generic4.test | 19 +-
tests/Printer/examples/list1.test | 15 +-
tests/Printer/examples/method1.test | 7 +-
tests/Printer/examples/method2.test | 7 +-
tests/Printer/examples/method3.test | 12 +-
tests/Printer/examples/method4.test | 12 +-
tests/Printer/examples/method5.test | 23 +-
tests/Printer/examples/method6.test | 27 ++-
tests/Printer/examples/mixin1.test | 7 +-
tests/Printer/examples/nullable1.test | 8 +-
tests/Printer/examples/param1.test | 8 +-
tests/Printer/examples/param2.test | 13 +-
tests/Printer/examples/param3.test | 18 +-
tests/Printer/examples/param4.test | 7 +-
tests/Printer/examples/param5.test | 9 +-
tests/Printer/examples/param6.test | 15 +-
tests/Printer/examples/php_core1.test | 30 ++-
tests/Printer/examples/property1.test | 6 +-
tests/Printer/examples/return1.test | 7 +-
tests/Printer/examples/text1.test | 3 +-
tests/Printer/examples/union1.test | 10 +-
tests/Printer/examples/var1.test | 8 +-
tests/Printer/examples/var2.test | 6 +-
tests/Printer/examples/var3.test | 6 +-
tests/Unit/Ast/NodeTestCase.php | 2 +-
35 files changed, 313 insertions(+), 360 deletions(-)
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index deb35bef..a02ca72f 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -59,7 +59,7 @@ public function shortName(): string
/**
* @return Generator
*/
- public function getDescendantNodes(): Generator
+ public function getDescendantElements(): Generator
{
yield $this;
yield from $this->walkNodes($this->getChildElements());
@@ -80,7 +80,7 @@ private function walkNodes(iterable $nodes): Generator
}
if ($child instanceof Node) {
- yield from $child->getDescendantNodes();
+ yield from $child->getDescendantElements();
continue;
}
diff --git a/lib/Parser.php b/lib/Parser.php
index 30e0c3ae..977a73f3 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -33,8 +33,11 @@
final class Parser
{
+ /**
+ * TODO: callable is not a scalar
+ */
private const SCALAR_TYPES = [
- 'int', 'float', 'bool', 'string', 'mixed'
+ 'int', 'float', 'bool', 'string', 'mixed', 'callable',
];
/**
* @var Tokens
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 829fc615..fb281ad7 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -36,308 +36,41 @@ final class TestPrinter implements Printer
*/
private $out = [];
- public function print(Node $node): string
- {
- $this->out = [];
-
- $this->render($node);
-
- return implode('', $this->out);
- }
-
- private function render(?Element $node): void
- {
- if (null === $node) {
- $this->out[] = '#missing#';
- return;
- }
-
- if ($node instanceof Docblock) {
- $this->renderDocblock($node);
- return;
- }
-
- if ($node instanceof Token) {
- $this->out[] = $node->value;
- return;
- }
-
- if ($node instanceof ParamTag) {
- $this->renderParam($node);
- return;
- }
-
- if ($node instanceof VarTag) {
- $this->renderVar($node);
- return;
- }
-
- if ($node instanceof ListNode) {
- $this->renderListNode($node);
- return;
- }
-
- if ($node instanceof GenericNode) {
- $this->renderGenericNode($node);
- return;
- }
-
- if ($node instanceof NullableNode) {
- $this->renderNullable($node);
- return;
- }
-
- if ($node instanceof UnionNode) {
- $this->renderUnion($node);
- return;
- }
-
- if ($node instanceof NullNode) {
- $this->out[] = $node->shortName() . '()';
- return;
- }
-
- if ($node instanceof TypeNode) {
- $this->renderTypeNode($node);
- return;
- }
+ private $indent = 0;
- if ($node instanceof TextNode) {
- $this->renderTextNode($node);
- return;
- }
-
- if ($node instanceof VariableNode) {
- $this->renderVariableNode($node);
- return;
- }
-
- if ($node instanceof UnknownTag) {
- $this->out[] = $node->shortName();
- return;
- }
-
- if ($node instanceof DeprecatedTag) {
- $this->renderDeprecated($node);
- return;
- }
-
- if ($node instanceof MethodTag) {
- $this->renderMethod($node);
- return;
- }
-
- if ($node instanceof PropertyTag) {
- $this->renderProperty($node);
- return;
- }
-
- if ($node instanceof MixinTag) {
- $this->renderMixin($node);
- return;
- }
-
- if ($node instanceof ReturnTag) {
- $this->renderReturn($node);
- return;
- }
-
- if ($node instanceof ParameterTag) {
- $this->renderParameter($node);
- return;
- }
-
- if ($node instanceof ValueNode) {
- $this->renderValue($node);
- return;
- }
-
- throw new RuntimeException(sprintf(
- 'Do not know how to render "%s"',
- get_class($node)
- ));
- }
-
- private function renderDocblock(Docblock $node): void
- {
- foreach ($node->children() as $child) {
- $this->render($child);
- }
- }
-
- private function renderParam(ParamTag $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- $this->out[] = ',';
- $this->render($node->variable());
- if ($node->text()) {
- $this->out[] = ',';
- $this->render($node->text());
- }
- $this->out[] = ')';
- }
-
- private function renderVar(VarTag $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- if ($node->variable()) {
- $this->out[] = ',';
- $this->render($node->variable());
- }
- $this->out[] = ')';
- }
-
- private function renderTypeNode(TypeNode $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->name());
- $this->out[] = ')';
- }
-
- private function renderVariableNode(VariableNode $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->name());
- $this->out[] = ')';
- }
-
- private function renderListNode(ListNode $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- $this->out[] = ')';
- }
-
- private function renderGenericNode(GenericNode $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- $this->out[] = ',';
- $this->renderTypeList($node->parameters());
- $this->out[] = ')';
- }
-
- private function renderTypeList(TypeList $typeList, string $delimiter = ','): void
- {
- foreach ($typeList as $i => $param) {
- $this->render($param);
- if ($i + 1 !== $typeList->count()) {
- $this->out[] = $delimiter;
- }
- }
- }
-
- private function renderTextNode(TextNode $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->out[] = $node->toString();
- $this->out[] = ')';
- }
-
- private function renderDeprecated(DeprecatedTag $node): void
- {
- $this->out[] = $node->shortName() . '(';
- if ($node->text()) {
- $this->render($node->text());
- }
- $this->out[] = ')';
- }
-
- private function renderMethod(MethodTag $node): void
+ public function print(Node $node): string
{
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- if ($node->name()) {
- $this->out[] = ',';
- $this->render($node->name());
- }
- if ($node->static()) {
- $this->out[] = ',static';
- }
- if ($node->parameters()) {
- $this->out[] = ',';
- $this->renderParameterList($node->parameters());
- }
- if ($node->text()) {
- $this->out[] = ',';
- $this->render($node->text());
+ $this->indent++;
+ $out = sprintf('%s: = ', $node->shortName());
+ foreach ($node->getChildElements() as $child) {
+ $out .= $this->printElement($child);
}
- $this->out[] = ')';
- }
+ $this->indent--;
- private function renderMixin(MixinTag $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->class());
- $this->out[] = ')';
+ return $out;
}
- private function renderNullable(NullableNode $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- $this->out[] = ')';
- }
-
- private function renderProperty(PropertyTag $node): void
+ /**
+ * @param array|Element $element
+ */
+ public function printElement($element): string
{
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- if ($node->name()) {
- $this->out[] = ',';
- $this->render($node->name());
+ if ($element instanceof Token) {
+ return sprintf('%s', $element->value);
}
- $this->out[] = ')';
- }
- private function renderReturn(ReturnTag $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->render($node->type());
- if ($node->text()) {
- $this->out[] = ',';
- $this->render($node->text());
+ if ($element instanceof Node) {
+ return $this->newLine() . $this->print($element);
}
- $this->out[] = ')';
- }
- private function renderUnion(UnionNode $node): void
- {
- $this->out[] = $node->shortName() . '(';
- $this->renderTypeList($node->types(), '|');
- $this->out[] = ')';
+ return implode('', array_map(function (Element $element) {
+ return $this->printElement($element);
+ }, (array)$element));
}
- private function renderParameterList(ParameterList $list): void
+ private function newLine(): string
{
- $this->out[] = 'ParameterList(';
- foreach ($list as $i => $parameter) {
- $this->render($parameter);
- }
- $this->out[] = ')';
+ return "\n".str_repeat(' ', $this->indent);
}
- private function renderParameter(ParameterTag $node): void
- {
- $this->out[] = $node->shortName() . '(';
- if ($node->name()) {
- $this->render($node->name());
- }
- if ($node->type()) {
- $this->out[] = ',';
- $this->render($node->type());
- }
- if ($node->default()) {
- $this->out[] = ',';
- $this->render($node->default());
- }
- $this->out[] = ')';
- }
-
- private function renderValue(ValueNode $node): void
- {
- $this->out[] = json_encode($node->value());
- }
}
diff --git a/tests/Printer/examples/classname1.test b/tests/Printer/examples/classname1.test
index 492b2b5b..e107e06e 100644
--- a/tests/Printer/examples/classname1.test
+++ b/tests/Printer/examples/classname1.test
@@ -2,7 +2,9 @@
* @var Foobar\Barfoo
*/
---
-/**
- * VarNode(ClassNode(Foobar\Barfoo))
+Docblock: =
+ ElementList: = /**
+ *
+ VarTag: = @var
+ ClassNode: = Foobar\Barfoo
*/
-
diff --git a/tests/Printer/examples/classname2.test b/tests/Printer/examples/classname2.test
index 286959e4..3dab2b6b 100644
--- a/tests/Printer/examples/classname2.test
+++ b/tests/Printer/examples/classname2.test
@@ -2,7 +2,9 @@
* @var \foo_bar\bar_foo
*/
---
-/**
- * VarNode(ClassNode(\foo_bar\bar_foo))
+Docblock: =
+ ElementList: = /**
+ *
+ VarTag: = @var
+ ClassNode: = \foo_bar\bar_foo
*/
-
diff --git a/tests/Printer/examples/deprecated1.test b/tests/Printer/examples/deprecated1.test
index dd9ff0d5..2189e884 100644
--- a/tests/Printer/examples/deprecated1.test
+++ b/tests/Printer/examples/deprecated1.test
@@ -2,6 +2,8 @@
* @deprecated
*/
---
-/**
- * DeprecatedNode()
+Docblock: =
+ ElementList: = /**
+ *
+ DeprecatedTag: = @deprecated
*/
diff --git a/tests/Printer/examples/deprecated2.test b/tests/Printer/examples/deprecated2.test
index 9695d03a..721db036 100644
--- a/tests/Printer/examples/deprecated2.test
+++ b/tests/Printer/examples/deprecated2.test
@@ -2,6 +2,9 @@
* @deprecated This is because
*/
---
-/**
- * DeprecatedNode(TextNode(This is because))
+Docblock: =
+ ElementList: = /**
+ *
+ DeprecatedTag: = @deprecated
+ TextNode: = This is because
*/
diff --git a/tests/Printer/examples/generic1.test b/tests/Printer/examples/generic1.test
index 8b3b802d..3fd85b39 100644
--- a/tests/Printer/examples/generic1.test
+++ b/tests/Printer/examples/generic1.test
@@ -2,6 +2,14 @@
* @param Foobar $foobar
*/
---
-/**
- * ParamNode(GenericNode(ClassNode(Foobar),ListNode(ClassNode(Barfoo))),VariableNode($foobar))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ GenericNode: =
+ ClassNode: = Foobar<
+ TypeList: =
+ ListNode: =
+ ClassNode: = Barfoo[]>
+ VariableNode: = $foobar
*/
diff --git a/tests/Printer/examples/generic2.test b/tests/Printer/examples/generic2.test
index 94017cf8..60b522ec 100644
--- a/tests/Printer/examples/generic2.test
+++ b/tests/Printer/examples/generic2.test
@@ -2,6 +2,15 @@
* @param Foobar $foobar
*/
---
-/**
- * ParamNode(GenericNode(ClassNode(Foobar),ListNode(ClassNode(Barfoo)),ScalarNode(string)),VariableNode($foobar))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ GenericNode: =
+ ClassNode: = Foobar<
+ TypeList: =
+ ListNode: =
+ ClassNode: = Barfoo[],
+ ScalarNode: = string>
+ VariableNode: = $foobar
*/
diff --git a/tests/Printer/examples/generic3.test b/tests/Printer/examples/generic3.test
index 13b5eba0..5b2addde 100644
--- a/tests/Printer/examples/generic3.test
+++ b/tests/Printer/examples/generic3.test
@@ -2,6 +2,18 @@
* @param Foobar,string> $foobar
*/
---
-/**
- * ParamNode(GenericNode(ClassNode(Foobar),GenericNode(ClassNode(Barfoo),ScalarNode(int),ScalarNode(string)),ScalarNode(string)),VariableNode($foobar))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ GenericNode: =
+ ClassNode: = Foobar<
+ TypeList: =
+ GenericNode: =
+ ClassNode: = Barfoo<
+ TypeList: =
+ ScalarNode: = int,
+ ScalarNode: = string>,
+ ScalarNode: = string>
+ VariableNode: = $foobar
*/
diff --git a/tests/Printer/examples/generic4.test b/tests/Printer/examples/generic4.test
index d2e633da..97a8ef56 100644
--- a/tests/Printer/examples/generic4.test
+++ b/tests/Printer/examples/generic4.test
@@ -2,6 +2,21 @@
* @param Foobar,string, Baz> $foobar
*/
---
-/**
- * ParamNode(GenericNode(ClassNode(Foobar),GenericNode(ClassNode(Barfoo),ListNode(ScalarNode(int)),ListNode(ScalarNode(int))),ScalarNode(string),ClassNode(Baz)),VariableNode($foobar))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ GenericNode: =
+ ClassNode: = Foobar<
+ TypeList: =
+ GenericNode: =
+ ClassNode: = Barfoo<
+ TypeList: =
+ ListNode: =
+ ScalarNode: = int[],
+ ListNode: =
+ ScalarNode: = int[]>,
+ ScalarNode: = string,
+ ClassNode: = Baz>
+ VariableNode: = $foobar
*/
diff --git a/tests/Printer/examples/list1.test b/tests/Printer/examples/list1.test
index 94d5be11..9ee9cb00 100644
--- a/tests/Printer/examples/list1.test
+++ b/tests/Printer/examples/list1.test
@@ -3,7 +3,16 @@
* @param string[] $strings
*/
---
-/**
- * ParamNode(ListNode(ClassNode(Foobar)),VariableNode($foobar))
- * ParamNode(ListNode(ScalarNode(string)),VariableNode($strings))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ ListNode: =
+ ClassNode: = Foobar[]
+ VariableNode: = $foobar
+ *
+ ParamTag: = @param
+ ListNode: =
+ ScalarNode: = string[]
+ VariableNode: = $strings
*/
diff --git a/tests/Printer/examples/method1.test b/tests/Printer/examples/method1.test
index 826a9b5a..80770a95 100644
--- a/tests/Printer/examples/method1.test
+++ b/tests/Printer/examples/method1.test
@@ -2,6 +2,9 @@
* @method Foobar foobar()
*/
---
-/**
- * MethodNode(ClassNode(Foobar),foobar)
+Docblock: =
+ ElementList: = /**
+ *
+ MethodTag: = @method
+ ClassNode: = Foobarfoobar()
*/
diff --git a/tests/Printer/examples/method2.test b/tests/Printer/examples/method2.test
index ab66ad1f..ba7d6680 100644
--- a/tests/Printer/examples/method2.test
+++ b/tests/Printer/examples/method2.test
@@ -2,6 +2,9 @@
* @method static Foobar foobar()
*/
---
-/**
- * MethodNode(ClassNode(Foobar),foobar,static)
+Docblock: =
+ ElementList: = /**
+ *
+ MethodTag: = @methodstatic
+ ClassNode: = Foobarfoobar()
*/
diff --git a/tests/Printer/examples/method3.test b/tests/Printer/examples/method3.test
index 8600ba44..ef4e61d5 100644
--- a/tests/Printer/examples/method3.test
+++ b/tests/Printer/examples/method3.test
@@ -2,6 +2,14 @@
* @method static bool allAlnum(mixed $value) Assert that value is alphanumeric for all values.
*/
---
-/**
- * MethodNode(ScalarNode(bool),allAlnum,static,ParameterList(ParameterNode(VariableNode($value),ScalarNode(mixed))),TextNode(Assert that value is alphanumeric for all values.))
+Docblock: =
+ ElementList: = /**
+ *
+ MethodTag: = @methodstatic
+ ScalarNode: = boolallAlnum(
+ ParameterList: =
+ ParameterTag: =
+ ScalarNode: = mixed
+ VariableNode: = $value)
+ TextNode: = Assert that value is alphanumeric for all values.
*/
diff --git a/tests/Printer/examples/method4.test b/tests/Printer/examples/method4.test
index c6a80786..c2876fec 100644
--- a/tests/Printer/examples/method4.test
+++ b/tests/Printer/examples/method4.test
@@ -2,6 +2,14 @@
* @method bool is(string $value = null)
*/
---
-/**
- * MethodNode(ScalarNode(bool),is,ParameterList(ParameterNode(VariableNode($value),ScalarNode(string),null)))
+Docblock: =
+ ElementList: = /**
+ *
+ MethodTag: = @method
+ ScalarNode: = boolis(
+ ParameterList: =
+ ParameterTag: =
+ ScalarNode: = string
+ VariableNode: = $value
+ NullValue: = )
*/
diff --git a/tests/Printer/examples/method5.test b/tests/Printer/examples/method5.test
index ee5656aa..a27e6a65 100644
--- a/tests/Printer/examples/method5.test
+++ b/tests/Printer/examples/method5.test
@@ -2,6 +2,25 @@
* @method static bool allAlnum(mixed $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values.
*/
---
-/**
- * MethodNode(ScalarNode(bool),allAlnum,static,ParameterList(ParameterNode(VariableNode($value),ScalarNode(mixed)),ParameterNode(VariableNode($message),UnionNode(ScalarNode(string)|ScalarNode(callable)),null),ParameterNode(VariableNode($propertyPath),ScalarNode(string),null)),TextNode(Assert that value is alphanumeric for all values.))
+Docblock: =
+ ElementList: = /**
+ *
+ MethodTag: = @methodstatic
+ ScalarNode: = boolallAlnum(
+ ParameterList: =
+ ParameterTag: =
+ ScalarNode: = mixed
+ VariableNode: = $value,
+ ParameterTag: =
+ UnionNode: =
+ TypeList: =
+ ScalarNode: = string|
+ ScalarNode: = callable
+ VariableNode: = $message
+ NullValue: = ,
+ ParameterTag: =
+ ScalarNode: = string
+ VariableNode: = $propertyPath
+ NullValue: = )
+ TextNode: = Assert that value is alphanumeric for all values.
*/
diff --git a/tests/Printer/examples/method6.test b/tests/Printer/examples/method6.test
index 60452406..9a301be1 100644
--- a/tests/Printer/examples/method6.test
+++ b/tests/Printer/examples/method6.test
@@ -2,6 +2,29 @@
* @method static bool allIpv4(string $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address for all values.
*/
---
-/**
- * MethodNode(ScalarNode(bool),allIpv4,static,ParameterList(ParameterNode(VariableNode($value),ScalarNode(string)),ParameterNode(VariableNode($flag),ScalarNode(int),null),ParameterNode(VariableNode($message),UnionNode(ScalarNode(string)|ScalarNode(callable)),null),ParameterNode(VariableNode($propertyPath),ScalarNode(string),null)),TextNode(Assert that value is an IPv4 address for all values.))
+Docblock: =
+ ElementList: = /**
+ *
+ MethodTag: = @methodstatic
+ ScalarNode: = boolallIpv4(
+ ParameterList: =
+ ParameterTag: =
+ ScalarNode: = string
+ VariableNode: = $value,
+ ParameterTag: =
+ ScalarNode: = int
+ VariableNode: = $flag
+ NullValue: = ,
+ ParameterTag: =
+ UnionNode: =
+ TypeList: =
+ ScalarNode: = string|
+ ScalarNode: = callable
+ VariableNode: = $message
+ NullValue: = ,
+ ParameterTag: =
+ ScalarNode: = string
+ VariableNode: = $propertyPath
+ NullValue: = )
+ TextNode: = Assert that value is an IPv4 address for all values.
*/
diff --git a/tests/Printer/examples/mixin1.test b/tests/Printer/examples/mixin1.test
index acdadcfa..a2fde0b2 100644
--- a/tests/Printer/examples/mixin1.test
+++ b/tests/Printer/examples/mixin1.test
@@ -2,6 +2,9 @@
* @mixin Foobar
*/
---
-/**
- * MixinNode(ClassNode(Foobar))
+Docblock: =
+ ElementList: = /**
+ *
+ MixinTag: = @mixin
+ ClassNode: = Foobar
*/
diff --git a/tests/Printer/examples/nullable1.test b/tests/Printer/examples/nullable1.test
index 9014ebf9..806112b9 100644
--- a/tests/Printer/examples/nullable1.test
+++ b/tests/Printer/examples/nullable1.test
@@ -2,6 +2,10 @@
* @var ?Foo
*/
---
-/**
- * VarNode(NullableNode(ClassNode(Foo)))
+Docblock: =
+ ElementList: = /**
+ *
+ VarTag: = @var
+ NullableNode: = ?
+ ClassNode: = Foo
*/
diff --git a/tests/Printer/examples/param1.test b/tests/Printer/examples/param1.test
index ee64e32a..971f3a7e 100644
--- a/tests/Printer/examples/param1.test
+++ b/tests/Printer/examples/param1.test
@@ -2,6 +2,10 @@
* @param Foobar $foobar
*/
---
-/**
- * ParamNode(ClassNode(Foobar),VariableNode($foobar))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ ClassNode: = Foobar
+ VariableNode: = $foobar
*/
diff --git a/tests/Printer/examples/param2.test b/tests/Printer/examples/param2.test
index 2eb6c34d..25e0bf36 100644
--- a/tests/Printer/examples/param2.test
+++ b/tests/Printer/examples/param2.test
@@ -3,7 +3,14 @@
* @param string $barfoo
*/
---
-/**
- * ParamNode(ClassNode(Foobar),VariableNode($foobar))
- * ParamNode(ScalarNode(string),VariableNode($barfoo))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ ClassNode: = Foobar
+ VariableNode: = $foobar
+ *
+ ParamTag: = @param
+ ScalarNode: = string
+ VariableNode: = $barfoo
*/
diff --git a/tests/Printer/examples/param3.test b/tests/Printer/examples/param3.test
index eb7e6809..45cb617b 100644
--- a/tests/Printer/examples/param3.test
+++ b/tests/Printer/examples/param3.test
@@ -8,12 +8,22 @@
* @param bool $bool
*/
---
-/**
+Docblock: =
+ ElementList: = /**
* Hello World
*
- * ParamNode(ClassNode(Foobar),VariableNode($foobar))
+ *
+ ParamTag: = @param
+ ClassNode: = Foobar
+ VariableNode: = $foobar
*
- * ParamNode(ScalarNode(string),VariableNode($barfoo))
+ *
+ ParamTag: = @param
+ ScalarNode: = string
+ VariableNode: = $barfoo
*
- * ParamNode(ScalarNode(bool),VariableNode($bool))
+ *
+ ParamTag: = @param
+ ScalarNode: = bool
+ VariableNode: = $bool
*/
diff --git a/tests/Printer/examples/param4.test b/tests/Printer/examples/param4.test
index b189e757..bb1afcd6 100644
--- a/tests/Printer/examples/param4.test
+++ b/tests/Printer/examples/param4.test
@@ -2,6 +2,9 @@
* @param bool
*/
---
-/**
- * ParamNode(ScalarNode(bool),#missing#)
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ ScalarNode: = bool
*/
diff --git a/tests/Printer/examples/param5.test b/tests/Printer/examples/param5.test
index 477e13bd..bf093d8a 100644
--- a/tests/Printer/examples/param5.test
+++ b/tests/Printer/examples/param5.test
@@ -2,6 +2,11 @@
* @param bool $bool This is a boolean
*/
---
-/**
- * ParamNode(ScalarNode(bool),VariableNode($bool),TextNode(This is a boolean))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ ScalarNode: = bool
+ VariableNode: = $bool
+ TextNode: = This is a boolean
*/
diff --git a/tests/Printer/examples/param6.test b/tests/Printer/examples/param6.test
index 5e3e4f75..2a0ff792 100644
--- a/tests/Printer/examples/param6.test
+++ b/tests/Printer/examples/param6.test
@@ -4,8 +4,17 @@
* @param bool $bool This is a boolean
*/
---
-/**
- * ParamNode(ScalarNode(bool),VariableNode($bool),TextNode(This is a boolean))
+Docblock: =
+ ElementList: = /**
+ *
+ ParamTag: = @param
+ ScalarNode: = bool
+ VariableNode: = $bool
+ TextNode: = This is a boolean
* multiline comments not currently supported
- * ParamNode(ScalarNode(bool),VariableNode($bool),TextNode(This is a boolean))
+ *
+ ParamTag: = @param
+ ScalarNode: = bool
+ VariableNode: = $bool
+ TextNode: = This is a boolean
*/
diff --git a/tests/Printer/examples/php_core1.test b/tests/Printer/examples/php_core1.test
index e555336e..cdd3916a 100644
--- a/tests/Printer/examples/php_core1.test
+++ b/tests/Printer/examples/php_core1.test
@@ -24,10 +24,19 @@
* and the method name.
*/
---
-/**
+Docblock: =
+ ElementList: = /**
* Sets a user-defined error handler function
- * UnknownTag https://php.net/manual/en/function.set-error-handler.php
- * ParamNode(UnionNode(ScalarNode(callable)|NullNode()),VariableNode($error_handler),TextNode( ))
+ *
+ UnknownTag: = https://php.net/manual/en/function.set-error-handler.php
+ *
+ ParamTag: = @param
+ UnionNode: =
+ TypeList: =
+ ScalarNode: = callable|
+ NullNode: = null
+ VariableNode: = $error_handler
+ TextNode: =
* The user function needs to accept two parameters...
*
*
@@ -36,16 +45,27 @@
* errno
* The first parameter, errno, contains the
* level of the error raised, as an integer.
- * ParamNode(ScalarNode(int),VariableNode($error_types),TextNode( [optional]
))
+ *
+ ParamTag: = @param
+ ScalarNode: = int
+ VariableNode: = $error_types
+ TextNode: = [optional]
* Can be used to mask the triggering of the
* error_handler function just like the error_reporting ini setting
* controls which errors are shown. Without this mask set the
* error_handler will be called for every error
* regardless to the setting of the error_reporting setting.
*
- * ReturnNode(UnionNode(ScalarNode(callable)|NullNode()),TextNode(a string containing the previously defined error handler (if any). If))
+ *
+ ReturnTag: = @return
+ UnionNode: =
+ TypeList: =
+ ScalarNode: = callable|
+ NullNode: = null
+ TextNode: = a string containing the previously defined error handler (if any). If
* the built-in error handler is used null is returned. null is also returned
* in case of an error such as an invalid callback. If the previous error handler
* was a class method, this function will return an indexed array with the class
* and the method name.
*/
+
\ No newline at end of file
diff --git a/tests/Printer/examples/property1.test b/tests/Printer/examples/property1.test
index d05b05b0..4dcc98f4 100644
--- a/tests/Printer/examples/property1.test
+++ b/tests/Printer/examples/property1.test
@@ -2,6 +2,8 @@
* @property string $foo
*/
---
-/**
- * PropertyNode(ScalarNode(string),$foo)
+Docblock: =
+ ElementList: = /**
+ *
+ PropertyTag: =
*/
diff --git a/tests/Printer/examples/return1.test b/tests/Printer/examples/return1.test
index 9e8ab1db..573dc13e 100644
--- a/tests/Printer/examples/return1.test
+++ b/tests/Printer/examples/return1.test
@@ -2,6 +2,9 @@
* @return Foobar
*/
---
-/**
- * ReturnNode(ClassNode(Foobar))
+Docblock: =
+ ElementList: = /**
+ *
+ ReturnTag: = @return
+ ClassNode: = Foobar
*/
diff --git a/tests/Printer/examples/text1.test b/tests/Printer/examples/text1.test
index bf18af6d..b87fca2c 100644
--- a/tests/Printer/examples/text1.test
+++ b/tests/Printer/examples/text1.test
@@ -4,7 +4,8 @@
* This is some text
*/
---
-/**
+Docblock: =
+ ElementList: = /**
* Hello World
*
* This is some text
diff --git a/tests/Printer/examples/union1.test b/tests/Printer/examples/union1.test
index 379eba23..3e648291 100644
--- a/tests/Printer/examples/union1.test
+++ b/tests/Printer/examples/union1.test
@@ -1,3 +1,11 @@
/** @var string|bool|?Bar */
---
-/** VarNode(UnionNode(ScalarNode(string)|ScalarNode(bool)|NullableNode(ClassNode(Bar)))) */
+Docblock: =
+ ElementList: = /**
+ VarTag: = @var
+ UnionNode: =
+ TypeList: =
+ ScalarNode: = string|
+ ScalarNode: = bool|
+ NullableNode: = ?
+ ClassNode: = Bar */
diff --git a/tests/Printer/examples/var1.test b/tests/Printer/examples/var1.test
index 5926e58d..52dbf287 100644
--- a/tests/Printer/examples/var1.test
+++ b/tests/Printer/examples/var1.test
@@ -2,6 +2,10 @@
* @var string[]
*/
---
-/**
- * VarNode(ListNode(ScalarNode(string)))
+Docblock: =
+ ElementList: = /**
+ *
+ VarTag: = @var
+ ListNode: =
+ ScalarNode: = string[]
*/
diff --git a/tests/Printer/examples/var2.test b/tests/Printer/examples/var2.test
index 4c21c5b4..63e6b43b 100644
--- a/tests/Printer/examples/var2.test
+++ b/tests/Printer/examples/var2.test
@@ -1,3 +1,7 @@
/** @var string $foobar */
---
-/** VarNode(ScalarNode(string),VariableNode($foobar)) */
+Docblock: =
+ ElementList: = /**
+ VarTag: = @var
+ ScalarNode: = string
+ VariableNode: = $foobar */
diff --git a/tests/Printer/examples/var3.test b/tests/Printer/examples/var3.test
index 4c21c5b4..63e6b43b 100644
--- a/tests/Printer/examples/var3.test
+++ b/tests/Printer/examples/var3.test
@@ -1,3 +1,7 @@
/** @var string $foobar */
---
-/** VarNode(ScalarNode(string),VariableNode($foobar)) */
+Docblock: =
+ ElementList: = /**
+ VarTag: = @var
+ ScalarNode: = string
+ VariableNode: = $foobar */
diff --git a/tests/Unit/Ast/NodeTestCase.php b/tests/Unit/Ast/NodeTestCase.php
index bdde0e85..53c74f5a 100644
--- a/tests/Unit/Ast/NodeTestCase.php
+++ b/tests/Unit/Ast/NodeTestCase.php
@@ -17,7 +17,7 @@ class NodeTestCase extends TestCase
public function testNode(string $doc, ?Closure $assertion = null): void
{
$node = $this->parse($doc);
- $nodes = iterator_to_array($node->getDescendantNodes(), false);
+ $nodes = iterator_to_array($node->getDescendantElements(), false);
self::assertIsIterable($nodes);
self::assertEquals(0, $node->start(), 'Start offset');
self::assertEquals(strlen($doc), $node->end(), 'End offset');
From 3bc0fbf754b72d0a0b0c24a09332d4c728f55469 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 20:29:36 +0000
Subject: [PATCH 52/63] Add property traversable support
---
lib/Ast/Tag/PropertyTag.php | 28 ++++++++++++++-------------
lib/Parser.php | 4 ++--
tests/Printer/examples/property1.test | 3 ++-
tests/Unit/Ast/NodeTest.php | 1 +
4 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/lib/Ast/Tag/PropertyTag.php b/lib/Ast/Tag/PropertyTag.php
index 28e5f450..623a389c 100644
--- a/lib/Ast/Tag/PropertyTag.php
+++ b/lib/Ast/Tag/PropertyTag.php
@@ -8,29 +8,31 @@
class PropertyTag extends TagNode
{
+ protected const CHILD_NAMES = [
+ 'tag',
+ 'type',
+ 'name',
+ ];
+
/**
* @var TypeNode|null
*/
- private $type;
+ public $type;
/**
* @var Token|null
*/
- private $name;
+ public $name;
- public function __construct(?TypeNode $type, ?Token $name)
+ /**
+ * @var Token
+ */
+ public $tag;
+
+ public function __construct(Token $tag, ?TypeNode $type, ?Token $name)
{
$this->type = $type;
$this->name = $name;
- }
-
- public function name(): ?Token
- {
- return $this->name;
- }
-
- public function type(): ?TypeNode
- {
- return $this->type;
+ $this->tag = $tag;
}
}
diff --git a/lib/Parser.php b/lib/Parser.php
index 977a73f3..61207016 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -158,7 +158,7 @@ private function parseMethod(): MethodTag
private function parseProperty(): PropertyTag
{
- $this->tokens->chomp(Token::T_TAG);
+ $tag = $this->tokens->chomp(Token::T_TAG);
$type = $name = null;
if ($this->ifType()) {
$type = $this->parseTypes();
@@ -167,7 +167,7 @@ private function parseProperty(): PropertyTag
$name = $this->tokens->chomp();
}
- return new PropertyTag($type, $name);
+ return new PropertyTag($tag, $type, $name);
}
private function parseTypes(): ?TypeNode
diff --git a/tests/Printer/examples/property1.test b/tests/Printer/examples/property1.test
index 4dcc98f4..8842aa05 100644
--- a/tests/Printer/examples/property1.test
+++ b/tests/Printer/examples/property1.test
@@ -5,5 +5,6 @@
Docblock: =
ElementList: = /**
*
- PropertyTag: =
+ PropertyTag: = @property
+ ScalarNode: = string$foo
*/
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index 05b9616e..9713b961 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -47,6 +47,7 @@ function (MethodTag $methodNode) {
yield ['@param Baz\Bar $foobar This is a parameter'];
yield ['@var Baz\Bar $foobar'];
yield ['@return Baz\Bar'];
+ yield ['@property Baz\Bar $foobar'];
}
/**
From 013368c85a906d473cce4da8e70d370de4c5e365 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 20:45:14 +0000
Subject: [PATCH 53/63] Improving API
---
lib/Ast/Docblock.php | 9 ---
lib/Ast/ElementList.php | 19 ------
lib/Ast/Node.php | 103 ++++++++++++++++++--------------
lib/Printer/TestPrinter.php | 2 +-
tests/Unit/Ast/NodeTestCase.php | 4 +-
5 files changed, 60 insertions(+), 77 deletions(-)
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
index bf63d68c..40e87431 100644
--- a/lib/Ast/Docblock.php
+++ b/lib/Ast/Docblock.php
@@ -20,13 +20,4 @@ public function __construct(array $children)
{
$this->children = new ElementList($children);
}
-
- /**
- * @return ElementList
- */
- public function children(): ElementList
-
- {
- return $this->children;
- }
}
diff --git a/lib/Ast/ElementList.php b/lib/Ast/ElementList.php
index 7e67f267..93d0480c 100644
--- a/lib/Ast/ElementList.php
+++ b/lib/Ast/ElementList.php
@@ -37,25 +37,6 @@ public function getIterator(): Iterator
return new ArrayIterator($this->elements);
}
- /**
- * @template T
- * @param class-string $classFqn
- * @return ElementList
- */
- public function byClass(string $classFqn): ElementList
- {
- return new self(array_filter($this->elements, function (Element $element) use ($classFqn): bool {
- return get_class($element) === $classFqn;
- }));
- }
-
- public function byName(string $name): ElementList
- {
- return new self(array_filter($this->elements, function (Element $element) use ($classFqn): bool {
- return get_class($element) === $classFqn;
- }));
- }
-
/**
* @return Element[]
*/
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index a02ca72f..b2746f4f 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -26,31 +26,12 @@ public function toString(): string
*/
public function tokens(): Generator
{
- yield from $this->findTokens($this->getChildElements());
+ yield from $this->findTokens($this->children());
}
/**
- * @return Generator
- * @param iterable> $nodes
+ * Return the short name of the node class (e.g. ParamTag)
*/
- private function findTokens(iterable $nodes): Generator
- {
- foreach ($nodes as $node) {
- if ($node instanceof Token) {
- yield $node;
- continue;
- }
-
- if ($node instanceof Node) {
- yield from $node->tokens();
- }
-
- if (is_array($node)) {
- yield from $this->findTokens($node);
- }
- }
- }
-
public function shortName(): string
{
return substr(get_class($this), strrpos(get_class($this), '\\') + 1);
@@ -59,10 +40,18 @@ public function shortName(): string
/**
* @return Generator
*/
- public function getDescendantElements(): Generator
+ public function selfAndDescendantElements(): Generator
{
yield $this;
- yield from $this->walkNodes($this->getChildElements());
+ yield from $this->traverseNodes($this->children());
+ }
+
+ /**
+ * @return Generator
+ */
+ public function descendantElements(): Generator
+ {
+ yield from $this->traverseNodes($this->children());
}
/**
@@ -70,17 +59,17 @@ public function getDescendantElements(): Generator
*
* @return Generator
*/
- private function walkNodes(iterable $nodes): Generator
+ private function traverseNodes(iterable $nodes): Generator
{
$result = [];
foreach ($nodes as $child) {
if (is_array($child)) {
- yield from $this->walkNodes($child);
+ yield from $this->traverseNodes($child);
continue;
}
if ($child instanceof Node) {
- yield from $child->getDescendantElements();
+ yield from $child->selfAndDescendantElements();
continue;
}
@@ -94,7 +83,7 @@ private function walkNodes(iterable $nodes): Generator
/**
* @return Generator
*/
- public function getChildElements(): Generator
+ public function children(): Generator
{
foreach (static::CHILD_NAMES as $name) {
$child = $this->$name;
@@ -106,29 +95,12 @@ public function getChildElements(): Generator
public function start(): int
{
- return $this->startOf($this->getChildElements());
- }
-
- /**
- * @param iterable> $elements
- */
- public function startOf(iterable $elements): int
- {
- foreach ($elements as $element) {
- if ($element instanceof Element) {
- return $element->start();
- }
- if (is_array($element)) {
- return $this->startOf($element);
- }
- }
-
- return 0;
+ return $this->startOf($this->children());
}
public function end(): int
{
- return $this->endOf(array_reverse(iterator_to_array($this->getChildElements(), false)));
+ return $this->endOf(array_reverse(iterator_to_array($this->children(), false)));
}
/**
@@ -156,4 +128,43 @@ private function length(): int
{
return $this->end() - $this->start();
}
+
+ /**
+ * @param iterable> $elements
+ */
+ private function startOf(iterable $elements): int
+ {
+ foreach ($elements as $element) {
+ if ($element instanceof Element) {
+ return $element->start();
+ }
+ if (is_array($element)) {
+ return $this->startOf($element);
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * @return Generator
+ * @param iterable> $nodes
+ */
+ private function findTokens(iterable $nodes): Generator
+ {
+ foreach ($nodes as $node) {
+ if ($node instanceof Token) {
+ yield $node;
+ continue;
+ }
+
+ if ($node instanceof Node) {
+ yield from $node->tokens();
+ }
+
+ if (is_array($node)) {
+ yield from $this->findTokens($node);
+ }
+ }
+ }
}
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index fb281ad7..79fa260f 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -42,7 +42,7 @@ public function print(Node $node): string
{
$this->indent++;
$out = sprintf('%s: = ', $node->shortName());
- foreach ($node->getChildElements() as $child) {
+ foreach ($node->children() as $child) {
$out .= $this->printElement($child);
}
$this->indent--;
diff --git a/tests/Unit/Ast/NodeTestCase.php b/tests/Unit/Ast/NodeTestCase.php
index 53c74f5a..85d36fe5 100644
--- a/tests/Unit/Ast/NodeTestCase.php
+++ b/tests/Unit/Ast/NodeTestCase.php
@@ -17,7 +17,7 @@ class NodeTestCase extends TestCase
public function testNode(string $doc, ?Closure $assertion = null): void
{
$node = $this->parse($doc);
- $nodes = iterator_to_array($node->getDescendantElements(), false);
+ $nodes = iterator_to_array($node->selfAndDescendantElements(), false);
self::assertIsIterable($nodes);
self::assertEquals(0, $node->start(), 'Start offset');
self::assertEquals(strlen($doc), $node->end(), 'End offset');
@@ -34,7 +34,7 @@ public function testPartialParse(string $doc): void
{
$node = $this->parse($doc);
$partial = [];
- foreach ($node->getChildElements() as $child) {
+ foreach ($node->children() as $child) {
$partial[] = $child->toString();
$node = $this->parse(implode(' ', $partial));
self::assertInstanceOf(Element::class, $node);
From 1384d0b9da4e1396eb17095988e0031a86d63e73 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 20:45:47 +0000
Subject: [PATCH 54/63] Apply CS fix
---
lib/Ast/Node.php | 51 ++++++++++++++++++-------------------
lib/Ast/TextNode.php | 2 --
lib/Ast/Token.php | 2 --
lib/Ast/Tokens.php | 1 -
lib/Ast/TypeList.php | 1 -
lib/Ast/TypeNode.php | 2 --
lib/Ast/UnknownTag.php | 2 --
lib/Ast/VariableNode.php | 2 --
lib/Printer/TestPrinter.php | 23 -----------------
tests/Unit/Ast/NodeTest.php | 12 +++------
10 files changed, 29 insertions(+), 69 deletions(-)
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index b2746f4f..1ba65525 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -3,7 +3,6 @@
namespace Phpactor\Docblock\Ast;
use Generator;
-use Phpactor\Docblock\Ast\Token;
abstract class Node implements Element
{
@@ -12,7 +11,8 @@ abstract class Node implements Element
public function toString(): string
{
- $out = str_repeat(' ', $this->length());;
+ $out = str_repeat(' ', $this->length());
+ ;
$start = $this->start();
foreach ($this->tokens() as $token) {
$out = substr_replace($out, $token->value, $token->start() - $start, $token->length());
@@ -54,6 +54,29 @@ public function descendantElements(): Generator
yield from $this->traverseNodes($this->children());
}
+ /**
+ * @return Generator
+ */
+ public function children(): Generator
+ {
+ foreach (static::CHILD_NAMES as $name) {
+ $child = $this->$name;
+ if (null !== $child) {
+ yield $child;
+ }
+ }
+ }
+
+ public function start(): int
+ {
+ return $this->startOf($this->children());
+ }
+
+ public function end(): int
+ {
+ return $this->endOf(array_reverse(iterator_to_array($this->children(), false)));
+ }
+
/**
* @param iterable> $nodes
*
@@ -80,36 +103,12 @@ private function traverseNodes(iterable $nodes): Generator
}
}
- /**
- * @return Generator
- */
- public function children(): Generator
- {
- foreach (static::CHILD_NAMES as $name) {
- $child = $this->$name;
- if (null !== $child) {
- yield $child;
- }
- }
- }
-
- public function start(): int
- {
- return $this->startOf($this->children());
- }
-
- public function end(): int
- {
- return $this->endOf(array_reverse(iterator_to_array($this->children(), false)));
- }
-
/**
* @param iterable> $elements
*/
private function endOf(iterable $elements): int
{
foreach ($elements as $element) {
-
if (null === $element) {
continue;
}
diff --git a/lib/Ast/TextNode.php b/lib/Ast/TextNode.php
index a09d8c78..4168c69f 100644
--- a/lib/Ast/TextNode.php
+++ b/lib/Ast/TextNode.php
@@ -2,8 +2,6 @@
namespace Phpactor\Docblock\Ast;
-use Phpactor\Docblock\Ast\Token;
-
class TextNode extends Node
{
protected const CHILD_NAMES = [
diff --git a/lib/Ast/Token.php b/lib/Ast/Token.php
index 89bbb3bd..15cf7588 100644
--- a/lib/Ast/Token.php
+++ b/lib/Ast/Token.php
@@ -2,8 +2,6 @@
namespace Phpactor\Docblock\Ast;
-use Phpactor\Docblock\Ast\Element;
-
final class Token implements Element
{
public const T_PHPDOC_OPEN = 'PHPDOC_OPEN';
diff --git a/lib/Ast/Tokens.php b/lib/Ast/Tokens.php
index 6db6d5b5..9a697ad6 100644
--- a/lib/Ast/Tokens.php
+++ b/lib/Ast/Tokens.php
@@ -5,7 +5,6 @@
use ArrayIterator;
use IteratorAggregate;
use RuntimeException;
-use Phpactor\Docblock\Ast\Token;
final class Tokens implements IteratorAggregate
{
diff --git a/lib/Ast/TypeList.php b/lib/Ast/TypeList.php
index 220dc1b7..a3d1d8d4 100644
--- a/lib/Ast/TypeList.php
+++ b/lib/Ast/TypeList.php
@@ -5,7 +5,6 @@
use ArrayIterator;
use Countable;
use IteratorAggregate;
-use Phpactor\Docblock\Ast\Token;
use RuntimeException;
/**
diff --git a/lib/Ast/TypeNode.php b/lib/Ast/TypeNode.php
index a6f58279..4cefe3ea 100644
--- a/lib/Ast/TypeNode.php
+++ b/lib/Ast/TypeNode.php
@@ -2,8 +2,6 @@
namespace Phpactor\Docblock\Ast;
-use Phpactor\Docblock\Ast\Token;
-
abstract class TypeNode extends Node
{
}
diff --git a/lib/Ast/UnknownTag.php b/lib/Ast/UnknownTag.php
index f16436e7..6ca6fefc 100644
--- a/lib/Ast/UnknownTag.php
+++ b/lib/Ast/UnknownTag.php
@@ -2,8 +2,6 @@
namespace Phpactor\Docblock\Ast;
-use Phpactor\Docblock\Ast\Token;
-
class UnknownTag extends TagNode
{
/**
diff --git a/lib/Ast/VariableNode.php b/lib/Ast/VariableNode.php
index 846f6882..f53301f5 100644
--- a/lib/Ast/VariableNode.php
+++ b/lib/Ast/VariableNode.php
@@ -2,8 +2,6 @@
namespace Phpactor\Docblock\Ast;
-use Phpactor\Docblock\Ast\Token;
-
class VariableNode extends Node
{
protected const CHILD_NAMES = [
diff --git a/lib/Printer/TestPrinter.php b/lib/Printer/TestPrinter.php
index 79fa260f..90fdd8fe 100644
--- a/lib/Printer/TestPrinter.php
+++ b/lib/Printer/TestPrinter.php
@@ -2,32 +2,10 @@
namespace Phpactor\Docblock\Printer;
-use Phpactor\Docblock\Ast\Tag\DeprecatedTag;
-use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Element;
-use Phpactor\Docblock\Ast\Tag\MethodTag;
-use Phpactor\Docblock\Ast\Tag\MixinTag;
-use Phpactor\Docblock\Ast\ParameterList;
-use Phpactor\Docblock\Ast\Tag\ParameterTag;
-use Phpactor\Docblock\Ast\Tag\PropertyTag;
-use Phpactor\Docblock\Ast\Tag\ReturnTag;
-use Phpactor\Docblock\Ast\TextNode;
-use Phpactor\Docblock\Ast\TypeList;
-use Phpactor\Docblock\Ast\TypeNode;
use Phpactor\Docblock\Ast\Node;
-use Phpactor\Docblock\Ast\Tag\ParamTag;
-use Phpactor\Docblock\Ast\Type\GenericNode;
-use Phpactor\Docblock\Ast\Type\ListNode;
-use Phpactor\Docblock\Ast\Type\NullNode;
-use Phpactor\Docblock\Ast\Type\NullableNode;
-use Phpactor\Docblock\Ast\Type\UnionNode;
-use Phpactor\Docblock\Ast\UnknownTag;
-use Phpactor\Docblock\Ast\ValueNode;
-use Phpactor\Docblock\Ast\Tag\VarTag;
-use Phpactor\Docblock\Ast\VariableNode;
use Phpactor\Docblock\Printer;
use Phpactor\Docblock\Ast\Token;
-use RuntimeException;
final class TestPrinter implements Printer
{
@@ -72,5 +50,4 @@ private function newLine(): string
{
return "\n".str_repeat(' ', $this->indent);
}
-
}
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index 9713b961..e371a2e9 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -3,10 +3,7 @@
namespace Phpactor\Docblock\Tests\Unit\Ast;
use Generator;
-use PHPUnit\Framework\TestCase;
use Phpactor\Docblock\Ast\Tag\MethodTag;
-use Phpactor\Docblock\Ast\Node;
-use Phpactor\Docblock\Ast\Tag\ParamTag;
use Phpactor\Docblock\Ast\Tag\ReturnTag;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
@@ -34,7 +31,7 @@ private function provideTags()
yield [ '@param string $foo This is a parameter'];
yield [
'@method static Baz\Bar bar(string $boo, string $baz)',
- function (MethodTag $methodNode) {
+ function (MethodTag $methodNode): void {
self::assertEquals('@method static Baz\Bar bar(string $boo, string $baz)', $methodNode->toString());
self::assertEquals('string $boo, string $baz', $methodNode->parameters->toString());
self::assertEquals('static', $methodNode->static->value);
@@ -58,7 +55,7 @@ private function provideTypes(): Generator
yield 'scalar' => ['string'];
yield 'union' => [
'@return string|int|bool|float|mixed',
- function (ReturnTag $return) {
+ function (ReturnTag $return): void {
$type = $return->type;
assert($type instanceof UnionNode);
self::assertInstanceOf(UnionNode::class, $type);
@@ -68,16 +65,15 @@ function (ReturnTag $return) {
];
yield 'list' => [
'@return Foo[]',
- function (ReturnTag $return) {
+ function (ReturnTag $return): void {
self::assertInstanceOf(ListNode::class, $return->type);
}
];
yield 'generic' => [
'@return Foo, Baz|Bar>',
- function (ReturnTag $return) {
+ function (ReturnTag $return): void {
self::assertInstanceOf(GenericNode::class, $return->type);
}
];
}
-
}
From aa7e948d727f070f810edb95eb0b92cabb2b2600 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 21:05:51 +0000
Subject: [PATCH 55/63] Added more API methods
---
lib/Ast/Node.php | 69 ++++++++++++++++++++++++++++++++++---
tests/Unit/Ast/NodeTest.php | 34 +++++++++++++++---
2 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index 1ba65525..fe44c695 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -49,34 +49,95 @@ public function selfAndDescendantElements(): Generator
/**
* @return Generator
*/
- public function descendantElements(): Generator
+ public function descendantElements(?string $elementFqn = null): Generator
{
- yield from $this->traverseNodes($this->children());
+ if (null === $elementFqn) {
+ yield from $this->traverseNodes($this->children());
+ return;
+ }
+
+ foreach ($this->traverseNodes($this->children()) as $element) {
+ if ($element instanceof $elementFqn) {
+ yield $element;
+ }
+ }
+ }
+
+ public function hasDescendant(string $elementFqn): bool
+ {
+ foreach ($this->descendantElements($elementFqn) as $element) {
+ return true;
+ }
+
+ return false;
}
/**
+ * @template T of Element
+ * @param class-string $elementFqn
+ * @return T|null
+ */
+ public function firstDescendant(string $elementFqn): ?Element
+ {
+ foreach ($this->descendantElements($elementFqn) as $element) {
+ return $element;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param class-string $elementFqn
* @return Generator
*/
- public function children(): Generator
+ public function children(?string $elementFqn = null): Generator
{
+ if (!$elementFqn) {
+ foreach (static::CHILD_NAMES as $name) {
+ $child = $this->$name;
+ if (null !== $child) {
+ yield $child;
+ }
+ }
+
+ return;
+ }
+
foreach (static::CHILD_NAMES as $name) {
$child = $this->$name;
- if (null !== $child) {
+ if ($child instanceof $elementFqn) {
yield $child;
}
}
}
+ /**
+ * Return the bytes offset for the start of this node.
+ */
public function start(): int
{
return $this->startOf($this->children());
}
+ /**
+ * Return the bytes offset for the end of this node.
+ */
public function end(): int
{
return $this->endOf(array_reverse(iterator_to_array($this->children(), false)));
}
+ public function hasChild(string $elementFqn): bool
+ {
+ foreach ($this->children() as $child) {
+ if ($child instanceof $elementFqn) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* @param iterable> $nodes
*
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index e371a2e9..01f1e9d3 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -5,9 +5,12 @@
use Generator;
use Phpactor\Docblock\Ast\Tag\MethodTag;
use Phpactor\Docblock\Ast\Tag\ReturnTag;
+use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
use Phpactor\Docblock\Ast\Type\ListNode;
+use Phpactor\Docblock\Ast\Type\ScalarNode;
use Phpactor\Docblock\Ast\Type\UnionNode;
+use Prophecy\Doubler\Generator\Node\MethodNode;
class NodeTest extends NodeTestCase
{
@@ -16,19 +19,36 @@ class NodeTest extends NodeTestCase
*/
public function provideNode(): Generator
{
+ yield from $this->provideApiTest();
yield from $this->provideTags();
yield from $this->provideTypes();
}
+ /**
+ * @return Generator
+ */
+ private function provideApiTest(): Generator
+ {
+ yield [
+ '@method static Baz\Bar bar(string $boo, string $baz)',
+ function (MethodTag $methodNode): void {
+ self::assertTrue($methodNode->hasChild(ClassNode::class));
+ self::assertFalse($methodNode->hasChild(MethodTag::class));
+ self::assertCount(7, iterator_to_array($methodNode->children()));
+ self::assertCount(1, iterator_to_array($methodNode->children(ClassNode::class)));
+ self::assertTrue($methodNode->hasDescendant(ScalarNode::class));
+ self::assertFalse($methodNode->hasDescendant(MethodNode::class));
+ self::assertCount(2, iterator_to_array($methodNode->descendantElements(ScalarNode::class)));
+ self::assertInstanceOf(ScalarNode::class, $methodNode->firstDescendant(ScalarNode::class));
+ }
+ ];
+ }
+
/**
* @return Generator
*/
private function provideTags()
{
- yield [ '@deprecated This is deprecated'];
- yield [ '/** This is docblock @deprecated Foo */'];
- yield [ '@mixin Foo\Bar'];
- yield [ '@param string $foo This is a parameter'];
yield [
'@method static Baz\Bar bar(string $boo, string $baz)',
function (MethodTag $methodNode): void {
@@ -39,8 +59,14 @@ function (MethodTag $methodNode): void {
self::assertEquals('bar', $methodNode->name->toString());
self::assertEquals('(', $methodNode->parenOpen->toString());
self::assertEquals(')', $methodNode->parenClose->toString());
+ self::assertTrue($methodNode->hasChild(ClassNode::class));
+ self::assertFalse($methodNode->hasChild(MethodTag::class));
}
];
+ yield [ '@deprecated This is deprecated'];
+ yield [ '/** This is docblock @deprecated Foo */'];
+ yield [ '@mixin Foo\Bar'];
+ yield [ '@param string $foo This is a parameter'];
yield ['@param Baz\Bar $foobar This is a parameter'];
yield ['@var Baz\Bar $foobar'];
yield ['@return Baz\Bar'];
From 878745c0744a908ccbcb1750548bae50b2a784e1 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 21:07:20 +0000
Subject: [PATCH 56/63] Updated composer, removed baseline
---
composer.json | 9 +-
lsp-client.log | 437 ++++++++++++++++++++++++++++++++++++
phpstan-baseline.neon | 387 -------------------------------
phpstan.neon | 2 -
test.php | 4 +
tests/Unit/Ast/NodeTest.php | 2 +-
6 files changed, 450 insertions(+), 391 deletions(-)
create mode 100644 lsp-client.log
delete mode 100644 phpstan-baseline.neon
create mode 100644 test.php
diff --git a/composer.json b/composer.json
index bd7deaf2..d15867d4 100644
--- a/composer.json
+++ b/composer.json
@@ -39,5 +39,12 @@
}
},
"minimum-stability": "dev",
- "prefer-stable": true
+ "prefer-stable": true,
+ "scripts": {
+ "integrate": [
+ "vendor/bin/phpunit",
+ "vendor/bin/php-cs-fixer fix --allow-risky=yes",
+ "vendor/bin/phpstan analyse"
+ ]
+ }
}
diff --git a/lsp-client.log b/lsp-client.log
new file mode 100644
index 00000000..c0eb594a
--- /dev/null
+++ b/lsp-client.log
@@ -0,0 +1,437 @@
+#######
+LanguageClient 0.1.120
+#######
+12:36:37 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[!!get(g:, 'LanguageClient_autoStart', 1), get(g:, 'LanguageClient_serverCommands', {}), get(g:, 'LanguageClient_selectionUI', v:null), get(g:, 'LanguageClient_trace', v:null), expand(get(g:, 'LanguageClient_settingsPath', '.vim/settings.json')), !!get(g:, 'LanguageClient_loadSettings', 1), get(g:, 'LanguageClient_rootMarkers', v:null), get(g:, 'LanguageClient_changeThrottle', v:null), get(g:, 'LanguageClient_waitOutputTimeout', v:null), !!get(g:, 'LanguageClient_diagnosticsEnable', 1), get(g:, 'LanguageClient_diagnosticsList', 'Quickfix'), get(g:, 'LanguageClient_diagnosticsDisplay', {}), get(g:, 'LanguageClient_windowLogMessageLevel', 'Warning'), get(g:, 'LanguageClient_hoverPreview', 'Auto'), get(g:, 'LanguageClient_completionPreferTextEdit', 0), has('nvim')]"],"id":41}
+12:36:37 INFO reader-main src/vim.rs:380 <= None {"id": 41, "jsonrpc": "2.0", "result": [1, {}, null, null, ".vim/settings.json", 1, null, null, null, 1, "Quickfix", {}, "Warning", "Auto", 1, 1]}
+12:36:37 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[get(g:, 'LanguageClient_diagnosticsSignsMax', v:null), get(g:, 'LanguageClient_documentHighlightDisplay', {})]"],"id":42}
+12:36:37 INFO reader-main src/vim.rs:380 <= None {"id": 42, "jsonrpc": "2.0", "result": [null, {}]}
+12:36:37 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["get(g:, 'loaded_fzf')"],"id":43}
+12:36:37 INFO reader-main src/vim.rs:380 <= None {"id": 43, "jsonrpc": "2.0", "result": 1}
+12:36:37 WARN main src/languageclient.rs:2173 Failed to start language server automatically. No language server command found for file type: markdown.
+12:36:37 INFO main src/languageclient.rs:2178 End languageClient/handleBufReadPost
+12:36:37 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:37 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(0)]
+12:36:37 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:37 INFO main src/languageclient.rs:2283 Updating signs: []
+12:36:37 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"execute","params":[],"id":44}
+12:36:37 INFO reader-main src/vim.rs:380 <= None {"id": 44, "jsonrpc": "2.0", "result": 0}
+12:36:37 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"nvim_buf_clear_highlight","params":[0,1,0,22]}
+12:36:37 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"s:AddHighlights","params":[1,[]]}
+12:36:37 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:37 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 1, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:37 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:37 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(1)]
+12:36:37 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:37 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 2, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(2)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 3, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(3)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 4, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(4)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 5, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(5)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 6, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(6)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 7, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(7)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 8, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(8)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:36:38 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 9, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:36:38 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:36:38 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(9)]
+12:36:38 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:36:38 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:37:06 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 9, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:37:06 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:37:06 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(9)]
+12:37:06 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:37:06 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:37:06 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 10, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:37:06 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:37:06 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(10)]
+12:37:06 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:37:06 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:37:07 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 11, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+12:37:07 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:37:07 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(11)]
+12:37:07 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+12:37:07 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:29 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 2, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:29 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:29 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(2)]
+12:47:29 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:29 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:30 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 1, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:30 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:30 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(1)]
+12:47:30 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:30 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:31 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 2, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:31 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:31 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(2)]
+12:47:31 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:31 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 3, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(3)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 4, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(4)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 5, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(5)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 6, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(6)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 7, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(7)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 8, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(8)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 9, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(9)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 10, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(10)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 11, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(11)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 12, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(12)]
+12:47:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:33 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 13, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:33 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:33 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(13)]
+12:47:33 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:33 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:33 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 14, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:33 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:33 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(14)]
+12:47:33 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:33 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:33 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 13, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:33 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:33 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(13)]
+12:47:33 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:33 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 14, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(14)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 15, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(15)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 16, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(16)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 17, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(17)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 18, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(18)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 19, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(19)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 20, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(20)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:34 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 21, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:34 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:34 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(21)]
+12:47:34 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:34 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 22, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(22)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 23, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(23)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 24, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(24)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 25, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(25)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 26, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(26)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 27, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(27)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 28, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(28)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 29, "LSP#visible_line_start()": 1, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(29)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(1), Number(29)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 28, "LSP#visible_line_start()": 1, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(28)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(1), Number(29)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 27, "LSP#visible_line_start()": 1, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:35 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(27)]
+12:47:35 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(1), Number(29)]
+12:47:35 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+12:47:36 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 26, "LSP#visible_line_start()": 1, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+12:47:36 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+12:47:36 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(26)]
+12:47:36 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(1), Number(29)]
+12:47:36 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:00 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 26, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:00 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:00 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(26)]
+21:05:00 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(29)]
+21:05:00 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:02 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 25, "LSP#visible_line_start()": 12, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:02 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:02 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(25)]
+21:05:02 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(12), Number(29)]
+21:05:02 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:02 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 24, "LSP#visible_line_start()": 12, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:02 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:02 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(24)]
+21:05:02 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(12), Number(29)]
+21:05:02 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:06 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 24, "LSP#visible_line_start()": 12, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:06 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:06 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(24)]
+21:05:06 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(12), Number(29)]
+21:05:06 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:06 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 25, "LSP#visible_line_start()": 12, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:06 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:06 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(25)]
+21:05:06 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(12), Number(29)]
+21:05:06 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:07 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 26, "LSP#visible_line_start()": 12, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:07 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:07 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(26)]
+21:05:07 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(12), Number(29)]
+21:05:07 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:09 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleTextChanged", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:09 INFO main src/languageclient.rs:2183 Begin languageClient/handleTextChanged
+21:05:09 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:09 INFO main src/languageclient.rs:1760 Begin textDocument/didChange
+21:05:09 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:09 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["text"]. Exps: ["LSP#text()"]
+21:05:09 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[LSP#text()]"],"id":45}
+21:05:09 INFO reader-main src/vim.rs:380 <= None {"id": 45, "jsonrpc": "2.0", "result": [["{", " \"name\": \"phpactor/docblock\",", " \"description\": \"Simple Docblock Parser\",", " \"type\": \"library\",", " \"autoload\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\\": \"lib/\"", " }", " },", " \"autoload-dev\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"", " }", " },", " \"require-dev\": {", " \"phpunit/phpunit\": \"^6.5\"", " },", " \"authors\": [", " {", " \"name\": \"Daniel Leech\",", " \"email\": \"daniel@dantleech.com\"", " }", " ],", " \"license\": \"MIT\",", " \"extra\": {", " \"branch-alias\": {", " \"dev-master\": \"\"", " }", " }", "}", ""]]}
+21:05:09 INFO main src/languageclient.rs:48 gather_args: [Text] = [Array([String("{"), String(" \"name\": \"phpactor/docblock\","), String(" \"description\": \"Simple Docblock Parser\","), String(" \"type\": \"library\","), String(" \"autoload\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\\": \"lib/\""), String(" }"), String(" },"), String(" \"autoload-dev\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\""), String(" }"), String(" },"), String(" \"require-dev\": {"), String(" \"phpunit/phpunit\": \"^6.5\""), String(" },"), String(" \"authors\": ["), String(" {"), String(" \"name\": \"Daniel Leech\","), String(" \"email\": \"daniel@dantleech.com\""), String(" }"), String(" ],"), String(" \"license\": \"MIT\","), String(" \"extra\": {"), String(" \"branch-alias\": {"), String(" \"dev-master\": \"\""), String(" }"), String(" }"), String("}"), String("")])]
+21:05:09 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.text: "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"1.0.x-dev\"\n }\n }\n}\n" ==> "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"\"\n }\n }\n}\n"
+21:05:09 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.version: 13 ==> 14
+21:05:09 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"text":"{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"\"\n }\n }\n}\n"}],"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json","version":14}}}
+21:05:09 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleTextChanged", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:09 INFO main src/languageclient.rs:2183 Begin languageClient/handleTextChanged
+21:05:09 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:09 INFO main src/languageclient.rs:1760 Begin textDocument/didChange
+21:05:09 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:09 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["text"]. Exps: ["LSP#text()"]
+21:05:09 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[LSP#text()]"],"id":46}
+21:05:09 INFO reader-main src/vim.rs:380 <= None {"id": 46, "jsonrpc": "2.0", "result": [["{", " \"name\": \"phpactor/docblock\",", " \"description\": \"Simple Docblock Parser\",", " \"type\": \"library\",", " \"autoload\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\\": \"lib/\"", " }", " },", " \"autoload-dev\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"", " }", " },", " \"require-dev\": {", " \"phpunit/phpunit\": \"^6.5\"", " },", " \"authors\": [", " {", " \"name\": \"Daniel Leech\",", " \"email\": \"daniel@dantleech.com\"", " }", " ],", " \"license\": \"MIT\",", " \"extra\": {", " \"branch-alias\": {", " \"dev-master\": \"0\"", " }", " }", "}", ""]]}
+21:05:09 INFO main src/languageclient.rs:48 gather_args: [Text] = [Array([String("{"), String(" \"name\": \"phpactor/docblock\","), String(" \"description\": \"Simple Docblock Parser\","), String(" \"type\": \"library\","), String(" \"autoload\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\\": \"lib/\""), String(" }"), String(" },"), String(" \"autoload-dev\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\""), String(" }"), String(" },"), String(" \"require-dev\": {"), String(" \"phpunit/phpunit\": \"^6.5\""), String(" },"), String(" \"authors\": ["), String(" {"), String(" \"name\": \"Daniel Leech\","), String(" \"email\": \"daniel@dantleech.com\""), String(" }"), String(" ],"), String(" \"license\": \"MIT\","), String(" \"extra\": {"), String(" \"branch-alias\": {"), String(" \"dev-master\": \"0\""), String(" }"), String(" }"), String("}"), String("")])]
+21:05:09 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.text: "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"\"\n }\n }\n}\n" ==> "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"0\"\n }\n }\n}\n"
+21:05:09 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.version: 14 ==> 15
+21:05:09 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"text":"{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"0\"\n }\n }\n}\n"}],"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json","version":15}}}
+21:05:11 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleTextChanged", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:11 INFO main src/languageclient.rs:2183 Begin languageClient/handleTextChanged
+21:05:11 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:11 INFO main src/languageclient.rs:1760 Begin textDocument/didChange
+21:05:11 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:11 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["text"]. Exps: ["LSP#text()"]
+21:05:11 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[LSP#text()]"],"id":47}
+21:05:11 INFO reader-main src/vim.rs:380 <= None {"id": 47, "jsonrpc": "2.0", "result": [["{", " \"name\": \"phpactor/docblock\",", " \"description\": \"Simple Docblock Parser\",", " \"type\": \"library\",", " \"autoload\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\\": \"lib/\"", " }", " },", " \"autoload-dev\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"", " }", " },", " \"require-dev\": {", " \"phpunit/phpunit\": \"^6.5\"", " },", " \"authors\": [", " {", " \"name\": \"Daniel Leech\",", " \"email\": \"daniel@dantleech.com\"", " }", " ],", " \"license\": \"MIT\",", " \"extra\": {", " \"branch-alias\": {", " \"dev-master\": \"\"", " }", " }", "}", ""]]}
+21:05:11 INFO main src/languageclient.rs:48 gather_args: [Text] = [Array([String("{"), String(" \"name\": \"phpactor/docblock\","), String(" \"description\": \"Simple Docblock Parser\","), String(" \"type\": \"library\","), String(" \"autoload\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\\": \"lib/\""), String(" }"), String(" },"), String(" \"autoload-dev\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\""), String(" }"), String(" },"), String(" \"require-dev\": {"), String(" \"phpunit/phpunit\": \"^6.5\""), String(" },"), String(" \"authors\": ["), String(" {"), String(" \"name\": \"Daniel Leech\","), String(" \"email\": \"daniel@dantleech.com\""), String(" }"), String(" ],"), String(" \"license\": \"MIT\","), String(" \"extra\": {"), String(" \"branch-alias\": {"), String(" \"dev-master\": \"\""), String(" }"), String(" }"), String("}"), String("")])]
+21:05:11 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.text: "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"0\"\n }\n }\n}\n" ==> "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"\"\n }\n }\n}\n"
+21:05:11 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.version: 15 ==> 16
+21:05:11 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"text":"{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"\"\n }\n }\n}\n"}],"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json","version":16}}}
+21:05:11 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleTextChanged", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:11 INFO main src/languageclient.rs:2183 Begin languageClient/handleTextChanged
+21:05:11 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:11 INFO main src/languageclient.rs:1760 Begin textDocument/didChange
+21:05:11 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:11 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["text"]. Exps: ["LSP#text()"]
+21:05:11 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[LSP#text()]"],"id":48}
+21:05:11 INFO reader-main src/vim.rs:380 <= None {"id": 48, "jsonrpc": "2.0", "result": [["{", " \"name\": \"phpactor/docblock\",", " \"description\": \"Simple Docblock Parser\",", " \"type\": \"library\",", " \"autoload\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\\": \"lib/\"", " }", " },", " \"autoload-dev\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"", " }", " },", " \"require-dev\": {", " \"phpunit/phpunit\": \"^6.5\"", " },", " \"authors\": [", " {", " \"name\": \"Daniel Leech\",", " \"email\": \"daniel@dantleech.com\"", " }", " ],", " \"license\": \"MIT\",", " \"extra\": {", " \"branch-alias\": {", " \"dev-master\": \"1.0.x-dev\"", " }", " }", "}", ""]]}
+21:05:11 INFO main src/languageclient.rs:48 gather_args: [Text] = [Array([String("{"), String(" \"name\": \"phpactor/docblock\","), String(" \"description\": \"Simple Docblock Parser\","), String(" \"type\": \"library\","), String(" \"autoload\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\\": \"lib/\""), String(" }"), String(" },"), String(" \"autoload-dev\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\""), String(" }"), String(" },"), String(" \"require-dev\": {"), String(" \"phpunit/phpunit\": \"^6.5\""), String(" },"), String(" \"authors\": ["), String(" {"), String(" \"name\": \"Daniel Leech\","), String(" \"email\": \"daniel@dantleech.com\""), String(" }"), String(" ],"), String(" \"license\": \"MIT\","), String(" \"extra\": {"), String(" \"branch-alias\": {"), String(" \"dev-master\": \"1.0.x-dev\""), String(" }"), String(" }"), String("}"), String("")])]
+21:05:11 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.version: 16 ==> 17
+21:05:11 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.text: "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"\"\n }\n }\n}\n" ==> "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"1.0.x-dev\"\n }\n }\n}\n"
+21:05:11 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"text":"{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"1.0.x-dev\"\n }\n }\n}\n"}],"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json","version":17}}}
+21:05:11 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleBufWritePost", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:11 INFO main src/languageclient.rs:2213 Begin languageClient/handleBufWritePost
+21:05:11 INFO main src/languageclient.rs:1830 Begin textDocument/didSave
+21:05:11 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:11 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didSave","params":{"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json"}}}
+21:05:14 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 25, "LSP#visible_line_start()": 12, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:14 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:14 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(25)]
+21:05:14 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(12), Number(29)]
+21:05:14 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:14 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 24, "LSP#visible_line_start()": 12, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:14 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:14 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(24)]
+21:05:14 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(12), Number(29)]
+21:05:14 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:15 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleBufWritePost", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:15 INFO main src/languageclient.rs:2213 Begin languageClient/handleBufWritePost
+21:05:15 INFO main src/languageclient.rs:1830 Begin textDocument/didSave
+21:05:15 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:15 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didSave","params":{"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json"}}}
+21:05:15 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 23, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:15 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:15 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(23)]
+21:05:15 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(29)]
+21:05:15 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:17 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 24, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:17 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:17 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(24)]
+21:05:17 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(29)]
+21:05:17 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:17 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 25, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:17 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:17 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(25)]
+21:05:17 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(29)]
+21:05:17 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 26, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:18 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(26)]
+21:05:18 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(29)]
+21:05:18 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 27, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:18 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(27)]
+21:05:18 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(29)]
+21:05:18 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 28, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 29, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:18 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(28)]
+21:05:18 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(29)]
+21:05:18 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 24, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 24, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:18 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(24)]
+21:05:18 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(24)]
+21:05:18 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleTextChanged", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:18 INFO main src/languageclient.rs:2183 Begin languageClient/handleTextChanged
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:18 INFO main src/languageclient.rs:1760 Begin textDocument/didChange
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:18 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["text"]. Exps: ["LSP#text()"]
+21:05:18 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[LSP#text()]"],"id":49}
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"id": 49, "jsonrpc": "2.0", "result": [["{", " \"name\": \"phpactor/docblock\",", " \"description\": \"Simple Docblock Parser\",", " \"type\": \"library\",", " \"autoload\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\\": \"lib/\"", " }", " },", " \"autoload-dev\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"", " }", " },", " \"require-dev\": {", " \"phpunit/phpunit\": \"^6.5\"", " },", " \"authors\": [", " {", " \"name\": \"Daniel Leech\",", " \"email\": \"daniel@dantleech.com\"", " }", " ],", " \"license\": \"MIT\",", "}", ""]]}
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Text] = [Array([String("{"), String(" \"name\": \"phpactor/docblock\","), String(" \"description\": \"Simple Docblock Parser\","), String(" \"type\": \"library\","), String(" \"autoload\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\\": \"lib/\""), String(" }"), String(" },"), String(" \"autoload-dev\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\""), String(" }"), String(" },"), String(" \"require-dev\": {"), String(" \"phpunit/phpunit\": \"^6.5\""), String(" },"), String(" \"authors\": ["), String(" {"), String(" \"name\": \"Daniel Leech\","), String(" \"email\": \"daniel@dantleech.com\""), String(" }"), String(" ],"), String(" \"license\": \"MIT\","), String("}"), String("")])]
+21:05:18 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.version: 17 ==> 18
+21:05:18 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.text: "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n \"extra\": {\n \"branch-alias\": {\n \"dev-master\": \"1.0.x-dev\"\n }\n }\n}\n" ==> "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n}\n"
+21:05:18 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"text":"{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n}\n"}],"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json","version":18}}}
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 23, "LSP#visible_line_start()": 10, "LSP#visible_line_end()": 24, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:18 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(23)]
+21:05:18 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(10), Number(24)]
+21:05:18 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleTextChanged", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:18 INFO main src/languageclient.rs:2183 Begin languageClient/handleTextChanged
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:18 INFO main src/languageclient.rs:1760 Begin textDocument/didChange
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:18 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["text"]. Exps: ["LSP#text()"]
+21:05:18 INFO main src/vim.rs:92 => None {"jsonrpc":"2.0","method":"eval","params":["[LSP#text()]"],"id":50}
+21:05:18 INFO reader-main src/vim.rs:380 <= None {"id": 50, "jsonrpc": "2.0", "result": [["{", " \"name\": \"phpactor/docblock\",", " \"description\": \"Simple Docblock Parser\",", " \"type\": \"library\",", " \"autoload\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\\": \"lib/\"", " }", " },", " \"autoload-dev\": {", " \"psr-4\": {", " \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"", " }", " },", " \"require-dev\": {", " \"phpunit/phpunit\": \"^6.5\"", " },", " \"authors\": [", " {", " \"name\": \"Daniel Leech\",", " \"email\": \"daniel@dantleech.com\"", " }", " ],", " \"license\": \"MIT\"", "}", ""]]}
+21:05:18 INFO main src/languageclient.rs:48 gather_args: [Text] = [Array([String("{"), String(" \"name\": \"phpactor/docblock\","), String(" \"description\": \"Simple Docblock Parser\","), String(" \"type\": \"library\","), String(" \"autoload\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\\": \"lib/\""), String(" }"), String(" },"), String(" \"autoload-dev\": {"), String(" \"psr-4\": {"), String(" \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\""), String(" }"), String(" },"), String(" \"require-dev\": {"), String(" \"phpunit/phpunit\": \"^6.5\""), String(" },"), String(" \"authors\": ["), String(" {"), String(" \"name\": \"Daniel Leech\","), String(" \"email\": \"daniel@dantleech.com\""), String(" }"), String(" ],"), String(" \"license\": \"MIT\""), String("}"), String("")])]
+21:05:18 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.text: "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\",\n}\n" ==> "{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\"\n}\n"
+21:05:18 DEBUG main src/vim.rs:320 state.text_documents./home/daniel/www/phpactor/docblock/composer.json.version: 18 ==> 19
+21:05:18 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"text":"{\n \"name\": \"phpactor/docblock\",\n \"description\": \"Simple Docblock Parser\",\n \"type\": \"library\",\n \"autoload\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\\": \"lib/\"\n }\n },\n \"autoload-dev\": {\n \"psr-4\": {\n \"Phpactor\\\\Docblock\\\\Tests\\\\\": \"tests/\"\n }\n },\n \"require-dev\": {\n \"phpunit/phpunit\": \"^6.5\"\n },\n \"authors\": [\n {\n \"name\": \"Daniel Leech\",\n \"email\": \"daniel@dantleech.com\"\n }\n ],\n \"license\": \"MIT\"\n}\n"}],"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json","version":19}}}
+21:05:19 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleBufWritePost", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:05:19 INFO main src/languageclient.rs:2213 Begin languageClient/handleBufWritePost
+21:05:19 INFO main src/languageclient.rs:1830 Begin textDocument/didSave
+21:05:19 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:05:19 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didSave","params":{"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json"}}}
+21:05:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "markdown", "line": 11, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 22, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/README.md"}}
+21:05:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:05:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/README.md"), Number(11)]
+21:05:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(22)]
+21:05:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:07:32 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "json", "line": 23, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 24, "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:07:32 INFO main src/languageclient.rs:2235 Begin languageClient/handleCursorMoved
+21:07:32 INFO main src/languageclient.rs:48 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/daniel/www/phpactor/docblock/composer.json"), Number(23)]
+21:07:32 INFO main src/languageclient.rs:48 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(24)]
+21:07:32 INFO main src/languageclient.rs:2325 End languageClient/handleCursorMoved
+21:07:35 INFO reader-main src/vim.rs:380 <= None {"method": "languageClient/handleBufWritePost", "jsonrpc": "2.0", "params": {"languageId": "json", "buftype": "", "filename": "/home/daniel/www/phpactor/docblock/composer.json"}}
+21:07:35 INFO main src/languageclient.rs:2213 Begin languageClient/handleBufWritePost
+21:07:35 INFO main src/languageclient.rs:1830 Begin textDocument/didSave
+21:07:35 INFO main src/languageclient.rs:48 gather_args: [Buftype, LanguageId, Filename] = [String(""), String("json"), String("/home/daniel/www/phpactor/docblock/composer.json")]
+21:07:35 INFO main src/vim.rs:92 => Some("json") {"jsonrpc":"2.0","method":"textDocument/didSave","params":{"textDocument":{"uri":"file:///home/daniel/www/phpactor/docblock/composer.json"}}}
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
deleted file mode 100644
index 54238413..00000000
--- a/phpstan-baseline.neon
+++ /dev/null
@@ -1,387 +0,0 @@
-parameters:
- ignoreErrors:
- -
- message: "#^Property Phpactor\\\\Docblock\\\\DefaultValue\\:\\:\\$value has no typehint specified\\.$#"
- count: 1
- path: lib/DefaultValue.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DefaultValue\\:\\:__construct\\(\\) has parameter \\$value with no typehint specified\\.$#"
- count: 1
- path: lib/DefaultValue.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DefaultValue\\:\\:none\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/DefaultValue.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DefaultValue\\:\\:ofValue\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/DefaultValue.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DefaultValue\\:\\:ofValue\\(\\) has parameter \\$value with no typehint specified\\.$#"
- count: 1
- path: lib/DefaultValue.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DefaultValue\\:\\:value\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/DefaultValue.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\Docblock\\:\\:\\$tags type has no value type specified in iterable type Phpactor\\\\Docblock\\\\Tags\\.$#"
- count: 1
- path: lib/Docblock.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Docblock\\:\\:__construct\\(\\) has parameter \\$tags with no value type specified in iterable type Phpactor\\\\Docblock\\\\Tags\\.$#"
- count: 1
- path: lib/Docblock.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Docblock\\:\\:fromTags\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Docblock.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Docblock\\:\\:fromTags\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Docblock.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Docblock\\:\\:fromProseAndTags\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Docblock.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Docblock\\:\\:fromProseAndTags\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Docblock.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Docblock\\:\\:tags\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\Tags\\.$#"
- count: 1
- path: lib/Docblock.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockFactory\\:\\:createVarTag\\(\\) has parameter \\$metadata with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/DocblockFactory.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockFactory\\:\\:createParamTag\\(\\) has parameter \\$metadata with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/DocblockFactory.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockFactory\\:\\:createMethodTag\\(\\) has parameter \\$metadata with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/DocblockFactory.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockFactory\\:\\:createReturnTag\\(\\) has parameter \\$metadata with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/DocblockFactory.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockFactory\\:\\:createPropertyTag\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/DocblockFactory.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockFactory\\:\\:createPropertyTag\\(\\) has parameter \\$metadata with no typehint specified\\.$#"
- count: 1
- path: lib/DocblockFactory.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockFactory\\:\\:createDeprecatedTag\\(\\) has parameter \\$metadata with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/DocblockFactory.php
-
- -
- message: "#^Class Phpactor\\\\Docblock\\\\DocblockTypes implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:\\$docblocktypes has no typehint specified\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:__construct\\(\\) has parameter \\$docblocktypes with no typehint specified\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:empty\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:fromStringTypes\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:fromStringTypes\\(\\) has parameter \\$types with no typehint specified\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:fromDocblockTypes\\(\\) has parameter \\$docblocktypes with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:fromDocblockTypes\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:getIterator\\(\\) return type has no value type specified in iterable type Traversable\\\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\DocblockTypes\\:\\:add\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/DocblockTypes.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\InheritTag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/InheritTag.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\Method\\\\Parameter\\:\\:\\$types type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Method/Parameter.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Method\\\\Parameter\\:\\:__construct\\(\\) has parameter \\$types with no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Method/Parameter.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Method\\\\Parameter\\:\\:types\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Method/Parameter.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\:\\:parse\\(\\) has parameter \\$docblock with no typehint specified\\.$#"
- count: 1
- path: lib/Parser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Parser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\:\\:extractProse\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Parser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\\\MethodParser\\:\\:parseMethod\\(\\) has parameter \\$parts with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Parser/MethodParser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\\\MethodParser\\:\\:methodInfo\\(\\) has parameter \\$parts with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Parser/MethodParser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\\\MethodParser\\:\\:methodInfo\\(\\) return type has no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Parser/MethodParser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\\\MethodParser\\:\\:parseParameters\\(\\) return type has no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Parser/MethodParser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\\\ParameterParser\\:\\:extractParts\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Parser/ParameterParser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Parser\\\\TypesParser\\:\\:parseTypes\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Parser/TypesParser.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\DeprecatedTag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/DeprecatedTag.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:\\$types type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:__construct\\(\\) has parameter \\$methodName with no typehint specified\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:__construct\\(\\) has parameter \\$types with no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:types\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:methodName\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\MethodTag\\:\\:parameters\\(\\) return type has no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Tag/MethodTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\ParamTag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/ParamTag.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\Tag\\\\PropertyTag\\:\\:\\$types type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/PropertyTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\PropertyTag\\:\\:__construct\\(\\) has parameter \\$types with no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/PropertyTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\PropertyTag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/PropertyTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\PropertyTag\\:\\:propertyName\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/PropertyTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\PropertyTag\\:\\:types\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/PropertyTag.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\Tag\\\\ReturnTag\\:\\:\\$types type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/ReturnTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\ReturnTag\\:\\:__construct\\(\\) has parameter \\$types with no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/ReturnTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\ReturnTag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/ReturnTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\ReturnTag\\:\\:types\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/ReturnTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\VarTag\\:\\:name\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/VarTag.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\Tag\\\\VarTag\\:\\:\\$types type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/VarTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\VarTag\\:\\:__construct\\(\\) has parameter \\$types with no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/VarTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\VarTag\\:\\:__construct\\(\\) has parameter \\$varName with no typehint specified\\.$#"
- count: 1
- path: lib/Tag/VarTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\VarTag\\:\\:types\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\DocblockTypes\\.$#"
- count: 1
- path: lib/Tag/VarTag.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tag\\\\VarTag\\:\\:varName\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tag/VarTag.php
-
- -
- message: "#^Class Phpactor\\\\Docblock\\\\Tags implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#"
- count: 1
- path: lib/Tags.php
-
- -
- message: "#^Property Phpactor\\\\Docblock\\\\Tags\\:\\:\\$tags has no typehint specified\\.$#"
- count: 1
- path: lib/Tags.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tags\\:\\:__construct\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Tags.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tags\\:\\:fromArray\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#"
- count: 1
- path: lib/Tags.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tags\\:\\:fromArray\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\Tags\\.$#"
- count: 1
- path: lib/Tags.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tags\\:\\:getIterator\\(\\) return type has no value type specified in iterable type Traversable\\\\.$#"
- count: 1
- path: lib/Tags.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tags\\:\\:byName\\(\\) return type has no value type specified in iterable type Phpactor\\\\Docblock\\\\Tags\\.$#"
- count: 1
- path: lib/Tags.php
-
- -
- message: "#^Method Phpactor\\\\Docblock\\\\Tags\\:\\:add\\(\\) has no return typehint specified\\.$#"
- count: 1
- path: lib/Tags.php
-
diff --git a/phpstan.neon b/phpstan.neon
index 09c3a5c2..0e807a0d 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,5 +1,3 @@
-includes:
- - phpstan-baseline.neon
parameters:
inferPrivatePropertyTypeFromConstructor: true
paths:
diff --git a/test.php b/test.php
new file mode 100644
index 00000000..c583f974
--- /dev/null
+++ b/test.php
@@ -0,0 +1,4 @@
+hasDescendant(ScalarNode::class));
self::assertFalse($methodNode->hasDescendant(MethodNode::class));
self::assertCount(2, iterator_to_array($methodNode->descendantElements(ScalarNode::class)));
- self::assertInstanceOf(ScalarNode::class, $methodNode->firstDescendant(ScalarNode::class));
+ self::assertInstanceOf(ScalarNode::class, $methodNode->firstDescendant(ScalarNode::class));
}
];
}
From 0ffbbe02e9cf2d0427649e95f76ac916541c5708 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 22:07:36 +0000
Subject: [PATCH 57/63] Render text
---
lib/Ast/Docblock.php | 17 ++++++++++++++++
lib/Lexer.php | 9 +++++----
tests/Printer/examples/method1.test | 4 ++--
tests/Printer/examples/method2.test | 4 ++--
tests/Unit/Ast/NodeTest.php | 30 +++++++++++++++++++++++++++++
tests/Unit/LexerTest.php | 13 ++++++-------
6 files changed, 62 insertions(+), 15 deletions(-)
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
index 40e87431..43846ba4 100644
--- a/lib/Ast/Docblock.php
+++ b/lib/Ast/Docblock.php
@@ -20,4 +20,21 @@ public function __construct(array $children)
{
$this->children = new ElementList($children);
}
+
+ public function prose(): string
+ {
+ return trim(implode('', array_map(function (Element $token): string {
+ if ($token instanceof Token) {
+ if (in_array($token->type, [
+ Token::T_PHPDOC_OPEN,
+ Token::T_PHPDOC_CLOSE,
+ Token::T_PHPDOC_LEADING
+ ])) {
+ return '';
+ }
+ return $token->value;
+ }
+ return '';
+ }, iterator_to_array($this->children, false))));
+ }
}
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 3806eeb0..735992d8 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -13,11 +13,12 @@ final class Lexer
private const PATTERNS = [
'/\*+', // start tag
'\*/', // close tag
- '\*', // leading tag
- '\[\]', //tag
+ ' {1}\* {1}',
+ '\[\]', // list
'\?', //tag
'@\w+', //tag
- '\s+', // whitespace
+ '\R', // newline
+ ' *', // space
',', // comma
'\|', // bar (union)
'=', // equals
@@ -97,7 +98,7 @@ private function resolveType(string $value, ?array $prevChunk = null): string
return Token::T_PHPDOC_CLOSE;
}
- if ($prevChunk && 0 === strpos($prevChunk[0], "\n") && trim($value) === '*') {
+ if (trim($value) === '*') {
return Token::T_PHPDOC_LEADING;
}
diff --git a/tests/Printer/examples/method1.test b/tests/Printer/examples/method1.test
index 80770a95..f85a3223 100644
--- a/tests/Printer/examples/method1.test
+++ b/tests/Printer/examples/method1.test
@@ -1,10 +1,10 @@
/**
- * @method Foobar foobar()
+ * @method Foobar foobar()
*/
---
Docblock: =
ElementList: = /**
*
MethodTag: = @method
- ClassNode: = Foobarfoobar()
+ ClassNode: = Foobarfoobar()
*/
diff --git a/tests/Printer/examples/method2.test b/tests/Printer/examples/method2.test
index ba7d6680..4a7a21e6 100644
--- a/tests/Printer/examples/method2.test
+++ b/tests/Printer/examples/method2.test
@@ -1,10 +1,10 @@
/**
- * @method static Foobar foobar()
+ * @method static Foobar foobar()
*/
---
Docblock: =
ElementList: = /**
*
MethodTag: = @methodstatic
- ClassNode: = Foobarfoobar()
+ ClassNode: = Foobarfoobar()
*/
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index 5e045b63..709725da 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -3,6 +3,7 @@
namespace Phpactor\Docblock\Tests\Unit\Ast;
use Generator;
+use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Tag\MethodTag;
use Phpactor\Docblock\Ast\Tag\ReturnTag;
use Phpactor\Docblock\Ast\Type\ClassNode;
@@ -20,6 +21,7 @@ class NodeTest extends NodeTestCase
public function provideNode(): Generator
{
yield from $this->provideApiTest();
+ yield from $this->provideDocblock();
yield from $this->provideTags();
yield from $this->provideTypes();
}
@@ -102,4 +104,32 @@ function (ReturnTag $return): void {
}
];
}
+
+ private function provideDocblock()
+ {
+ yield 'docblock' => [
+ <<<'EOT'
+/**
+ * This is a docblock
+ * With some text -
+ * and maybe some
+ * ```
+ * Markdown
+ * ```
+ * @param This $should not be included
+ */
+EOT
+ , function (Docblock $docblock): void {
+ self::assertEquals(<<<'EOT'
+This is a docblock
+With some text -
+and maybe some
+```
+Markdown
+```
+EOT
+, $docblock->prose());
+ }
+ ];
+ }
}
diff --git a/tests/Unit/LexerTest.php b/tests/Unit/LexerTest.php
index 4835aa8e..86943555 100644
--- a/tests/Unit/LexerTest.php
+++ b/tests/Unit/LexerTest.php
@@ -42,19 +42,18 @@ public function provideLex(): Generator
,[
[Token::T_PHPDOC_OPEN, '/**'],
- [Token::T_WHITESPACE, "\n "],
- [Token::T_PHPDOC_LEADING, '*'],
- [Token::T_WHITESPACE, ' '],
+ [Token::T_WHITESPACE, "\n"],
+ [Token::T_PHPDOC_LEADING, ' * '],
[Token::T_LABEL, 'Hello'],
[Token::T_WHITESPACE, ' '],
[Token::T_LABEL, 'this'],
[Token::T_WHITESPACE, ' '],
[Token::T_LABEL, 'is'],
- [Token::T_WHITESPACE, "\n "],
- [Token::T_PHPDOC_LEADING, '*'],
- [Token::T_WHITESPACE, ' '],
+ [Token::T_WHITESPACE, "\n"],
+ [Token::T_PHPDOC_LEADING, ' * '],
[Token::T_LABEL, 'Multi'],
- [Token::T_WHITESPACE, "\n "],
+ [Token::T_WHITESPACE, "\n"],
+ [Token::T_WHITESPACE, " "],
[Token::T_PHPDOC_CLOSE, '*/'],
]
];
From 59af98ef0f4eba011743f4fccb5e56851b70c94b Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 22:26:07 +0000
Subject: [PATCH 58/63] Add API for tags to docblock
---
lib/Ast/Docblock.php | 35 +++++++++++++++++++++++++++++++++++
lib/Ast/Node.php | 10 +++++++---
tests/Unit/Ast/NodeTest.php | 9 +++++++++
3 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
index 43846ba4..08b1ac0d 100644
--- a/lib/Ast/Docblock.php
+++ b/lib/Ast/Docblock.php
@@ -2,6 +2,8 @@
namespace Phpactor\Docblock\Ast;
+use Generator;
+
class Docblock extends Node
{
protected const CHILD_NAMES = [
@@ -21,6 +23,39 @@ public function __construct(array $children)
$this->children = new ElementList($children);
}
+ /**
+ * @param class-string $tagFqn
+ */
+ public function hasTag(string $tagFqn): bool
+ {
+ foreach ($this->tags() as $tag) {
+ if ($tag instanceof $tagFqn) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @template T
+ * @param ?class-string $tagFqn
+ * @return Generator
+ */
+ public function tags(?string $tagFqn = null): Generator
+ {
+ foreach ($this->children as $child) {
+ if ($tagFqn && $child instanceof $tagFqn) {
+ yield $child;
+ continue;
+ }
+ if ($child instanceof TagNode) {
+ yield $child;
+ continue;
+ }
+ }
+ }
+
public function prose(): string
{
return trim(implode('', array_map(function (Element $token): string {
diff --git a/lib/Ast/Node.php b/lib/Ast/Node.php
index fe44c695..0d7ff43a 100644
--- a/lib/Ast/Node.php
+++ b/lib/Ast/Node.php
@@ -147,7 +147,7 @@ private function traverseNodes(iterable $nodes): Generator
{
$result = [];
foreach ($nodes as $child) {
- if (is_array($child)) {
+ if (is_iterable($child)) {
yield from $this->traverseNodes($child);
continue;
}
@@ -178,6 +178,10 @@ private function endOf(iterable $elements): int
return $this->endOf(array_reverse($element));
}
+ if (is_iterable($element)) {
+ return $this->endOf(array_reverse(iterator_to_array($element)));
+ }
+
return $element->end();
}
@@ -198,7 +202,7 @@ private function startOf(iterable $elements): int
if ($element instanceof Element) {
return $element->start();
}
- if (is_array($element)) {
+ if (is_iterable($element)) {
return $this->startOf($element);
}
}
@@ -222,7 +226,7 @@ private function findTokens(iterable $nodes): Generator
yield from $node->tokens();
}
- if (is_array($node)) {
+ if (is_iterable($node)) {
yield from $this->findTokens($node);
}
}
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index 709725da..f4afe0e0 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -4,6 +4,7 @@
use Generator;
use Phpactor\Docblock\Ast\Docblock;
+use Phpactor\Docblock\Ast\Tag\DeprecatedTag;
use Phpactor\Docblock\Ast\Tag\MethodTag;
use Phpactor\Docblock\Ast\Tag\ReturnTag;
use Phpactor\Docblock\Ast\Type\ClassNode;
@@ -65,7 +66,15 @@ function (MethodTag $methodNode): void {
self::assertFalse($methodNode->hasChild(MethodTag::class));
}
];
+
yield [ '@deprecated This is deprecated'];
+ yield 'deprecated' => [
+ '/** @deprecated This is deprecated */',
+ function (Docblock $block) {
+ self::assertTrue($block->hasTag(DeprecatedTag::class));
+ }
+ ];
+
yield [ '/** This is docblock @deprecated Foo */'];
yield [ '@mixin Foo\Bar'];
yield [ '@param string $foo This is a parameter'];
From eb60d2c93227fc5c4f93c3103d3eca7b73cbd9e8 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sun, 31 Jan 2021 08:19:30 +0000
Subject: [PATCH 59/63] FIx docblock get tag
---
lib/Ast/Docblock.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
index 08b1ac0d..98d37435 100644
--- a/lib/Ast/Docblock.php
+++ b/lib/Ast/Docblock.php
@@ -49,7 +49,7 @@ public function tags(?string $tagFqn = null): Generator
yield $child;
continue;
}
- if ($child instanceof TagNode) {
+ if (!$tagFqn && $child instanceof TagNode) {
yield $child;
continue;
}
From 91d2d921af482a708bdbe5b203c528847df80a65 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sun, 31 Jan 2021 10:01:32 +0000
Subject: [PATCH 60/63] Accessor for parameters
---
lib/Ast/Docblock.php | 2 +-
lib/Ast/ParameterList.php | 14 ++++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/lib/Ast/Docblock.php b/lib/Ast/Docblock.php
index 98d37435..40e10805 100644
--- a/lib/Ast/Docblock.php
+++ b/lib/Ast/Docblock.php
@@ -39,7 +39,7 @@ public function hasTag(string $tagFqn): bool
/**
* @template T
- * @param ?class-string $tagFqn
+ * @param class-string|null $tagFqn
* @return Generator
*/
public function tags(?string $tagFqn = null): Generator
diff --git a/lib/Ast/ParameterList.php b/lib/Ast/ParameterList.php
index f01f2f6e..5fae446c 100644
--- a/lib/Ast/ParameterList.php
+++ b/lib/Ast/ParameterList.php
@@ -4,7 +4,9 @@
use ArrayIterator;
use Countable;
+use Generator;
use IteratorAggregate;
+use Phpactor\Docblock\Ast\Tag\ParameterTag;
/**
* @implements IteratorAggregate
@@ -28,6 +30,18 @@ public function __construct(array $list)
$this->list = $list;
}
+ /**
+ * @return Generator
+ */
+ public function parameters(): Generator
+ {
+ foreach ($this->list as $element) {
+ if ($element instanceof ParameterTag) {
+ yield $element;
+ }
+ }
+ }
+
/**
* @return ArrayIterator
*/
From 60064204457ed1f3dad74b19006fb48f9ba149f8 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Wed, 27 Jan 2021 22:34:57 +0000
Subject: [PATCH 61/63] Removed core PHP docs from benchmark
---
tests/Benchmark/AbstractParserBenchCase.php | 21 ++-------------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/tests/Benchmark/AbstractParserBenchCase.php b/tests/Benchmark/AbstractParserBenchCase.php
index 9cdb1639..f8a10be8 100644
--- a/tests/Benchmark/AbstractParserBenchCase.php
+++ b/tests/Benchmark/AbstractParserBenchCase.php
@@ -26,26 +26,9 @@ public function benchParse(): void
}
/**
- * @ParamProviders({"provideCoreDocs"})
+ * @Revs(5)
+ * @Iterations(10)
*/
- public function benchPhpCore(array $params): void
- {
- $this->parse(trim($params['doc']));
- }
-
- /**
- * @return Generator
- */
- public function provideCoreDocs(): Generator
- {
- $contents = file_get_contents(__DIR__ . '/examples/php_core.example');
- foreach (explode('#!---!#', $contents) as $doc) {
- yield str_replace("\n", '', substr($doc, 0, 10)) => [
- 'doc' => $doc
- ];
- }
- }
-
public function benchAssert(): void
{
$this->parse(file_get_contents(__DIR__ . '/examples/assert.example'));
From beae2699ef9346696bb9721bacd187e334bd38a2 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sun, 31 Jan 2021 10:12:36 +0000
Subject: [PATCH 62/63] Prop assertion
---
tests/Unit/Ast/NodeTest.php | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index f4afe0e0..9d777542 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -6,6 +6,7 @@
use Phpactor\Docblock\Ast\Docblock;
use Phpactor\Docblock\Ast\Tag\DeprecatedTag;
use Phpactor\Docblock\Ast\Tag\MethodTag;
+use Phpactor\Docblock\Ast\Tag\PropertyTag;
use Phpactor\Docblock\Ast\Tag\ReturnTag;
use Phpactor\Docblock\Ast\Type\ClassNode;
use Phpactor\Docblock\Ast\Type\GenericNode;
@@ -66,6 +67,12 @@ function (MethodTag $methodNode): void {
self::assertFalse($methodNode->hasChild(MethodTag::class));
}
];
+ yield [
+ '@property Baz\Bar $foobar',
+ function (PropertyTag $property): void {
+ self::assertEquals('$foobar', $property->name->toString());t
+ }
+ ];
yield [ '@deprecated This is deprecated'];
yield 'deprecated' => [
@@ -81,7 +88,6 @@ function (Docblock $block) {
yield ['@param Baz\Bar $foobar This is a parameter'];
yield ['@var Baz\Bar $foobar'];
yield ['@return Baz\Bar'];
- yield ['@property Baz\Bar $foobar'];
}
/**
From 6d4b18e67d7b043e0ff28ac1a99d0a7f571b15f8 Mon Sep 17 00:00:00 2001
From: Daniel Leech
Date: Sun, 31 Jan 2021 10:17:47 +0000
Subject: [PATCH 63/63] Support for return $this
---
lib/Ast/Type/ThisNode.php | 23 +++++++++++++++++++++++
lib/Parser.php | 8 ++++++++
tests/Printer/examples/return2.test | 10 ++++++++++
tests/Unit/Ast/NodeTest.php | 3 ++-
4 files changed, 43 insertions(+), 1 deletion(-)
create mode 100644 lib/Ast/Type/ThisNode.php
create mode 100644 tests/Printer/examples/return2.test
diff --git a/lib/Ast/Type/ThisNode.php b/lib/Ast/Type/ThisNode.php
new file mode 100644
index 00000000..7b159688
--- /dev/null
+++ b/lib/Ast/Type/ThisNode.php
@@ -0,0 +1,23 @@
+name = $name;
+ }
+}
diff --git a/lib/Parser.php b/lib/Parser.php
index 61207016..c4263cef 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -22,6 +22,7 @@
use Phpactor\Docblock\Ast\Type\NullNode;
use Phpactor\Docblock\Ast\Type\NullableNode;
use Phpactor\Docblock\Ast\Type\ScalarNode;
+use Phpactor\Docblock\Ast\Type\ThisNode;
use Phpactor\Docblock\Ast\Type\UnionNode;
use Phpactor\Docblock\Ast\UnknownTag;
use Phpactor\Docblock\Ast\ValueNode;
@@ -348,6 +349,13 @@ private function parseReturn(): ReturnTag
$type = $this->parseTypes();
}
+ if ($this->tokens->if(Token::T_VARIABLE)) {
+ $variable = $this->tokens->chomp(Token::T_VARIABLE);
+ if ($variable->value === '$this') {
+ $type = new ThisNode($variable);
+ }
+ }
+
return new ReturnTag($tag, $type, $this->parseText());
}
diff --git a/tests/Printer/examples/return2.test b/tests/Printer/examples/return2.test
new file mode 100644
index 00000000..2e64713a
--- /dev/null
+++ b/tests/Printer/examples/return2.test
@@ -0,0 +1,10 @@
+/**
+ * @return $this
+ */
+---
+Docblock: =
+ ElementList: = /**
+ *
+ ReturnTag: = @return
+ ThisNode: = $this
+ */
diff --git a/tests/Unit/Ast/NodeTest.php b/tests/Unit/Ast/NodeTest.php
index 9d777542..6e5d88da 100644
--- a/tests/Unit/Ast/NodeTest.php
+++ b/tests/Unit/Ast/NodeTest.php
@@ -70,7 +70,7 @@ function (MethodTag $methodNode): void {
yield [
'@property Baz\Bar $foobar',
function (PropertyTag $property): void {
- self::assertEquals('$foobar', $property->name->toString());t
+ self::assertEquals('$foobar', $property->name->toString());
}
];
@@ -88,6 +88,7 @@ function (Docblock $block) {
yield ['@param Baz\Bar $foobar This is a parameter'];
yield ['@var Baz\Bar $foobar'];
yield ['@return Baz\Bar'];
+ yield ['@return $this'];
}
/**