Skip to content

Commit b495bf4

Browse files
committed
Added option to dump a RST reference for the application
- For use by the documentation
1 parent 9515c6f commit b495bf4

File tree

6 files changed

+275
-43
lines changed

6 files changed

+275
-43
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ dev-master
66

77
### Features
88

9+
- [shell] Added "--reference" option to session command to dump a complete shell reference
10+
in restructured text format for the official documentation.
911
- [shell] Added "shell:clear" command to support clearing the console output
1012
- [general] The shell supports being embedded as a dependency
1113
- [node:edit] New command `node:edit` enables editing of entire node

src/PHPCR/Shell/Console/Application/ShellApplication.php

-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ protected function registerShellCommands()
228228
{
229229
// add shell-specific commands
230230
$this->add(new CommandShell\AliasListCommand());
231-
$this->add(new CommandShell\ClearCommand());
232231
$this->add(new CommandShell\ConfigInitCommand());
233232
$this->add(new CommandShell\ConfigReloadCommand());
234233
$this->add(new CommandShell\PathChangeCommand());

src/PHPCR/Shell/Console/Command/Phpcr/NodeCopyCommand.php

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ protected function configure()
3131
The subgraph rooted at and including N' (call it S') is created and is
3232
identical to the subgraph rooted at and including N (call it S) with the
3333
following exceptions:
34+
3435
- Every node in S' is given a new and distinct identifier
3536
- or, if <info>srcWorkspace</info> is given -
3637
Every referenceable node in S' is given a new and distinct identifier

src/PHPCR/Shell/Console/Command/Phpcr/VersionRestoreCommand.php

+41-40
Original file line numberDiff line numberDiff line change
@@ -20,50 +20,49 @@ protected function configure()
2020
$this->setHelp(<<<HERE
2121
Attempt to restore an old version of a node.
2222
23-
<em>If <info>path</info> is given and <info>versionName</info> is a version name:</em>
23+
* If <info>path</info> is given and <info>versionName</info> is a version name:
2424
Restores the node at <info>path</info> to the state defined by the version with
2525
the specified version name (<info>versionName</info>).
2626
This method will work regardless of whether the node at path is
2727
checked-in or not.
2828
2929
30-
<em>If <info>path</info> is given and <info>versionName</info> is a VersionInterface instance:
31-
</em>
32-
Restores the specified version to <info>path</info>. There must be no existing
33-
node at <info>path</info>. If one exists, a VersionException is thrown.
34-
There must be a parent node to the location at <info>path</info>, otherwise a
35-
PathNotFoundException is thrown.
36-
If the would-be parent of the location <info>path</info> is actually a property,
37-
or if a node type restriction would be violated, then a
38-
ConstraintViolationException is thrown.
39-
40-
41-
<em>If <info>versionName</info> is VersionInterface instance:</em>
42-
Restores the node in the current workspace that is the versionable node
43-
of the specified version to the state reflected in that version.
44-
This method ignores checked-in status.
45-
46-
47-
<em>If <info>versionName</info> is an array of VersionInterface instances:</em>
48-
Restores a set of versions at once. Used in cases where a "chicken and
49-
egg" problem of mutually referring REFERENCE properties would prevent
50-
the restore in any serial order.
51-
The following restrictions apply to the set of versions specified: If S
52-
is the set of versions being restored simultaneously,
53-
- For every version V in S that corresponds to a missing node, there
54-
must also be a parent of V in S.
55-
- S must contain at least one version that corresponds to an existing
56-
node in the workspace.
57-
- No V in S can be a root version (jcr:rootVersion).
58-
If any of these restrictions does not hold, the restore will fail
59-
because the system will be unable to determine the path locations to
60-
which one or more versions are to be restored. In this case a
61-
VersionException is thrown.
62-
The versionable nodes in the current workspace that correspond to the
63-
versions being restored define a set of (one or more) subgraphs.
64-
65-
<em>If the restore succeeds the changes made are dispatched immediately;
66-
</em>
30+
* If <info>path</info> is given and <info>versionName</info> is a VersionInterface instance:
31+
Restores the specified version to <info>path</info>. There must be no existing
32+
node at <info>path</info>. If one exists, a VersionException is thrown.
33+
There must be a parent node to the location at <info>path</info>, otherwise a
34+
PathNotFoundException is thrown.
35+
If the would-be parent of the location <info>path</info> is actually a property,
36+
or if a node type restriction would be violated, then a
37+
ConstraintViolationException is thrown.
38+
39+
40+
* If <info>versionName</info> is VersionInterface instance:
41+
Restores the node in the current workspace that is the versionable node
42+
of the specified version to the state reflected in that version.
43+
This method ignores checked-in status.
44+
45+
46+
* If <info>versionName</info> is an array of VersionInterface instances:
47+
Restores a set of versions at once. Used in cases where a "chicken and
48+
egg" problem of mutually referring REFERENCE properties would prevent
49+
the restore in any serial order.
50+
The following restrictions apply to the set of versions specified: If S
51+
is the set of versions being restored simultaneously,
52+
- For every version V in S that corresponds to a missing node, there
53+
must also be a parent of V in S.
54+
- S must contain at least one version that corresponds to an existing
55+
node in the workspace.
56+
- No V in S can be a root version (jcr:rootVersion).
57+
If any of these restrictions does not hold, the restore will fail
58+
because the system will be unable to determine the path locations to
59+
which one or more versions are to be restored. In this case a
60+
VersionException is thrown.
61+
The versionable nodes in the current workspace that correspond to the
62+
versions being restored define a set of (one or more) subgraphs.
63+
64+
If the restore succeeds the changes made are dispatched immediately;
65+
6766
there is no need to call save.
6867
6968
If an array of VersionInterface instances is restored, an identifier
@@ -85,8 +84,10 @@ protected function configure()
8584
OnParentVersion settings of COPY or VERSION are also governed by the
8685
<info>removeExisting</info> flag.
8786
88-
<b>Note:</b> The Java API defines this with multiple differing
89-
signatures, you need to act accordingly in your implementation.
87+
Note:
88+
89+
The Java API defines this with multiple differing
90+
signatures, you need to act accordingly in your implementation.
9091
HERE
9192
);
9293
}

