MarkdownDescriptor.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. /**
  28. * {@inheritdoc}
  29. */
  30. public function describe(OutputInterface $output, object $object, array $options = [])
  31. {
  32. $decorated = $output->isDecorated();
  33. $output->setDecorated(false);
  34. parent::describe($output, $object, $options);
  35. $output->setDecorated($decorated);
  36. }
  37. /**
  38. * {@inheritdoc}
  39. */
  40. protected function write(string $content, bool $decorated = true)
  41. {
  42. parent::write($content, $decorated);
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. protected function describeInputArgument(InputArgument $argument, array $options = [])
  48. {
  49. $this->write(
  50. '#### `'.($argument->getName() ?: '<none>')."`\n\n"
  51. .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
  52. .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
  53. .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
  54. .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
  55. );
  56. }
  57. /**
  58. * {@inheritdoc}
  59. */
  60. protected function describeInputOption(InputOption $option, array $options = [])
  61. {
  62. $name = '--'.$option->getName();
  63. if ($option->isNegatable()) {
  64. $name .= '|--no-'.$option->getName();
  65. }
  66. if ($option->getShortcut()) {
  67. $name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
  68. }
  69. $this->write(
  70. '#### `'.$name.'`'."\n\n"
  71. .($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '')
  72. .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
  73. .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
  74. .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
  75. .'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
  76. .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
  77. );
  78. }
  79. /**
  80. * {@inheritdoc}
  81. */
  82. protected function describeInputDefinition(InputDefinition $definition, array $options = [])
  83. {
  84. if ($showArguments = \count($definition->getArguments()) > 0) {
  85. $this->write('### Arguments');
  86. foreach ($definition->getArguments() as $argument) {
  87. $this->write("\n\n");
  88. if (null !== $describeInputArgument = $this->describeInputArgument($argument)) {
  89. $this->write($describeInputArgument);
  90. }
  91. }
  92. }
  93. if (\count($definition->getOptions()) > 0) {
  94. if ($showArguments) {
  95. $this->write("\n\n");
  96. }
  97. $this->write('### Options');
  98. foreach ($definition->getOptions() as $option) {
  99. $this->write("\n\n");
  100. if (null !== $describeInputOption = $this->describeInputOption($option)) {
  101. $this->write($describeInputOption);
  102. }
  103. }
  104. }
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. protected function describeCommand(Command $command, array $options = [])
  110. {
  111. if ($options['short'] ?? false) {
  112. $this->write(
  113. '`'.$command->getName()."`\n"
  114. .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
  115. .($command->getDescription() ? $command->getDescription()."\n\n" : '')
  116. .'### Usage'."\n\n"
  117. .array_reduce($command->getAliases(), function ($carry, $usage) {
  118. return $carry.'* `'.$usage.'`'."\n";
  119. })
  120. );
  121. return;
  122. }
  123. $command->mergeApplicationDefinition(false);
  124. $this->write(
  125. '`'.$command->getName()."`\n"
  126. .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
  127. .($command->getDescription() ? $command->getDescription()."\n\n" : '')
  128. .'### Usage'."\n\n"
  129. .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) {
  130. return $carry.'* `'.$usage.'`'."\n";
  131. })
  132. );
  133. if ($help = $command->getProcessedHelp()) {
  134. $this->write("\n");
  135. $this->write($help);
  136. }
  137. $definition = $command->getDefinition();
  138. if ($definition->getOptions() || $definition->getArguments()) {
  139. $this->write("\n\n");
  140. $this->describeInputDefinition($definition);
  141. }
  142. }
  143. /**
  144. * {@inheritdoc}
  145. */
  146. protected function describeApplication(Application $application, array $options = [])
  147. {
  148. $describedNamespace = $options['namespace'] ?? null;
  149. $description = new ApplicationDescription($application, $describedNamespace);
  150. $title = $this->getApplicationTitle($application);
  151. $this->write($title."\n".str_repeat('=', Helper::width($title)));
  152. foreach ($description->getNamespaces() as $namespace) {
  153. if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
  154. $this->write("\n\n");
  155. $this->write('**'.$namespace['id'].':**');
  156. }
  157. $this->write("\n\n");
  158. $this->write(implode("\n", array_map(function ($commandName) use ($description) {
  159. return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName()));
  160. }, $namespace['commands'])));
  161. }
  162. foreach ($description->getCommands() as $command) {
  163. $this->write("\n\n");
  164. if (null !== $describeCommand = $this->describeCommand($command, $options)) {
  165. $this->write($describeCommand);
  166. }
  167. }
  168. }
  169. private function getApplicationTitle(Application $application): string
  170. {
  171. if ('UNKNOWN' !== $application->getName()) {
  172. if ('UNKNOWN' !== $application->getVersion()) {
  173. return sprintf('%s %s', $application->getName(), $application->getVersion());
  174. }
  175. return $application->getName();
  176. }
  177. return 'Console Tool';
  178. }
  179. }