MarkdownDescriptor.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Console\Descriptor;
  11. use Symfony\Component\Console\Application;
  12. use Symfony\Component\Console\Command\Command;
  13. use Symfony\Component\Console\Helper\Helper;
  14. use Symfony\Component\Console\Input\InputArgument;
  15. use Symfony\Component\Console\Input\InputDefinition;
  16. use Symfony\Component\Console\Input\InputOption;
  17. use Symfony\Component\Console\Output\OutputInterface;
  18. /**
  19. * Markdown descriptor.
  20. *
  21. * @author Jean-François Simon <contact@jfsimon.fr>
  22. *
  23. * @internal
  24. */
  25. class MarkdownDescriptor extends Descriptor
  26. {
  27. public function describe(OutputInterface $output, object $object, array $options = []): void
  28. {
  29. $decorated = $output->isDecorated();
  30. $output->setDecorated(false);
  31. parent::describe($output, $object, $options);
  32. $output->setDecorated($decorated);
  33. }
  34. protected function write(string $content, bool $decorated = true): void
  35. {
  36. parent::write($content, $decorated);
  37. }
  38. protected function describeInputArgument(InputArgument $argument, array $options = []): void
  39. {
  40. $this->write(
  41. '#### `'.($argument->getName() ?: '<none>')."`\n\n"
  42. .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
  43. .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
  44. .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
  45. .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
  46. );
  47. }
  48. protected function describeInputOption(InputOption $option, array $options = []): void
  49. {
  50. $name = '--'.$option->getName();
  51. if ($option->isNegatable()) {
  52. $name .= '|--no-'.$option->getName();
  53. }
  54. if ($option->getShortcut()) {
  55. $name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
  56. }
  57. $this->write(
  58. '#### `'.$name.'`'."\n\n"
  59. .($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '')
  60. .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
  61. .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
  62. .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
  63. .'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
  64. .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
  65. );
  66. }
  67. protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
  68. {
  69. if ($showArguments = \count($definition->getArguments()) > 0) {
  70. $this->write('### Arguments');
  71. foreach ($definition->getArguments() as $argument) {
  72. $this->write("\n\n");
  73. $this->describeInputArgument($argument);
  74. }
  75. }
  76. if (\count($definition->getOptions()) > 0) {
  77. if ($showArguments) {
  78. $this->write("\n\n");
  79. }
  80. $this->write('### Options');
  81. foreach ($definition->getOptions() as $option) {
  82. $this->write("\n\n");
  83. $this->describeInputOption($option);
  84. }
  85. }
  86. }
  87. protected function describeCommand(Command $command, array $options = []): void
  88. {
  89. if ($options['short'] ?? false) {
  90. $this->write(
  91. '`'.$command->getName()."`\n"
  92. .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
  93. .($command->getDescription() ? $command->getDescription()."\n\n" : '')
  94. .'### Usage'."\n\n"
  95. .array_reduce($command->getAliases(), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
  96. );
  97. return;
  98. }
  99. $command->mergeApplicationDefinition(false);
  100. $this->write(
  101. '`'.$command->getName()."`\n"
  102. .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
  103. .($command->getDescription() ? $command->getDescription()."\n\n" : '')
  104. .'### Usage'."\n\n"
  105. .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
  106. );
  107. if ($help = $command->getProcessedHelp()) {
  108. $this->write("\n");
  109. $this->write($help);
  110. }
  111. $definition = $command->getDefinition();
  112. if ($definition->getOptions() || $definition->getArguments()) {
  113. $this->write("\n\n");
  114. $this->describeInputDefinition($definition);
  115. }
  116. }
  117. protected function describeApplication(Application $application, array $options = []): void
  118. {
  119. $describedNamespace = $options['namespace'] ?? null;
  120. $description = new ApplicationDescription($application, $describedNamespace);
  121. $title = $this->getApplicationTitle($application);
  122. $this->write($title."\n".str_repeat('=', Helper::width($title)));
  123. foreach ($description->getNamespaces() as $namespace) {
  124. if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
  125. $this->write("\n\n");
  126. $this->write('**'.$namespace['id'].':**');
  127. }
  128. $this->write("\n\n");
  129. $this->write(implode("\n", array_map(fn ($commandName) => sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())), $namespace['commands'])));
  130. }
  131. foreach ($description->getCommands() as $command) {
  132. $this->write("\n\n");
  133. $this->describeCommand($command, $options);
  134. }
  135. }
  136. private function getApplicationTitle(Application $application): string
  137. {
  138. if ('UNKNOWN' !== $application->getName()) {
  139. if ('UNKNOWN' !== $application->getVersion()) {
  140. return sprintf('%s %s', $application->getName(), $application->getVersion());
  141. }
  142. return $application->getName();
  143. }
  144. return 'Console Tool';
  145. }
  146. }