RuleSetDocumentationGenerator.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Documentation;
  13. use PhpCsFixer\Fixer\FixerInterface;
  14. use PhpCsFixer\Preg;
  15. use PhpCsFixer\RuleSet\DeprecatedRuleSetDescriptionInterface;
  16. use PhpCsFixer\RuleSet\RuleSetDescriptionInterface;
  17. use PhpCsFixer\Utils;
  18. /**
  19. * @internal
  20. */
  21. final class RuleSetDocumentationGenerator
  22. {
  23. private DocumentationLocator $locator;
  24. public function __construct(DocumentationLocator $locator)
  25. {
  26. $this->locator = $locator;
  27. }
  28. /**
  29. * @param list<FixerInterface> $fixers
  30. */
  31. public function generateRuleSetsDocumentation(RuleSetDescriptionInterface $definition, array $fixers): string
  32. {
  33. $fixerNames = [];
  34. foreach ($fixers as $fixer) {
  35. $fixerNames[$fixer->getName()] = $fixer;
  36. }
  37. $title = "Rule set ``{$definition->getName()}``";
  38. $titleLine = str_repeat('=', \strlen($title));
  39. $doc = "{$titleLine}\n{$title}\n{$titleLine}\n\n".$definition->getDescription();
  40. $warnings = [];
  41. if ($definition instanceof DeprecatedRuleSetDescriptionInterface) {
  42. $deprecationDescription = <<<'RST'
  43. This rule set is deprecated and will be removed in the next major version
  44. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  45. RST;
  46. $alternatives = $definition->getSuccessorsNames();
  47. if (0 !== \count($alternatives)) {
  48. $deprecationDescription .= RstUtils::toRst(
  49. sprintf(
  50. "\n\nYou should use %s instead.",
  51. Utils::naturalLanguageJoinWithBackticks($alternatives)
  52. ),
  53. 0
  54. );
  55. } else {
  56. $deprecationDescription .= 'No replacement available.';
  57. }
  58. $warnings[] = $deprecationDescription;
  59. }
  60. if ($definition->isRisky()) {
  61. $warnings[] = <<<'RST'
  62. This set contains rules that are risky
  63. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  64. Using this rule set may lead to changes in your code's logic and behaviour. Use it with caution and review changes before incorporating them into your code base.
  65. RST;
  66. }
  67. if ([] !== $warnings) {
  68. $warningsHeader = 1 === \count($warnings) ? 'Warning' : 'Warnings';
  69. $warningsHeaderLine = str_repeat('-', \strlen($warningsHeader));
  70. $doc .= "\n\n".implode(
  71. "\n",
  72. [
  73. $warningsHeader,
  74. $warningsHeaderLine,
  75. ...$warnings,
  76. ]
  77. );
  78. }
  79. $rules = $definition->getRules();
  80. if ([] === $rules) {
  81. $doc .= "\n\nThis is an empty set.";
  82. } else {
  83. $enabledRules = array_filter($rules, static fn ($config) => false !== $config);
  84. $disabledRules = array_filter($rules, static fn ($config) => false === $config);
  85. $listRules = function (array $rules) use (&$doc, $fixerNames): void {
  86. foreach ($rules as $rule => $config) {
  87. if (str_starts_with($rule, '@')) {
  88. $ruleSetPath = $this->locator->getRuleSetsDocumentationFilePath($rule);
  89. $ruleSetPath = substr($ruleSetPath, strrpos($ruleSetPath, '/'));
  90. $doc .= "\n- `{$rule} <.{$ruleSetPath}>`_";
  91. } else {
  92. $path = Preg::replace(
  93. '#^'.preg_quote($this->locator->getFixersDocumentationDirectoryPath(), '#').'/#',
  94. './../rules/',
  95. $this->locator->getFixerDocumentationFilePath($fixerNames[$rule])
  96. );
  97. $doc .= "\n- `{$rule} <{$path}>`_";
  98. }
  99. if (!\is_bool($config)) {
  100. $doc .= " with config:\n\n ``".Utils::toString($config)."``\n";
  101. }
  102. }
  103. };
  104. if ([] !== $enabledRules) {
  105. $doc .= "\n\nRules\n-----\n";
  106. $listRules($enabledRules);
  107. }
  108. if ([] !== $disabledRules) {
  109. $doc .= "\n\nDisabled rules\n--------------\n";
  110. $listRules($disabledRules);
  111. }
  112. }
  113. return $doc."\n";
  114. }
  115. /**
  116. * @param array<string, RuleSetDescriptionInterface> $setDefinitions
  117. */
  118. public function generateRuleSetsDocumentationIndex(array $setDefinitions): string
  119. {
  120. $documentation = <<<'RST'
  121. ===========================
  122. List of Available Rule sets
  123. ===========================
  124. RST;
  125. foreach ($setDefinitions as $path => $definition) {
  126. $path = substr($path, strrpos($path, '/'));
  127. $attributes = [];
  128. if ($definition instanceof DeprecatedRuleSetDescriptionInterface) {
  129. $attributes[] = 'deprecated';
  130. }
  131. $attributes = 0 === \count($attributes)
  132. ? ''
  133. : ' *('.implode(', ', $attributes).')*';
  134. $documentation .= "\n- `{$definition->getName()} <.{$path}>`_{$attributes}";
  135. }
  136. return $documentation."\n";
  137. }
  138. }