src/PHPCR/Shell/Console/Command/ShellCommand.php

+14-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPCR\Shell\Console\Application\ShellApplication;
1010
use PHPCR\Shell\Console\Application\Shell;
1111
use PHPCR\Shell\Console\Input\StringInput;
12+
use PHPCR\Shell\Console\Descriptor\RstDescriptor;
1213

1314
/**
1415
* The shell command is the command used to configure the shell session
@@ -64,7 +65,8 @@ public function configure()
6465
new InputOption('--profile', '-p', InputOption::VALUE_OPTIONAL, 'Speicfy a profile name, use wit <info>--transport</info> to update or create'),
6566
new InputOption('--unsupported', null, InputOption::VALUE_NONE, 'Show all commands, including commands not supported by the repository'),
6667
new InputOption('--command', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Run the given command'),
67-
));
68+
new InputOption('--reference', null, InputOption::VALUE_NONE, 'Dump a complete command reference in RST format')
69+
));
6870
}
6971

7072
/**
@@ -73,12 +75,14 @@ public function configure()
7375
public function execute(InputInterface $input, OutputInterface $output)
7476
{
7577
$showUnspported = $input->getOption('unsupported');
78+
$noInteraction = $input->getOption('no-interaction');
79+
$dumpReference = $input->getOption('reference');
7680

7781
$application = $this->application;
82+
7883
$application->setShowUnsupported($showUnspported);
7984
$application->dispatchProfileInitEvent($input, $output);
8085

81-
$noInteraction = $input->getOption('no-interaction');
8286

8387
if ($commands = $input->getOption('command')) {
8488
$application->setCatchExceptions(false);
@@ -94,6 +98,14 @@ public function execute(InputInterface $input, OutputInterface $output)
9498
$application = new Shell($this->application);
9599
}
96100

101+
if ($dumpReference) {
102+
$this->application->init();
103+
$descriptor = new RstDescriptor();
104+
$out = $descriptor->describe($this->application);
105+
die($out);
106+
return 0;
107+
}
108+
97109
if ($noInteraction) {
98110
return 0;
99111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
<?php
2+
3+
namespace PHPCR\Shell\Console\Descriptor;
4+
5+
use Symfony\Component\Console\Descriptor\Descriptor;
6+
use Symfony\Component\Console\Input\InputArgument;
7+
use Symfony\Component\Console\Input\InputOption;
8+
use Symfony\Component\Console\Input\InputDefinition;
9+
use Symfony\Component\Console\Command\Command;
10+
use Symfony\Component\Console\Application;
11+
use Symfony\Component\Console\Descriptor\ApplicationDescription;
12+
13+
/**
14+
* Class which dumps the command reference in RST format
15+
* for use by the official documentation.
16+
*
17+
* @author Daniel Leech <[email protected]>
18+
*/
19+
class RstDescriptor extends Descriptor
20+
{
21+
protected $ignoreOptions = array(
22+
'verbose',
23+
'version',
24+
'quiet',
25+
'ansi',
26+
'no-ansi',
27+
'no-interaction',
28+
);
29+
30+
protected function getCommandRefName($name)
31+
{
32+
return 'phpcr_shell_command_' . str_replace(':', '', $name);
33+
}
34+
35+
protected function underline($string, $char)
36+
{
37+
return str_repeat($char, strlen($string));
38+
}
39+
40+
protected function formatText($text)
41+
{
42+
$lines = explode("\n", $text);
43+
$newLines = array();
44+
$blockLines = array();
45+
46+
foreach ($lines as $line) {
47+
// if line is indented by 2 or 4 spaces, assume
48+
// that it is a code block
49+
if (preg_match('{^[ | ]}', $line)) {
50+
$inBlock = true;
51+
$blockLines = array();
52+
} else {
53+
$inBlock = false;
54+
}
55+
56+
if (true === $inBlock) {
57+
$blockLines[] = $line;
58+
continue;
59+
}
60+
61+
if (false === $inBlock && $blockLines) {
62+
$newLines[] = '';
63+
$newLines[] = '.. code-block:: bash';
64+
$newLines[] = '';
65+
foreach ($blockLines as $blockLine) {
66+
$blockLine = preg_replace('{( +)<(.*?)>(.*)</(.*)>}', '\3', $blockLine);
67+
$newLines[] = ' ' . $blockLine;
68+
}
69+
$newLines[] = '';
70+
$blockLines = array();
71+
} else {
72+
// replace inline tags with literals
73+
$line = preg_replace('{<(.*?)>(.*)</(.*)>}', '``\2``', $line);
74+
$newLines[] = $line;
75+
}
76+
}
77+
78+
79+
return implode("\n", $newLines);
80+
}
81+
82+
/**
83+
* {@inheritdoc}
84+
*/
85+
protected function describeInputArgument(InputArgument $argument, array $options = array())
86+
{
87+
return implode("\n", array(
88+
$argument->getName(),
89+
$this->underline($argument->getName(), '"'),
90+
'',
91+
'* **Name:** ``'. ($argument->getName() ?: '*<none>*').'``',
92+
'* **Is required:** '.($argument->isRequired() ? 'yes' : 'no'),
93+
'* **Is array:** '.($argument->isArray() ? 'yes' : 'no'),
94+
'* **Description:** '.($argument->getDescription() ?: '*<none>*'),
95+
'* **Default:** ``'.str_replace("\n", '', var_export($argument->getDefault(), true)).'``',
96+
'',
97+
));
98+
}
99+
100+
/**
101+
* {@inheritdoc}
102+
*/
103+
protected function describeInputOption(InputOption $option, array $options = array())
104+
{
105+
if (in_array($option->getName(), $this->ignoreOptions)) {
106+
return '';
107+
}
108+
109+
return implode("\n", array(
110+
$option->getName(),
111+
$this->underline($option->getName(), '"'),
112+
'',
113+
'* **Name:** ``--'.$option->getName().'``',
114+
'* **Accept value:** '.($option->acceptValue() ? 'yes' : 'no'),
115+
'* **Is value required:** '.($option->isValueRequired() ? 'yes' : 'no'),
116+
'* **Is multiple:** '.($option->isArray() ? 'yes' : 'no'),
117+
'* **Description:** '.($option->getDescription() ?: '*<none>*'),
118+
'* **Default:** ``'.str_replace("\n", '', var_export($option->getDefault(), true)).'``',
119+
'',
120+
));
121+
}
122+
123+
/**
124+
* {@inheritdoc}
125+
*/
126+
protected function describeInputDefinition(InputDefinition $definition, array $options = array())
127+
{
128+
$blocks = array();
129+
130+
if (count($definition->getArguments()) > 0) {
131+
$blocks[] = 'Arguments:';
132+
$blocks[] = '~~~~~~~~~~';
133+
$blocks[] = '';
134+
foreach ($definition->getArguments() as $argument) {
135+
$blocks[] = $this->describeInputArgument($argument);
136+
}
137+
$blocks[] = '';
138+
}
139+
140+
if (count($definition->getOptions()) > 0) {
141+
$blocks[] = 'Options:';
142+
$blocks[] = '~~~~~~~~';
143+
$blocks[] = '';
144+
foreach ($definition->getOptions() as $option) {
145+
$blocks[] = $this->describeInputOption($option);
146+
}
147+
$blocks[] = '';
148+
}
149+
150+
return implode("\n", $blocks);
151+
}
152+
153+
/**
154+
* {@inheritdoc}
155+
*/
156+
protected function describeCommand(Command $command, array $options = array())
157+
{
158+
$command->getSynopsis();
159+
$command->mergeApplicationDefinition(false);
160+
161+
$rst = array(
162+
'',
163+
'.. _' . $this->getCommandRefName($command->getName()) . ':',
164+
'',
165+
$command->getName(),
166+
$this->underline($command->getName(), '-'),
167+
'',
168+
'* **Description:** '.($command->getDescription() ? $this->formatText($command->getDescription()) : '*<none>*'),
169+
'* **Usage:** ``'.$command->getSynopsis().'``',
170+
);
171+
172+
$rst[] = '';
173+
174+
if ($help = $command->getProcessedHelp()) {
175+
$rst[] = $this->formatText($help);
176+
$rst[] = '';
177+
}
178+
179+
if ($definitionRst = $this->describeInputDefinition($command->getNativeDefinition())) {
180+
$rst[] = $this->formatText($definitionRst);
181+
$rst[] = '';
182+
}
183+
184+
return implode("\n", $rst);
185+
}
186+
187+
/**
188+
* {@inheritdoc}
189+
*/
190+
protected function describeApplication(Application $application, array $options = array())
191+
{
192+
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
193+
$description = new ApplicationDescription($application, $describedNamespace);
194+
$blocks[] = 'Reference';
195+
$blocks[] = '=========';
196+
$blocks[] = '';
197+
198+
foreach ($description->getNamespaces() as $namespace) {
199+
if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
200+
$blocks[] = $namespace['id'];
201+
$blocks[] = str_repeat('-', strlen($namespace['id']));
202+
$blocks[] = '';
203+
}
204+
205+
$blocks[] = implode("\n", array_map(function ($commandName) {
206+
return '* :ref:`' . $this->getCommandRefName($commandName) . '`';
207+
} , $namespace['commands']));
208+
$blocks[] = '';
209+
}
210+
211+
foreach ($description->getCommands() as $command) {
212+
$blocks[] = $this->describeCommand($command);
213+
}
214+
215+
return implode("\n", $blocks);
216+
}
217+
}

0 commit comments

Comments
 (0)