From 88cfa2afe29d7f64cf0fc9369be0fad593574583 Mon Sep 17 00:00:00 2001 From: dantleech Date: Sun, 17 Aug 2014 10:48:32 +0200 Subject: [PATCH 1/2] Added option to dump a RST reference for the application - For use by the documentation --- CHANGELOG.md | 2 + .../Console/Application/ShellApplication.php | 1 - .../Console/Command/Phpcr/NodeCopyCommand.php | 1 + .../Command/Phpcr/VersionRestoreCommand.php | 81 +++---- .../Shell/Console/Command/ShellCommand.php | 15 ++ .../Console/Descriptor/RstDescriptor.php | 217 ++++++++++++++++++ 6 files changed, 276 insertions(+), 41 deletions(-) create mode 100644 src/PHPCR/Shell/Console/Descriptor/RstDescriptor.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 611ca065..aa74f492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,8 @@ alpha-5 ### Features +- [shell] Added "--reference" option to session command to dump a complete shell reference + in restructured text format for the official documentation. - [shell] Added "shell:clear" command to support clearing the console output - [general] The shell supports being embedded as a dependency - [node:edit] New command `node:edit` enables editing of entire node diff --git a/src/PHPCR/Shell/Console/Application/ShellApplication.php b/src/PHPCR/Shell/Console/Application/ShellApplication.php index 07b9152c..a2992cab 100644 --- a/src/PHPCR/Shell/Console/Application/ShellApplication.php +++ b/src/PHPCR/Shell/Console/Application/ShellApplication.php @@ -187,7 +187,6 @@ protected function registerShellCommands() { // add shell-specific commands $this->add(new CommandShell\AliasListCommand()); - $this->add(new CommandShell\ClearCommand()); $this->add(new CommandShell\ConfigInitCommand()); $this->add(new CommandShell\ConfigReloadCommand()); $this->add(new CommandShell\PathChangeCommand()); diff --git a/src/PHPCR/Shell/Console/Command/Phpcr/NodeCopyCommand.php b/src/PHPCR/Shell/Console/Command/Phpcr/NodeCopyCommand.php index ffc36879..55525e4d 100644 --- a/src/PHPCR/Shell/Console/Command/Phpcr/NodeCopyCommand.php +++ b/src/PHPCR/Shell/Console/Command/Phpcr/NodeCopyCommand.php @@ -31,6 +31,7 @@ protected function configure() The subgraph rooted at and including N' (call it S') is created and is identical to the subgraph rooted at and including N (call it S) with the following exceptions: + - Every node in S' is given a new and distinct identifier - or, if srcWorkspace is given - Every referenceable node in S' is given a new and distinct identifier diff --git a/src/PHPCR/Shell/Console/Command/Phpcr/VersionRestoreCommand.php b/src/PHPCR/Shell/Console/Command/Phpcr/VersionRestoreCommand.php index 1d8dfd30..48a291e6 100644 --- a/src/PHPCR/Shell/Console/Command/Phpcr/VersionRestoreCommand.php +++ b/src/PHPCR/Shell/Console/Command/Phpcr/VersionRestoreCommand.php @@ -20,50 +20,49 @@ protected function configure() $this->setHelp(<<If path is given and versionName is a version name: +* If path is given and versionName is a version name: Restores the node at path to the state defined by the version with the specified version name (versionName). This method will work regardless of whether the node at path is checked-in or not. -If path is given and versionName is a VersionInterface instance: - - Restores the specified version to path. There must be no existing - node at path. If one exists, a VersionException is thrown. - There must be a parent node to the location at path, otherwise a - PathNotFoundException is thrown. - If the would-be parent of the location path is actually a property, - or if a node type restriction would be violated, then a - ConstraintViolationException is thrown. - - -If versionName is VersionInterface instance: - Restores the node in the current workspace that is the versionable node - of the specified version to the state reflected in that version. - This method ignores checked-in status. - - -If versionName is an array of VersionInterface instances: - Restores a set of versions at once. Used in cases where a "chicken and - egg" problem of mutually referring REFERENCE properties would prevent - the restore in any serial order. - The following restrictions apply to the set of versions specified: If S - is the set of versions being restored simultaneously, - - For every version V in S that corresponds to a missing node, there - must also be a parent of V in S. - - S must contain at least one version that corresponds to an existing - node in the workspace. - - No V in S can be a root version (jcr:rootVersion). - If any of these restrictions does not hold, the restore will fail - because the system will be unable to determine the path locations to - which one or more versions are to be restored. In this case a - VersionException is thrown. - The versionable nodes in the current workspace that correspond to the - versions being restored define a set of (one or more) subgraphs. - -If the restore succeeds the changes made are dispatched immediately; - +* If path is given and versionName is a VersionInterface instance: + Restores the specified version to path. There must be no existing + node at path. If one exists, a VersionException is thrown. + There must be a parent node to the location at path, otherwise a + PathNotFoundException is thrown. + If the would-be parent of the location path is actually a property, + or if a node type restriction would be violated, then a + ConstraintViolationException is thrown. + + +* If versionName is VersionInterface instance: + Restores the node in the current workspace that is the versionable node + of the specified version to the state reflected in that version. + This method ignores checked-in status. + + +* If versionName is an array of VersionInterface instances: + Restores a set of versions at once. Used in cases where a "chicken and + egg" problem of mutually referring REFERENCE properties would prevent + the restore in any serial order. + The following restrictions apply to the set of versions specified: If S + is the set of versions being restored simultaneously, + - For every version V in S that corresponds to a missing node, there + must also be a parent of V in S. + - S must contain at least one version that corresponds to an existing + node in the workspace. + - No V in S can be a root version (jcr:rootVersion). + If any of these restrictions does not hold, the restore will fail + because the system will be unable to determine the path locations to + which one or more versions are to be restored. In this case a + VersionException is thrown. + The versionable nodes in the current workspace that correspond to the + versions being restored define a set of (one or more) subgraphs. + +If the restore succeeds the changes made are dispatched immediately; + there is no need to call save. If an array of VersionInterface instances is restored, an identifier @@ -85,8 +84,10 @@ protected function configure() OnParentVersion settings of COPY or VERSION are also governed by the removeExisting flag. -Note: The Java API defines this with multiple differing -signatures, you need to act accordingly in your implementation. +Note: + + The Java API defines this with multiple differing + signatures, you need to act accordingly in your implementation. HERE ); } diff --git a/src/PHPCR/Shell/Console/Command/ShellCommand.php b/src/PHPCR/Shell/Console/Command/ShellCommand.php index 37b268db..75e6ddc6 100644 --- a/src/PHPCR/Shell/Console/Command/ShellCommand.php +++ b/src/PHPCR/Shell/Console/Command/ShellCommand.php @@ -10,6 +10,7 @@ use PHPCR\Shell\Console\Application\Shell; use PHPCR\Shell\Console\Input\StringInput; use Symfony\Component\Console\Input\InputArgument; +use PHPCR\Shell\Console\Descriptor\RstDescriptor; /** * The shell command is the command used to configure the shell session @@ -66,8 +67,11 @@ public function configure() new InputOption('--unsupported', null, InputOption::VALUE_NONE, 'Show all commands, including commands not supported by the repository'), new InputOption('--command', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Run the given command'), + new InputOption('--reference', null, InputOption::VALUE_NONE, 'Dump a complete command reference in RST format') + new InputArgument('workspace', InputArgument::OPTIONAL, 'Workspace to start with', 'default'), )); + )); } /** @@ -76,8 +80,11 @@ public function configure() public function execute(InputInterface $input, OutputInterface $output) { $showUnspported = $input->getOption('unsupported'); + $noInteraction = $input->getOption('no-interaction'); + $dumpReference = $input->getOption('reference'); $application = $this->application; + $application->setShowUnsupported($showUnspported); $application->dispatchProfileInitEvent($input, $output); @@ -101,6 +108,14 @@ public function execute(InputInterface $input, OutputInterface $output) $application = new Shell($this->application); } + if ($dumpReference) { + $this->application->init(); + $descriptor = new RstDescriptor(); + $out = $descriptor->describe($this->application); + die($out); + return 0; + } + if ($noInteraction) { return 0; } diff --git a/src/PHPCR/Shell/Console/Descriptor/RstDescriptor.php b/src/PHPCR/Shell/Console/Descriptor/RstDescriptor.php new file mode 100644 index 00000000..5da579b3 --- /dev/null +++ b/src/PHPCR/Shell/Console/Descriptor/RstDescriptor.php @@ -0,0 +1,217 @@ + + */ +class RstDescriptor extends Descriptor +{ + protected $ignoreOptions = array( + 'verbose', + 'version', + 'quiet', + 'ansi', + 'no-ansi', + 'no-interaction', + ); + + protected function getCommandRefName($name) + { + return 'phpcr_shell_command_' . str_replace(':', '', $name); + } + + protected function underline($string, $char) + { + return str_repeat($char, strlen($string)); + } + + protected function formatText($text) + { + $lines = explode("\n", $text); + $newLines = array(); + $blockLines = array(); + + foreach ($lines as $line) { + // if line is indented by 2 or 4 spaces, assume + // that it is a code block + if (preg_match('{^[ | ]}', $line)) { + $inBlock = true; + $blockLines = array(); + } else { + $inBlock = false; + } + + if (true === $inBlock) { + $blockLines[] = $line; + continue; + } + + if (false === $inBlock && $blockLines) { + $newLines[] = ''; + $newLines[] = '.. code-block:: bash'; + $newLines[] = ''; + foreach ($blockLines as $blockLine) { + $blockLine = preg_replace('{( +)<(.*?)>(.*)}', '\3', $blockLine); + $newLines[] = ' ' . $blockLine; + } + $newLines[] = ''; + $blockLines = array(); + } else { + // replace inline tags with literals + $line = preg_replace('{<(.*?)>(.*)}', '``\2``', $line); + $newLines[] = $line; + } + } + + + return implode("\n", $newLines); + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + return implode("\n", array( + $argument->getName(), + $this->underline($argument->getName(), '"'), + '', + '* **Name:** ``'. ($argument->getName() ?: '**').'``', + '* **Is required:** '.($argument->isRequired() ? 'yes' : 'no'), + '* **Is array:** '.($argument->isArray() ? 'yes' : 'no'), + '* **Description:** '.($argument->getDescription() ?: '**'), + '* **Default:** ``'.str_replace("\n", '', var_export($argument->getDefault(), true)).'``', + '', + )); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + if (in_array($option->getName(), $this->ignoreOptions)) { + return ''; + } + + return implode("\n", array( + $option->getName(), + $this->underline($option->getName(), '"'), + '', + '* **Name:** ``--'.$option->getName().'``', + '* **Accept value:** '.($option->acceptValue() ? 'yes' : 'no'), + '* **Is value required:** '.($option->isValueRequired() ? 'yes' : 'no'), + '* **Is multiple:** '.($option->isArray() ? 'yes' : 'no'), + '* **Description:** '.($option->getDescription() ?: '**'), + '* **Default:** ``'.str_replace("\n", '', var_export($option->getDefault(), true)).'``', + '', + )); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $blocks = array(); + + if (count($definition->getArguments()) > 0) { + $blocks[] = 'Arguments:'; + $blocks[] = '~~~~~~~~~~'; + $blocks[] = ''; + foreach ($definition->getArguments() as $argument) { + $blocks[] = $this->describeInputArgument($argument); + } + $blocks[] = ''; + } + + if (count($definition->getOptions()) > 0) { + $blocks[] = 'Options:'; + $blocks[] = '~~~~~~~~'; + $blocks[] = ''; + foreach ($definition->getOptions() as $option) { + $blocks[] = $this->describeInputOption($option); + } + $blocks[] = ''; + } + + return implode("\n", $blocks); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $rst = array( + '', + '.. _' . $this->getCommandRefName($command->getName()) . ':', + '', + $command->getName(), + $this->underline($command->getName(), '-'), + '', + '* **Description:** '.($command->getDescription() ? $this->formatText($command->getDescription()) : '**'), + '* **Usage:** ``'.$command->getSynopsis().'``', + ); + + $rst[] = ''; + + if ($help = $command->getProcessedHelp()) { + $rst[] = $this->formatText($help); + $rst[] = ''; + } + + if ($definitionRst = $this->describeInputDefinition($command->getNativeDefinition())) { + $rst[] = $this->formatText($definitionRst); + $rst[] = ''; + } + + return implode("\n", $rst); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $blocks[] = 'Reference'; + $blocks[] = '========='; + $blocks[] = ''; + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $blocks[] = $namespace['id']; + $blocks[] = str_repeat('-', strlen($namespace['id'])); + $blocks[] = ''; + } + + $blocks[] = implode("\n", array_map(function ($commandName) { + return '* :ref:`' . $this->getCommandRefName($commandName) . '`'; + } , $namespace['commands'])); + $blocks[] = ''; + } + + foreach ($description->getCommands() as $command) { + $blocks[] = $this->describeCommand($command); + } + + return implode("\n", $blocks); + } +} From f5a3a218ca915f719beb5762bdf7c677c07267c2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 22 Dec 2014 11:59:21 +0000 Subject: [PATCH 2/2] Foo --- src/PHPCR/Shell/Console/Command/ShellCommand.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/PHPCR/Shell/Console/Command/ShellCommand.php b/src/PHPCR/Shell/Console/Command/ShellCommand.php index 75e6ddc6..8968d0e4 100644 --- a/src/PHPCR/Shell/Console/Command/ShellCommand.php +++ b/src/PHPCR/Shell/Console/Command/ShellCommand.php @@ -67,10 +67,9 @@ public function configure() new InputOption('--unsupported', null, InputOption::VALUE_NONE, 'Show all commands, including commands not supported by the repository'), new InputOption('--command', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Run the given command'), - new InputOption('--reference', null, InputOption::VALUE_NONE, 'Dump a complete command reference in RST format') + new InputOption('--reference', null, InputOption::VALUE_NONE, 'Dump a complete command reference in RST format'), - new InputArgument('workspace', InputArgument::OPTIONAL, 'Workspace to start with', 'default'), - )); + new InputArgument('workspace', InputArgument::OPTIONAL, 'Workspace to start with', 'default') )); } @@ -111,8 +110,7 @@ public function execute(InputInterface $input, OutputInterface $output) if ($dumpReference) { $this->application->init(); $descriptor = new RstDescriptor(); - $out = $descriptor->describe($this->application); - die($out); + $descriptor->describe($output, $this->application); return 0; }