diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35ab0d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Third party +/composer.lock +/phpunit.xml +/vendor +/.couscous +/.puli diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c955756 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +language: php + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +matrix: +# include: +# - php: 5.3.3 +# env: COMPOSER_FLAGS="--prefer-lowest" + allow_failures: + - php: hhvm + - php: 7.0 + +before_script: + - curl http://cs.sensiolabs.org/get/php-cs-fixer.phar -o php-cs-fixer.phar + - composer selfupdate + - composer update $COMPOSER_FLAGS + +script: + - echo '[phpspec] Running specification tests'; ./vendor/bin/phpspec run -n -f dot + - output=$(php -n php-cs-fixer.phar fix -v --dry-run --config=sf23 .); if [[ $(grep -o F <<< $output | wc -l) -gt 3 ]]; then while read -r line; do echo -e "\e[00;31m$line\e[00m"; done <<< "$output"; false; fi; diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..62804cb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG + +## 1.0.0-rc-1: Import + +* imported pretty printer from [memio/memio](http://github.com/memio/memio) v1.0.0-rc10 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b9174ef --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,63 @@ +# How to contribute + +Everybody should be able to help. Here's how you can make this project more +awesome: + +1. [Fork it](https://github.com/memio/pretty-printer/fork_select) +2. improve it +3. submit a [pull request](https://help.github.com/articles/creating-a-pull-request) + +Your work will then be reviewed as soon as possible (suggestions about some +changes, improvements or alternatives may be given). + +Here's some tips to make you the best contributor ever: + +* [Standard code](#standard-code) +* [Specifications](#specifications) +* [Keeping your fork up-to-date](#keeping-your-fork-up-to-date) + +## Standard code + +Use [PHP CS fixer](http://cs.sensiolabs.org/) to make your code compliant with +Memio PrettyPrinter's coding standards: + + ./vendor/bin/php-cs-fixer fix --config=sf23 . + +## Specifications + +Memio PrettyPrinter drives its development using [phpspec](http://www.phpspec.net/): + + # Generate the specification class: + phpspec describe 'Memio\Memio\MyNewClass' + + # Customize the specification class: + $EDITOR tests/spec/Memio/pretty-printer/MyNewClass.php + + # Generate the specified class: + phpspec run + + # Customize the class: + $EDITOR src/Memio/pretty-printer/MyNewClass.php + + phpspec run # Should be green! + +## Keeping your fork up-to-date + +To keep your fork up-to-date, you should track the upstream (original) one +using the following command: + + git remote add upstream https://github.com/memio/pretty-printer.git + +Then get the upstream changes: + + git checkout master + git pull --rebase origin master + git pull --rebase upstream master + git checkout + git rebase master + +Finally, publish your changes: + + git push -f origin + +Your pull request will be automatically updated. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba0bc4d --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Loïc Chardonnet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc47868 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Memio's PrettyPrinter [![SensioLabsInsight](https://insight.sensiolabs.com/projects/a2b24423-9840-45ab-a011-598aa3ba26bf/mini.png)](https://insight.sensiolabs.com/projects/a2b24423-9840-45ab-a011-598aa3ba26bf) [![Travis CI](https://travis-ci.org/memio/pretty-printer.png)](https://travis-ci.org/memio/pretty-printer) + +`PrettyPrinter` is a code generator (printer) that takes a Model and calls the +appropriate `TemplateEngine` to actually generate the corresponding code, +using highly opinionated coding standards (pretty). + +`PrettyPrinter` returns a string that can be saved in a file, dislpayed on a +console output or displayed in a web page. Possibilities are endless! + +> **Note**: This package is part of [Memio](http://memio.github.io/memio). +> Have a look at [the main repository](http://github.com/memio/memio). + +## Installation + +Install it using [Composer](https://getcomposer.org/download): + + composer require memio/pretty-printer:~1.0@rc + +## Example + +We're going to generate a class with a constructor and two attributes: + +```php +setStructure( + Object::make('Vendor\Project\MyService') + ->addProperty(new Property('createdAt')) + ->addProperty(new Property('filename')) + ->addMethod( + Method::make('__construct') + ->addArgument(new Argument('DateTime', 'createdAt')) + ->addArgument(new Argument('string', 'filename')) + ) + ) +; + +// Generate the code and display in the console +echo $prettyPrinter->generateCode($myService); + +// Or display it in a browser +// echo '
'.htmlspecialchars($prettyPrinter->generateCode($myService)).'
'; +``` + +With this simple example, we get the following output: + +``` + Given a version number MAJOR.MINOR.PATCH, increment the: +> +> 1. MAJOR version when you make incompatible API changes +> 2. MINOR version when you add functionality in a backwards-compatible manner +> 3. PATCH version when you make backwards-compatible bug fixes + +### Public API + +Classes and methods marked with the `@api` tag are considered to be the public +API of this project. + +## Branching Model + +The branching is inspired by [@jbenet](https://github.com/jbenet) +[simple git branching model](https://gist.github.com/jbenet/ee6c9ac48068889b0912): + +> 1. `master` must always be deployable. +> 2. **all changes** are made through feature branches (pull-request + merge) +> 3. rebase to avoid/resolve conflicts; merge in to `master` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..91cb8c4 --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "memio/pretty-printer", + "license": "MIT", + "type": "library", + "description": "Memio's PrettyPrinter, used to generate PHP code from given Model", + "keywords": ["generator", "PHP", "code"], + "homepage": "http://memio.github.io/memio", + "authors": [ + { + "name": "Loïc Chardonnet", + "email": "loic.chardonnet@gmail.com", + "homepage": "http://gnugat.github.io", + "role": "Developer" + } + ], + "autoload": { "psr-4": { + "Memio\\PrettyPrinter\\": "src/Memio/PrettyPrinter" + }}, + "require": { + "memio/model": "~1.0", + "php": ">=5.3.3", + "twig/twig": "~1.18" + }, + "require-dev": { + "ciaranmcnulty/phpspec-typehintedmethods": "~1.1", + "fabpot/php-cs-fixer": "~1.6", + "phpspec/phpspec": "~2.2" + } +} diff --git a/phpspec.yml b/phpspec.yml new file mode 100644 index 0000000..c1c2a05 --- /dev/null +++ b/phpspec.yml @@ -0,0 +1,2 @@ +extensions: + - Cjm\PhpSpec\Extension\TypeHintedMethodsExtension diff --git a/spec/Memio/PrettyPrinter/PrettyPrinter/EmptyCollectionPrettyPrinterSpec.php b/spec/Memio/PrettyPrinter/PrettyPrinter/EmptyCollectionPrettyPrinterSpec.php new file mode 100644 index 0000000..a23758b --- /dev/null +++ b/spec/Memio/PrettyPrinter/PrettyPrinter/EmptyCollectionPrettyPrinterSpec.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\Argument; +use PhpSpec\ObjectBehavior; + +class EmptyCollectionPrettyPrinterSpec extends ObjectBehavior +{ + function it_is_a_pretty_printer_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\PrettyPrinter\PrettyPrinterStrategy'); + } + + function it_supports_empty_arrays() + { + $this->supports(array(), array())->shouldBe(true); + } + + function it_generates_an_empty_string() + { + $this->generateCode(array())->shouldBe(''); + } +} diff --git a/spec/Memio/PrettyPrinter/PrettyPrinter/ModelCollectionPrettyPrinterSpec.php b/spec/Memio/PrettyPrinter/PrettyPrinter/ModelCollectionPrettyPrinterSpec.php new file mode 100644 index 0000000..7712b5c --- /dev/null +++ b/spec/Memio/PrettyPrinter/PrettyPrinter/ModelCollectionPrettyPrinterSpec.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\Argument; +use PhpSpec\ObjectBehavior; +use Twig_Environment; + +class ModelCollectionPrettyPrinterSpec extends ObjectBehavior +{ + function let(Twig_Environment $twig) + { + $this->beConstructedWith($twig); + } + + function it_is_a_pretty_printer_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\PrettyPrinter\PrettyPrinterStrategy'); + } + + function it_supports_array_of_models() + { + $argument = new Argument('string', 'filename'); + $arguments = array($argument); + + $this->supports($arguments, array())->shouldBe(true); + } + + function it_generates_code_using_collection_templates(Twig_Environment $twig) + { + $argument = new Argument('string', 'filename'); + $arguments = array($argument); + + $twig->render('collection/argument_collection.twig', array('argument_collection' => $arguments))->shouldBeCalled(); + + $this->generateCode($arguments); + } +} diff --git a/spec/Memio/PrettyPrinter/PrettyPrinter/ModelPrettyPrinterSpec.php b/spec/Memio/PrettyPrinter/PrettyPrinter/ModelPrettyPrinterSpec.php new file mode 100644 index 0000000..6c5b805 --- /dev/null +++ b/spec/Memio/PrettyPrinter/PrettyPrinter/ModelPrettyPrinterSpec.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\Argument; +use PhpSpec\ObjectBehavior; +use Twig_Environment; + +class ModelPrettyPrinterSpec extends ObjectBehavior +{ + function let(Twig_Environment $twig) + { + $this->beConstructedWith($twig); + } + + function it_is_a_pretty_printer_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\PrettyPrinter\PrettyPrinterStrategy'); + } + + function it_supports_models() + { + $argument = new Argument('bool', 'isObject'); + + $this->supports($argument, array())->shouldBe(true); + } + + function it_generates_code_using_root_templates(Twig_Environment $twig) + { + $argument = new Argument('string', 'filename'); + + $twig->render('argument.twig', array('argument' => $argument))->shouldBeCalled(); + + $this->generateCode($argument); + } +} diff --git a/spec/Memio/PrettyPrinter/PrettyPrinter/PhpdocCollectionPrettyPrinterSpec.php b/spec/Memio/PrettyPrinter/PrettyPrinter/PhpdocCollectionPrettyPrinterSpec.php new file mode 100644 index 0000000..3f9bfab --- /dev/null +++ b/spec/Memio/PrettyPrinter/PrettyPrinter/PhpdocCollectionPrettyPrinterSpec.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\Phpdoc\ParameterTag; +use PhpSpec\ObjectBehavior; +use Twig_Environment; + +class PhpdocCollectionPrettyPrinterSpec extends ObjectBehavior +{ + function let(Twig_Environment $twig) + { + $this->beConstructedWith($twig); + } + + function it_is_a_pretty_printer_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\PrettyPrinter\PrettyPrinterStrategy'); + } + + function it_supports_array_of_phpdocs() + { + $parameterTag = new ParameterTag('string', 'filename'); + $parameterTags = array($parameterTag); + + $this->supports($parameterTags, array())->shouldBe(true); + } + + function it_generates_code_using_collection_templates(Twig_Environment $twig) + { + $parameterTag = new ParameterTag('string', 'filename'); + $parameterTags = array($parameterTag); + + $twig->render( + 'collection/phpdoc/parameter_tag_collection.twig', + array('parameter_tag_collection' => $parameterTags) + )->shouldBeCalled(); + + $this->generateCode($parameterTags); + } +} diff --git a/spec/Memio/PrettyPrinter/PrettyPrinter/PhpdocPrettyPrinterSpec.php b/spec/Memio/PrettyPrinter/PrettyPrinter/PhpdocPrettyPrinterSpec.php new file mode 100644 index 0000000..c85411d --- /dev/null +++ b/spec/Memio/PrettyPrinter/PrettyPrinter/PhpdocPrettyPrinterSpec.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\Phpdoc\LicensePhpdoc; +use PhpSpec\ObjectBehavior; +use Twig_Environment; + +class PhpdocPrettyPrinterSpec extends ObjectBehavior +{ + function let(Twig_Environment $twig) + { + $this->beConstructedWith($twig); + } + + function it_is_a_pretty_printer_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\PrettyPrinter\PrettyPrinterStrategy'); + } + + function it_supports_phpdocs() + { + $licensePhpdoc = new LicensePhpdoc('Memio', 'author','author@example.com'); + + $this->supports($licensePhpdoc, array())->shouldBe(true); + } + + function it_generates_code_using_phpdoc_templates(Twig_Environment $twig) + { + $licensePhpdoc = new LicensePhpdoc('Memio', 'author','author@example.com'); + + $twig->render('phpdoc/license_phpdoc.twig', array('license_phpdoc' => $licensePhpdoc))->shouldBeCalled(); + + $this->generateCode($licensePhpdoc); + } +} diff --git a/spec/Memio/PrettyPrinter/PrettyPrinterSpec.php b/spec/Memio/PrettyPrinter/PrettyPrinterSpec.php new file mode 100644 index 0000000..42354c0 --- /dev/null +++ b/spec/Memio/PrettyPrinter/PrettyPrinterSpec.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter; + +use Memio\Model\Argument; +use Memio\Model\FullyQualifiedName; +use Memio\Model\Method; +use PhpSpec\ObjectBehavior; +use Prophecy\Argument as ProphecyArgument; +use Twig_Environment; + +class PrettyPrinterSpec extends ObjectBehavior +{ + function let(Twig_Environment $twig) + { + $twig->addExtension(ProphecyArgument::any())->shouldBeCalled(); + + $this->beConstructedWith($twig); + } + + function it_handles_one_worded_model_class_names(Twig_Environment $twig) + { + $argument = new Argument('string', 'filename'); + $twig->render('argument.twig', array('argument' => $argument))->shouldBeCalled(); + + $this->generateCode($argument); + } + + function it_handles_many_worded_model_class_names(Twig_Environment $twig) + { + $fullyQualifiedName = new FullyQualifiedName('Memio\PrettyPrinter\MyClass'); + $twig->render('fully_qualified_name.twig', array('fully_qualified_name' => $fullyQualifiedName))->shouldBeCalled(); + + $this->generateCode($fullyQualifiedName); + } + + function it_passes_extra_parameters_to_template(Twig_Environment $twig) + { + $argument = new Argument('int', 'total'); + $twig->render('argument.twig', array('extra' => 'parameter', 'argument' => $argument))->shouldBeCalled(); + + $this->generateCode($argument, array('extra' => 'parameter')); + } + + function it_handles_collections(Twig_Environment $twig) + { + $collection = array(new Argument('bool', 'isObject')); + $twig->render('collection/argument_collection.twig', array('argument_collection' => $collection))->shouldBeCalled(); + + $this->generateCode($collection); + } + + function it_handles_empty_collections() + { + $this->generateCode(array())->shouldBe(''); + } + + function it_throws_exception_when_no_strategy_support_the_given_arguments() + { + $invalidArgumentException = 'Memio\PrettyPrinter\Exception\InvalidArgumentException'; + + $this->shouldThrow($invalidArgumentException)->duringGenerateCode('nope'); + } +} diff --git a/spec/Memio/PrettyPrinter/TwigExtension/Line/ContractLineStrategySpec.php b/spec/Memio/PrettyPrinter/TwigExtension/Line/ContractLineStrategySpec.php new file mode 100644 index 0000000..9d70c31 --- /dev/null +++ b/spec/Memio/PrettyPrinter/TwigExtension/Line/ContractLineStrategySpec.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\Contract; +use PhpSpec\ObjectBehavior; + +class ContractLineStrategySpec extends ObjectBehavior +{ + const CONSTANT_BLOCK = 'constants'; + + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\TwigExtension\Line\LineStrategy'); + } + + function it_supports_contracts(Contract $contract) + { + $this->supports($contract)->shouldBe(true); + } + + function it_needs_line_after_constants_if_contract_has_both_constants_and_methods(Contract $contract) + { + $contract->allConstants()->willReturn(array(1)); + $contract->allMethods()->willReturn(array(2)); + + $this->needsLineAfter($contract, self::CONSTANT_BLOCK)->shouldBe(true); + } +} diff --git a/spec/Memio/PrettyPrinter/TwigExtension/Line/FileLineStrategySpec.php b/spec/Memio/PrettyPrinter/TwigExtension/Line/FileLineStrategySpec.php new file mode 100644 index 0000000..e440aab --- /dev/null +++ b/spec/Memio/PrettyPrinter/TwigExtension/Line/FileLineStrategySpec.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\File; +use PhpSpec\ObjectBehavior; + +class FileLineStrategySpec extends ObjectBehavior +{ + const IMPORT_BLOCK = 'fully_qualified_names'; + + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\TwigExtension\Line\LineStrategy'); + } + + function it_supports_files(File $file) + { + $this->supports($file)->shouldBe(true); + } + + function it_needs_line_after_fully_qualified_names_if_file_has_fully_qualified_names(File $file) + { + $file->allFullyQualifiedNames()->willReturn(array(1)); + + $this->needsLineAfter($file, self::IMPORT_BLOCK)->shouldBe(true); + } +} diff --git a/spec/Memio/PrettyPrinter/TwigExtension/Line/LineSpec.php b/spec/Memio/PrettyPrinter/TwigExtension/Line/LineSpec.php new file mode 100644 index 0000000..2a9c833 --- /dev/null +++ b/spec/Memio/PrettyPrinter/TwigExtension/Line/LineSpec.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\Argument; +use Memio\PrettyPrinter\TwigExtension\Line\LineStrategy; +use PhpSpec\ObjectBehavior; + +class LineSpec extends ObjectBehavior +{ + const BLOCK = 'constants'; + const STRATEGY_RETURN = true; + + function let(LineStrategy $lineStrategy) + { + $this->add($lineStrategy); + } + + function it_executes_the_first_strategy_that_supports_given_model(Argument $model, LineStrategy $lineStrategy) + { + $lineStrategy->supports($model)->willReturn(true); + $lineStrategy->needsLineAfter($model, self::BLOCK)->willReturn(self::STRATEGY_RETURN); + + $this->needsLineAfter($model, self::BLOCK)->shouldBe(self::STRATEGY_RETURN); + } + + function it_fails_when_no_strategy_supports_given_model(Argument $model, LineStrategy $lineStrategy) + { + $exception = 'Memio\PrettyPrinter\Exception\InvalidArgumentException'; + + $lineStrategy->supports($model)->willReturn(false); + + $this->shouldThrow($exception)->duringNeedsLineAfter($model, self::BLOCK); + } +} diff --git a/spec/Memio/PrettyPrinter/TwigExtension/Line/MethodPhpdocLineStrategySpec.php b/spec/Memio/PrettyPrinter/TwigExtension/Line/MethodPhpdocLineStrategySpec.php new file mode 100644 index 0000000..5dd06a5 --- /dev/null +++ b/spec/Memio/PrettyPrinter/TwigExtension/Line/MethodPhpdocLineStrategySpec.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\Phpdoc\MethodPhpdoc; +use Memio\Model\Phpdoc\ApiTag; +use Memio\Model\Phpdoc\Description; +use Memio\Model\Phpdoc\DeprecationTag; +use Memio\Model\Phpdoc\ParameterTag; +use PhpSpec\ObjectBehavior; + +class MethodPhpdocLineStrategySpec extends ObjectBehavior +{ + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\TwigExtension\Line\LineStrategy'); + } + + function it_supports_method_phpdocs(MethodPhpdoc $methodPhpdoc) + { + $this->supports($methodPhpdoc)->shouldBe(true); + } + + function it_needs_line_after_description_if_it_has_any_other_tag( + Description $description, + DeprecationTag $deprecationTag, + MethodPhpdoc $methodPhpdoc + ) + { + $methodPhpdoc->getApiTag()->willReturn(null); + $methodPhpdoc->getDescription()->willReturn($description); + $methodPhpdoc->getDeprecationTag()->willReturn($deprecationTag); + $methodPhpdoc->getParameterTags()->willReturn(null); + + $this->needsLineAfter($methodPhpdoc, 'description')->shouldBe(true); + } + + function it_needs_line_after_parameter_tags_if_it_has_api_or_deprecation_tags( + DeprecationTag $deprecationTag, + MethodPhpdoc $methodPhpdoc, + ParameterTag $parameterTag + ) + { + $methodPhpdoc->getApiTag()->willReturn(null); + $methodPhpdoc->getDescription()->willReturn(null); + $methodPhpdoc->getDeprecationTag()->willReturn($deprecationTag); + $methodPhpdoc->getParameterTags()->willReturn(array($parameterTag)); + + $this->needsLineAfter($methodPhpdoc, 'parameter_tags')->shouldBe(true); + } + + function it_needs_line_after_deprecation_it_also_has_an_api_tag( + ApiTag $apiTag, + DeprecationTag $deprecationTag, + MethodPhpdoc $methodPhpdoc + ) + { + $methodPhpdoc->getDeprecationTag()->willReturn($deprecationTag); + $methodPhpdoc->getDescription()->willReturn(null); + $methodPhpdoc->getApiTag()->willReturn($apiTag); + $methodPhpdoc->getParameterTags()->willReturn(null); + + $this->needsLineAfter($methodPhpdoc, 'deprecation_tag')->shouldBe(true); + } +} diff --git a/spec/Memio/PrettyPrinter/TwigExtension/Line/ObjectLineStrategySpec.php b/spec/Memio/PrettyPrinter/TwigExtension/Line/ObjectLineStrategySpec.php new file mode 100644 index 0000000..7bb4b34 --- /dev/null +++ b/spec/Memio/PrettyPrinter/TwigExtension/Line/ObjectLineStrategySpec.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\Object; +use PhpSpec\ObjectBehavior; + +class ObjectLineStrategySpec extends ObjectBehavior +{ + const CONSTANT_BLOCK = 'constants'; + const PROPERTY_BLOCK = 'properties'; + + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\TwigExtension\Line\LineStrategy'); + } + + function it_supports_objects(Object $object) + { + $this->supports($object)->shouldBe(true); + } + + function it_needs_line_after_constants_if_object_has_both_constants_and_properties(Object $object) + { + $object->allConstants()->willReturn(array(1)); + $object->allProperties()->willReturn(array(2)); + $object->allMethods()->willReturn(array()); + + $this->needsLineAfter($object, self::CONSTANT_BLOCK)->shouldBe(true); + } + + function it_needs_line_after_constants_if_object_has_both_constants_and_methods(Object $object) + { + $object->allConstants()->willReturn(array(1)); + $object->allProperties()->willReturn(array()); + $object->allMethods()->willReturn(array(2)); + + $this->needsLineAfter($object, self::CONSTANT_BLOCK)->shouldBe(true); + } + + function it_needs_line_after_properties_if_object_has_both_properties_and_methods(Object $object) + { + $object->allConstants()->willReturn(array()); + $object->allProperties()->willReturn(array(1)); + $object->allMethods()->willReturn(array(2)); + + $this->needsLineAfter($object, self::PROPERTY_BLOCK)->shouldBe(true); + } +} diff --git a/spec/Memio/PrettyPrinter/TwigExtension/Line/StructurePhpdocLineStrategySpec.php b/spec/Memio/PrettyPrinter/TwigExtension/Line/StructurePhpdocLineStrategySpec.php new file mode 100644 index 0000000..cbd2560 --- /dev/null +++ b/spec/Memio/PrettyPrinter/TwigExtension/Line/StructurePhpdocLineStrategySpec.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\Phpdoc\StructurePhpdoc; +use Memio\Model\Phpdoc\ApiTag; +use Memio\Model\Phpdoc\Description; +use Memio\Model\Phpdoc\DeprecationTag; +use PhpSpec\ObjectBehavior; + +class StructurePhpdocLineStrategySpec extends ObjectBehavior +{ + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\PrettyPrinter\TwigExtension\Line\LineStrategy'); + } + + function it_supports_structure_phpdocs(StructurePhpdoc $structurePhpdoc) + { + $this->supports($structurePhpdoc)->shouldBe(true); + } + + function it_needs_line_after_description_if_description_and_deprecation_or_api_are_defined( + ApiTag $apiTag, + Description $description, + DeprecationTag $deprecationTag, + StructurePhpdoc $structurePhpdoc + ) + { + $structurePhpdoc->getApiTag()->willReturn($apiTag); + $structurePhpdoc->getDescription()->willReturn($description); + $structurePhpdoc->getDeprecationTag()->willReturn($deprecationTag); + + $this->needsLineAfter($structurePhpdoc, 'description')->shouldBe(true); + } + + function it_needs_line_after_deprecation_if_deprecation_and_api_are_defined( + ApiTag $apiTag, + Description $description, + DeprecationTag $deprecationTag, + StructurePhpdoc $structurePhpdoc + ) + { + $structurePhpdoc->getApiTag()->willReturn($apiTag); + $structurePhpdoc->getDescription()->willReturn($description); + $structurePhpdoc->getDeprecationTag()->willReturn($deprecationTag); + + $this->needsLineAfter($structurePhpdoc, 'deprecation_tag')->shouldBe(true); + } +} diff --git a/src/Memio/PrettyPrinter/Exception/Exception.php b/src/Memio/PrettyPrinter/Exception/Exception.php new file mode 100644 index 0000000..975942b --- /dev/null +++ b/src/Memio/PrettyPrinter/Exception/Exception.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\Exception; + +/** + * @api + */ +interface Exception +{ +} diff --git a/src/Memio/PrettyPrinter/Exception/InvalidArgumentException.php b/src/Memio/PrettyPrinter/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..4c50403 --- /dev/null +++ b/src/Memio/PrettyPrinter/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\Exception; + +/** + * @api + */ +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} diff --git a/src/Memio/PrettyPrinter/PrettyPrinter.php b/src/Memio/PrettyPrinter/PrettyPrinter.php new file mode 100644 index 0000000..35f9d49 --- /dev/null +++ b/src/Memio/PrettyPrinter/PrettyPrinter.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; +use Memio\PrettyPrinter\PrettyPrinter\EmptyCollectionPrettyPrinter; +use Memio\PrettyPrinter\PrettyPrinter\ModelCollectionPrettyPrinter; +use Memio\PrettyPrinter\PrettyPrinter\ModelPrettyPrinter; +use Memio\PrettyPrinter\PrettyPrinter\PhpdocCollectionPrettyPrinter; +use Memio\PrettyPrinter\PrettyPrinter\PhpdocPrettyPrinter; +use Memio\PrettyPrinter\TwigExtension\Line\ContractLineStrategy; +use Memio\PrettyPrinter\TwigExtension\Line\FileLineStrategy; +use Memio\PrettyPrinter\TwigExtension\Line\Line; +use Memio\PrettyPrinter\TwigExtension\Line\MethodPhpdocLineStrategy; +use Memio\PrettyPrinter\TwigExtension\Line\ObjectLineStrategy; +use Memio\PrettyPrinter\TwigExtension\Line\StructurePhpdocLineStrategy; +use Memio\PrettyPrinter\TwigExtension\Type; +use Memio\PrettyPrinter\TwigExtension\Whitespace; +use Twig_Environment; + +/** + * @api + */ +class PrettyPrinter +{ + /** + * @var array + */ + private $strategies = array(); + + /** + * @param Twig_Environment $twig + * + * @api + */ + public function __construct(Twig_Environment $twig) + { + $line = new Line(); + $line->add(new ContractLineStrategy()); + $line->add(new FileLineStrategy()); + $line->add(new MethodPhpdocLineStrategy()); + $line->add(new ObjectLineStrategy()); + $line->add(new StructurePhpdocLineStrategy()); + + $twig->addExtension(new Type()); + $twig->addExtension(new Whitespace($line)); + + $this->strategies[] = new EmptyCollectionPrettyPrinter(); + $this->strategies[] = new PhpdocCollectionPrettyPrinter($twig); + $this->strategies[] = new ModelCollectionPrettyPrinter($twig); + $this->strategies[] = new PhpdocPrettyPrinter($twig); + $this->strategies[] = new ModelPrettyPrinter($twig); + } + + /** + * @param mixed $model + * @param array $parameters + * + * @return string + * + * @throws InvalidArgumentException If the given model and parameters aren't supported + * + * @api + */ + public function generateCode($model, array $parameters = array()) + { + foreach ($this->strategies as $strategy) { + if ($strategy->supports($model, $parameters)) { + return $strategy->generateCode($model, $parameters); + } + } + + throw new InvalidArgumentException('No PrettyPrinter support the given model and parameters'); + } +} diff --git a/src/Memio/PrettyPrinter/PrettyPrinter/EmptyCollectionPrettyPrinter.php b/src/Memio/PrettyPrinter/PrettyPrinter/EmptyCollectionPrettyPrinter.php new file mode 100644 index 0000000..88a6d64 --- /dev/null +++ b/src/Memio/PrettyPrinter/PrettyPrinter/EmptyCollectionPrettyPrinter.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\PrettyPrinter; + +class EmptyCollectionPrettyPrinter implements PrettyPrinterStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return (is_array($model) && empty($model)); + } + + /** + * {@inheritDoc} + */ + public function generateCode($model, array $parameters = array()) + { + return ''; + } +} diff --git a/src/Memio/PrettyPrinter/PrettyPrinter/ModelCollectionPrettyPrinter.php b/src/Memio/PrettyPrinter/PrettyPrinter/ModelCollectionPrettyPrinter.php new file mode 100644 index 0000000..5411f90 --- /dev/null +++ b/src/Memio/PrettyPrinter/PrettyPrinter/ModelCollectionPrettyPrinter.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\FullyQualifiedName; +use Twig_Environment; + +class ModelCollectionPrettyPrinter implements PrettyPrinterStrategy +{ + /** + * @var Twig_Environment + */ + private $twig; + + /** + * @param Twig_Environment $twig_Environment + */ + public function __construct(Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * {@inheritDoc} + */ + public function supports($model) + { + if (!is_array($model)) { + return false; + } + $firstElement = current($model); + if (!is_object($firstElement)) { + return false; + } + $fqcn = get_class($firstElement); + + return 1 === preg_match('/^Memio\\\\Model\\\\/', $fqcn); + } + + /** + * {@inheritDoc} + */ + public function generateCode($model, array $parameters = array()) + { + $firstElement = current($model); + $fqcn = get_class($firstElement); + $name = FullyQualifiedName::make($fqcn)->getName(); + $modelName = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name)).'_collection'; + $parameters[$modelName] = $model; + + return $this->twig->render('collection/'.$modelName.'.twig', $parameters); + } +} diff --git a/src/Memio/PrettyPrinter/PrettyPrinter/ModelPrettyPrinter.php b/src/Memio/PrettyPrinter/PrettyPrinter/ModelPrettyPrinter.php new file mode 100644 index 0000000..6897b0b --- /dev/null +++ b/src/Memio/PrettyPrinter/PrettyPrinter/ModelPrettyPrinter.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\FullyQualifiedName; +use Twig_Environment; + +class ModelPrettyPrinter implements PrettyPrinterStrategy +{ + /** + * @var Twig_Environment + */ + private $twig; + + /** + * @param Twig_Environment $twig_Environment + */ + public function __construct(Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * {@inheritDoc} + */ + public function supports($model) + { + if (!is_object($model)) { + return false; + } + $fqcn = get_class($model); + + return 1 === preg_match('/^Memio\\\\Model\\\\/', $fqcn); + } + + /** + * {@inheritDoc} + */ + public function generateCode($model, array $parameters = array()) + { + $fqcn = get_class($model); + $name = FullyQualifiedName::make($fqcn)->getName(); + $modelName = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name)); + $parameters[$modelName] = $model; + + return $this->twig->render($modelName.'.twig', $parameters); + } +} diff --git a/src/Memio/PrettyPrinter/PrettyPrinter/PhpdocCollectionPrettyPrinter.php b/src/Memio/PrettyPrinter/PrettyPrinter/PhpdocCollectionPrettyPrinter.php new file mode 100644 index 0000000..cf33229 --- /dev/null +++ b/src/Memio/PrettyPrinter/PrettyPrinter/PhpdocCollectionPrettyPrinter.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\FullyQualifiedName; +use Twig_Environment; + +class PhpdocCollectionPrettyPrinter implements PrettyPrinterStrategy +{ + /** + * @var Twig_Environment + */ + private $twig; + + /** + * @param Twig_Environment $twig_Environment + */ + public function __construct(Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * {@inheritDoc} + */ + public function supports($model) + { + if (!is_array($model)) { + return false; + } + $firstElement = current($model); + if (!is_object($firstElement)) { + return false; + } + $fqcn = get_class($firstElement); + + return 1 === preg_match('/^Memio\\\\Model\\\\Phpdoc\\\\/', $fqcn); + } + + /** + * {@inheritDoc} + */ + public function generateCode($model, array $parameters = array()) + { + $firstElement = current($model); + $fqcn = get_class($firstElement); + $name = FullyQualifiedName::make($fqcn)->getName(); + $modelName = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name)).'_collection'; + $parameters[$modelName] = $model; + + return $this->twig->render('collection/phpdoc/'.$modelName.'.twig', $parameters); + } +} diff --git a/src/Memio/PrettyPrinter/PrettyPrinter/PhpdocPrettyPrinter.php b/src/Memio/PrettyPrinter/PrettyPrinter/PhpdocPrettyPrinter.php new file mode 100644 index 0000000..6ebd2c8 --- /dev/null +++ b/src/Memio/PrettyPrinter/PrettyPrinter/PhpdocPrettyPrinter.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\PrettyPrinter; + +use Memio\Model\FullyQualifiedName; +use Twig_Environment; + +class PhpdocPrettyPrinter implements PrettyPrinterStrategy +{ + /** + * @var Twig_Environment + */ + private $twig; + + /** + * @param Twig_Environment $twig_Environment + */ + public function __construct(Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * {@inheritDoc} + */ + public function supports($model) + { + if (!is_object($model)) { + return false; + } + $fqcn = get_class($model); + + return 1 === preg_match('/^Memio\\\\Model\\\\Phpdoc\\\\/', $fqcn); + } + + /** + * {@inheritDoc} + */ + public function generateCode($model, array $parameters = array()) + { + $fqcn = get_class($model); + $name = FullyQualifiedName::make($fqcn)->getName(); + $modelName = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name)); + $parameters[$modelName] = $model; + + return $this->twig->render('phpdoc/'.$modelName.'.twig', $parameters); + } +} diff --git a/src/Memio/PrettyPrinter/PrettyPrinter/PrettyPrinterStrategy.php b/src/Memio/PrettyPrinter/PrettyPrinter/PrettyPrinterStrategy.php new file mode 100644 index 0000000..853b689 --- /dev/null +++ b/src/Memio/PrettyPrinter/PrettyPrinter/PrettyPrinterStrategy.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\PrettyPrinter; + +interface PrettyPrinterStrategy +{ + /** + * @param mixed $model + * + * @return bool + */ + public function supports($model); + + /** + * @param mixed $model + * @param array $parameters + * + * @return string + */ + public function generateCode($model, array $parameters = array()); +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Line/ContractLineStrategy.php b/src/Memio/PrettyPrinter/TwigExtension/Line/ContractLineStrategy.php new file mode 100644 index 0000000..4e957cc --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Line/ContractLineStrategy.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; +use Memio\Model\Contract; + +class ContractLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof Contract; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $constants = $model->allConstants(); + $methods = $model->allMethods(); + if ('constants' === $block) { + return (!empty($constants) && !empty($methods)); + } + + throw new InvalidArgumentException('The function needs_line_after does not support given "'.$block.'"'); + } +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Line/FileLineStrategy.php b/src/Memio/PrettyPrinter/TwigExtension/Line/FileLineStrategy.php new file mode 100644 index 0000000..7570937 --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Line/FileLineStrategy.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; +use Memio\Model\File; + +class FileLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof File; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $fullyQualifiedNames = $model->allFullyQualifiedNames(); + if ('fully_qualified_names' === $block) { + return (!empty($fullyQualifiedNames)); + } + + throw new InvalidArgumentException('The function needs_line_after does not support given "'.$block.'"'); + } +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Line/Line.php b/src/Memio/PrettyPrinter/TwigExtension/Line/Line.php new file mode 100644 index 0000000..0ac523b --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Line/Line.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; + +class Line +{ + /** + * @var array + */ + private $strategies = array(); + + /** + * @param LineStrategy $lineStrategy + */ + public function add(LineStrategy $lineStrategy) + { + $this->strategies[] = $lineStrategy; + } + + /** + * @param mixed $model + * @param string $block + * + * @throws InvalidArgumentException If no strategy supports the given model + */ + public function needsLineAfter($model, $block) + { + foreach ($this->strategies as $strategy) { + if ($strategy->supports($model)) { + return $strategy->needsLineAfter($model, $block); + } + } + + throw new InvalidArgumentException('No strategy supports given model '.get_class($model)); + } +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Line/LineStrategy.php b/src/Memio/PrettyPrinter/TwigExtension/Line/LineStrategy.php new file mode 100644 index 0000000..2237579 --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Line/LineStrategy.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension\Line; + +interface LineStrategy +{ + /** + * @param mixed $model + * + * @return bool + */ + public function supports($model); + + /** + * @param mixed $model + * @param string $block + * + * @throws \Memio\PrettyPrinter\Exception\InvalidArgumentException If the block isn't supported + */ + public function needsLineAfter($model, $block); +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Line/MethodPhpdocLineStrategy.php b/src/Memio/PrettyPrinter/TwigExtension/Line/MethodPhpdocLineStrategy.php new file mode 100644 index 0000000..af5e307 --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Line/MethodPhpdocLineStrategy.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\Phpdoc\MethodPhpdoc; + +class MethodPhpdocLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof MethodPhpdoc; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $parameterTags = $model->getParameterTags(); + + $hasApiTag = (null !== $model->getApiTag()); + $hasParameterTags = (!empty($parameterTags)); + $hasDescription = (null !== $model->getDescription()); + $hasDeprecationTag = (null !== $model->getDeprecationTag()); + if ('description' === $block) { + return ($hasDescription && ($hasParameterTags || $hasApiTag || $hasDeprecationTag)); + } + if ('parameter_tags' === $block) { + return ($hasParameterTags && ($hasApiTag || $hasDeprecationTag)); + } + if ('deprecation_tag' === $block) { + return ($hasApiTag && $hasDeprecationTag); + } + } +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Line/ObjectLineStrategy.php b/src/Memio/PrettyPrinter/TwigExtension/Line/ObjectLineStrategy.php new file mode 100644 index 0000000..b25b641 --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Line/ObjectLineStrategy.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; +use Memio\Model\Object; + +class ObjectLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof Object; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $constants = $model->allConstants(); + $properties = $model->allProperties(); + $methods = $model->allMethods(); + if ('constants' === $block) { + return (!empty($constants) && (!empty($properties) || !empty($methods))); + } + if ('properties' === $block) { + return (!empty($properties) && !empty($methods)); + } + + throw new InvalidArgumentException('The function needs_line_after does not support given "'.$block.'"'); + } +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Line/StructurePhpdocLineStrategy.php b/src/Memio/PrettyPrinter/TwigExtension/Line/StructurePhpdocLineStrategy.php new file mode 100644 index 0000000..d1301ec --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Line/StructurePhpdocLineStrategy.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension\Line; + +use Memio\Model\Phpdoc\StructurePhpdoc; + +class StructurePhpdocLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof StructurePhpdoc; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $hasDescription = (null !== $model->getDescription()); + $hasApiTag = (null !== $model->getApiTag()); + $hasDeprecationTag = (null !== $model->getDeprecationTag()); + if ('description' === $block) { + return ($hasDescription && ($hasApiTag || $hasDeprecationTag)); + } + if ('deprecation_tag' === $block) { + return ($hasApiTag && $hasDeprecationTag); + } + } +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Type.php b/src/Memio/PrettyPrinter/TwigExtension/Type.php new file mode 100644 index 0000000..d6c4d8a --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Type.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension; + +use Memio\Model\Argument; +use Memio\Model\Contract; +use Memio\Model\Type as ModelType; +use Twig_Extension; +use Twig_SimpleTest; +use Twig_SimpleFunction; + +class Type extends Twig_Extension +{ + /** + * {@inheritDoc} + */ + public function getTests() + { + return array( + new Twig_SimpleTest('contract', array($this, 'isContract')), + ); + } + + /** + * {@inheritDoc} + */ + public function getFunctions() + { + return array( + new Twig_SimpleFunction('has_typehint', array($this, 'hasTypehint')), + ); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'type'; + } + + /** + * @param mixed $model + * + * @return bool + */ + public function isContract($model) + { + return $model instanceof Contract; + } + + /** + * @param mixed $model + * + * @return bool + */ + public function hasTypehint($model) + { + if (!$model instanceof Argument) { + return false; + } + $type = new ModelType($model->getType()); + + return $type->hasTypehint(); + } +} diff --git a/src/Memio/PrettyPrinter/TwigExtension/Whitespace.php b/src/Memio/PrettyPrinter/TwigExtension/Whitespace.php new file mode 100644 index 0000000..2cce4c3 --- /dev/null +++ b/src/Memio/PrettyPrinter/TwigExtension/Whitespace.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\PrettyPrinter\TwigExtension; + +use Memio\PrettyPrinter\TwigExtension\Line\Line; +use Twig_Extension; +use Twig_SimpleFilter; +use Twig_SimpleFunction; + +class Whitespace extends Twig_Extension +{ + /** + * @var Line + */ + private $line; + + /** + * @param Line $line + */ + public function __construct(Line $line) + { + $this->line = $line; + } + + /** + * {@inheritDoc} + */ + public function getFunctions() + { + return array( + new Twig_SimpleFunction('needs_line_after', array($this->line, 'needsLineAfter')), + ); + } + + /** + * {@inheritDoc} + */ + public function getFilters() + { + return array( + new Twig_SimpleFilter('align', array($this, 'align')), + new Twig_SimpleFilter('indent', array($this, 'indent')), + ); + } + + /** + * @param string $current + * @param array $collection + * + * @return string + */ + public function align($current, $collection) + { + $elementLength = strlen($current); + $longestElement = $elementLength; + foreach ($collection as $element) { + if ('Memio\Model\Phpdoc\ParameterTag' === get_class($element)) { + $longestElement = max($longestElement, strlen($element->getType())); + } + } + + return $current.str_repeat(' ', $longestElement - $elementLength); + } + + /** + * @param string $text + * @param int $level + * @param string $type + * + * @return string + */ + public function indent($text, $level = 1, $type = 'code') + { + $lines = explode("\n", $text); + $indentedLines = array(); + if ('code' === $type) { + foreach ($lines as $line) { + $indentedLines[] = ' '.$line; + } + } + if ('phpdoc' === $type) { + foreach ($lines as $line) { + $indent = ' *'; + if (!empty($line)) { + $indent .= ' '; + } + $indentedLines[] = $indent.$line; + } + } + + return implode("\n", $indentedLines); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'whitespace'; + } +} diff --git a/templates/argument.twig b/templates/argument.twig new file mode 100644 index 0000000..5ac5abe --- /dev/null +++ b/templates/argument.twig @@ -0,0 +1,4 @@ +{{ has_typehint(argument) ? argument.type ~ ' ' }}${{ argument.name -}} +{% if argument.defaultValue%} + = {{ argument.defaultValue|raw -}} +{% endif %} diff --git a/templates/collection/argument_collection.twig b/templates/collection/argument_collection.twig new file mode 100644 index 0000000..202477e --- /dev/null +++ b/templates/collection/argument_collection.twig @@ -0,0 +1,8 @@ +{% set inline %} + {%- include 'collection/arguments/inline.twig' with { 'argument_collection': argument_collection } only -%} +{% endset %} +{% if inline|length > 120 - (length_restriction | default(0)) %} + {%- include 'collection/arguments/multiline.twig' with { 'argument_collection': argument_collection } only -%} +{% else %} + {{- inline -}} +{% endif %} diff --git a/templates/collection/arguments/inline.twig b/templates/collection/arguments/inline.twig new file mode 100644 index 0000000..ba49862 --- /dev/null +++ b/templates/collection/arguments/inline.twig @@ -0,0 +1,4 @@ +{% for argument in argument_collection %} + {%- include 'argument.twig' with { 'argument': argument } only -%} + {% if not loop.last %}, {% endif %} +{% endfor %} diff --git a/templates/collection/arguments/multiline.twig b/templates/collection/arguments/multiline.twig new file mode 100644 index 0000000..9151ede --- /dev/null +++ b/templates/collection/arguments/multiline.twig @@ -0,0 +1,5 @@ +{% for argument in argument_collection %} + + {% include 'argument.twig' with { 'argument': argument } only %}{{ (not loop.last) ? ',' }} +{%- endfor %} +{{ "\n " -}} diff --git a/templates/collection/constant_collection.twig b/templates/collection/constant_collection.twig new file mode 100644 index 0000000..1184187 --- /dev/null +++ b/templates/collection/constant_collection.twig @@ -0,0 +1,4 @@ +{% for constant in constant_collection -%} + {% include 'constant.twig' with { 'constant': constant } only %} + +{% endfor -%} diff --git a/templates/collection/contract_collection.twig b/templates/collection/contract_collection.twig new file mode 100644 index 0000000..cf41926 --- /dev/null +++ b/templates/collection/contract_collection.twig @@ -0,0 +1,4 @@ +{% for contract in contract_collection -%} + {{ contract.name }}{{ not loop.last ? ', ' }} +{%- endfor %} + diff --git a/templates/collection/fully_qualified_name_collection.twig b/templates/collection/fully_qualified_name_collection.twig new file mode 100644 index 0000000..4002fee --- /dev/null +++ b/templates/collection/fully_qualified_name_collection.twig @@ -0,0 +1,4 @@ +{% for fully_qualified_name in fully_qualified_name_collection %} +{% include 'fully_qualified_name.twig' with { 'fully_qualified_name': fully_qualified_name } only %} + +{% endfor -%} diff --git a/templates/collection/method_collection.twig b/templates/collection/method_collection.twig new file mode 100644 index 0000000..ec70718 --- /dev/null +++ b/templates/collection/method_collection.twig @@ -0,0 +1,3 @@ +{% for method in method_collection -%} + {% include 'method.twig' with { 'method': method } only %}{{ loop.last ? "\n" : "\n\n" }} +{%- endfor -%} diff --git a/templates/collection/methods/pure_virtual.twig b/templates/collection/methods/pure_virtual.twig new file mode 100644 index 0000000..424c727 --- /dev/null +++ b/templates/collection/methods/pure_virtual.twig @@ -0,0 +1,7 @@ +{% if method.phpdoc %}{% include 'phpdoc/method_phpdoc.twig' with {'method_phpdoc': method.phpdoc} %}{% endif %} + {{ method.visibility ~ (method.visibility is not empty ? ' ') -}} + function {{ method.name }}({% include 'collection/argument_collection.twig' with { + 'argument_collection': method.allArguments, + 'length_restriction': 22 + method.name | length + } only %}); +{#- Trimming lines -#} diff --git a/templates/collection/methods/pure_virtual_collection.twig b/templates/collection/methods/pure_virtual_collection.twig new file mode 100644 index 0000000..c91dad2 --- /dev/null +++ b/templates/collection/methods/pure_virtual_collection.twig @@ -0,0 +1,3 @@ +{% for method in method_collection -%} + {% include 'collection/methods/pure_virtual.twig' with { 'method': method } only %}{{ loop.last ? "\n" : "\n\n" }} +{%- endfor -%} diff --git a/templates/collection/phpdoc/parameter_tag_collection.twig b/templates/collection/phpdoc/parameter_tag_collection.twig new file mode 100644 index 0000000..c83e3e1 --- /dev/null +++ b/templates/collection/phpdoc/parameter_tag_collection.twig @@ -0,0 +1,7 @@ +{% for parameter_tag in parameter_tag_collection -%} + {% include 'phpdoc/parameter_tag.twig' with { + 'parameter_tag': parameter_tag, + 'parameter_tag_collection': parameter_tag_collection + } only -%} + {{ not loop.last ? "\n" }} +{%- endfor -%} diff --git a/templates/collection/property_collection.twig b/templates/collection/property_collection.twig new file mode 100644 index 0000000..1bc205f --- /dev/null +++ b/templates/collection/property_collection.twig @@ -0,0 +1,3 @@ +{% for property in property_collection -%} + {% include 'property.twig' with { 'property': property } only %}{{ loop.last ? "\n" : "\n\n" }} +{%- endfor -%} diff --git a/templates/constant.twig b/templates/constant.twig new file mode 100644 index 0000000..b5f8df4 --- /dev/null +++ b/templates/constant.twig @@ -0,0 +1,2 @@ + const {{ constant.name }} = {{ constant.value|raw }}; +{#- Trimming lines -#} diff --git a/templates/contract.twig b/templates/contract.twig new file mode 100644 index 0000000..cfdd0a1 --- /dev/null +++ b/templates/contract.twig @@ -0,0 +1,15 @@ +{% if contract.phpdoc %}{% include 'phpdoc/structure_phpdoc.twig' with {'structure_phpdoc': contract.phpdoc} %}{% endif %} +interface {{ contract.name }} + {{- contract.allContracts is not empty ? ' extends ' }} + {%- include 'collection/contract_collection.twig' with { 'contract_collection': contract.allContracts } only %} +{ +{% include 'collection/constant_collection.twig' with { + 'constant_collection': contract.allConstants +} only %} +{% if needs_line_after(contract, 'constants') %} + +{% endif %} +{% include 'collection/methods/pure_virtual_collection.twig' with { + 'method_collection': contract.allMethods +} only %} +} diff --git a/templates/file.twig b/templates/file.twig new file mode 100644 index 0000000..d943336 --- /dev/null +++ b/templates/file.twig @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ diff --git a/templates/phpdoc/method_phpdoc.twig b/templates/phpdoc/method_phpdoc.twig new file mode 100644 index 0000000..4763ada --- /dev/null +++ b/templates/phpdoc/method_phpdoc.twig @@ -0,0 +1,21 @@ +{% set phpdoc_content %} +{% if method_phpdoc.description %}{% include 'phpdoc/description.twig' with { 'description': method_phpdoc.description } only %}{% endif %} +{{- needs_line_after(method_phpdoc, 'description') ? "\n\n" -}} +{% if method_phpdoc.parameterTags %}{% include 'collection/phpdoc/parameter_tag_collection.twig' with { 'parameter_tag_collection': method_phpdoc.parameterTags } only %}{% endif %} +{{- needs_line_after(method_phpdoc, 'parameter_tags') ? "\n\n" -}} +{% if method_phpdoc.deprecationTag %}{% include 'phpdoc/deprecation_tag.twig' with { 'deprecation_tag': method_phpdoc.deprecationTag } only %}{% endif %} +{{- needs_line_after(method_phpdoc, 'deprecation_tag') ? "\n\n" -}} +{% if method_phpdoc.apiTag %}{% include 'phpdoc/api_tag.twig' with { 'api_tag': method_phpdoc.apiTag } only %}{% endif %} +{% endset -%} + +{% set phpdoc %} +{% if not method_phpdoc.isEmpty %} +/** +{{ phpdoc_content|indent(1, 'phpdoc') }} + */ +{%- endif %} +{% endset -%} + +{% if not method_phpdoc.isEmpty %} +{{ phpdoc|indent(1) }} +{% endif %} diff --git a/templates/phpdoc/parameter_tag.twig b/templates/phpdoc/parameter_tag.twig new file mode 100644 index 0000000..efb0b4c --- /dev/null +++ b/templates/phpdoc/parameter_tag.twig @@ -0,0 +1,3 @@ +{% set with_collection = parameter_tag_collection is not defined ? [] : parameter_tag_collection %} +@param {{ parameter_tag.type|align(with_collection) }} ${{ parameter_tag.name }}{{ parameter_tag.description ? ' ' ~ parameter_tag.description }} +{#- Trimming lines -#} diff --git a/templates/phpdoc/property_phpdoc.twig b/templates/phpdoc/property_phpdoc.twig new file mode 100644 index 0000000..e179ee6 --- /dev/null +++ b/templates/phpdoc/property_phpdoc.twig @@ -0,0 +1,15 @@ +{% set phpdoc_content %} +{% if property_phpdoc.VariableTag %}{% include 'phpdoc/variable_tag.twig' with { 'variable_tag': property_phpdoc.VariableTag } only %}{% endif %} +{% endset -%} + +{% set phpdoc %} +{% if not property_phpdoc.isEmpty %} +/** +{{ phpdoc_content|indent(1, 'phpdoc') }} + */ +{%- endif %} +{% endset -%} + +{% if not property_phpdoc.isEmpty %} +{{ phpdoc|indent(1) }} +{% endif %} diff --git a/templates/phpdoc/structure_phpdoc.twig b/templates/phpdoc/structure_phpdoc.twig new file mode 100644 index 0000000..910ea67 --- /dev/null +++ b/templates/phpdoc/structure_phpdoc.twig @@ -0,0 +1,12 @@ +{% set phpdoc_content %} +{% if structure_phpdoc.description %}{% include 'phpdoc/description.twig' with { 'description': structure_phpdoc.description } only %}{% endif %} +{{- needs_line_after(structure_phpdoc, 'description') ? "\n\n" -}} +{% if structure_phpdoc.deprecationTag %}{% include 'phpdoc/deprecation_tag.twig' with { 'deprecation_tag': structure_phpdoc.deprecationTag } only %}{% endif %} +{{- needs_line_after(structure_phpdoc, 'deprecation_tag') ? "\n\n" -}} +{% if structure_phpdoc.apiTag %}{% include 'phpdoc/api_tag.twig' with { 'api_tag': structure_phpdoc.apiTag } only %}{% endif %} +{% endset %} +{% if not structure_phpdoc.isEmpty %} +/** +{{ phpdoc_content|indent(1, 'phpdoc') }} + */ +{% endif %} diff --git a/templates/phpdoc/variable_tag.twig b/templates/phpdoc/variable_tag.twig new file mode 100644 index 0000000..b49f69f --- /dev/null +++ b/templates/phpdoc/variable_tag.twig @@ -0,0 +1,2 @@ +@var {{ variable_tag.type }} +{#- Trimming lines -#} diff --git a/templates/property.twig b/templates/property.twig new file mode 100644 index 0000000..8f5c9f5 --- /dev/null +++ b/templates/property.twig @@ -0,0 +1,7 @@ +{% if property.phpdoc %}{% include 'phpdoc/property_phpdoc.twig' with {'property_phpdoc': property.phpdoc} %}{% endif %} +{#- Trimming lines #} + {{ property.visibility ~ ' ' }} + {{- property.isStatic ? 'static ' -}} + ${{ property.name -}} + {{ property.defaultValue ? (' = ' ~ property.defaultValue)|raw }}; +{#- Trimming lines -#}