diff --git a/src/Psy/Command/HelpCommand.php b/src/Psy/Command/HelpCommand.php
index 99c47a439..cecd35d96 100644
--- a/src/Psy/Command/HelpCommand.php
+++ b/src/Psy/Command/HelpCommand.php
@@ -11,6 +11,7 @@
namespace Psy\Command;
+use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -65,11 +66,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
// list available commands
$commands = $this->getApplication()->all();
- $width = 0;
- foreach ($commands as $command) {
- $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
- }
- $width += 2;
+ $table = $this->getApplication()->getHelperSet()->get('table')
+ ->setLayout(TableHelper::LAYOUT_BORDERLESS)
+ ->setHorizontalBorderChar('')
+ ->setCrossingChar('');
foreach ($commands as $name => $command) {
if ($name !== $command->getName()) {
@@ -77,15 +77,21 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
if ($command->getAliases()) {
- $aliases = sprintf(' Aliases: %s', implode(', ', $command->getAliases()));
+ $aliases = sprintf('Aliases: %s', implode(', ', $command->getAliases()));
} else {
$aliases = '';
}
- $messages[] = sprintf(" %-${width}s %s%s", $name, $command->getDescription(), $aliases);
+ $table->addRow(array(
+ sprintf('%s', $name),
+ $command->getDescription(),
+ $aliases,
+ ));
}
- $output->page($messages);
+ $output->page(function($output) use ($table) {
+ $table->render($output);
+ });
}
}
}
diff --git a/src/Psy/Command/HistoryCommand.php b/src/Psy/Command/HistoryCommand.php
index 66e8564a2..71b4d4bd3 100644
--- a/src/Psy/Command/HistoryCommand.php
+++ b/src/Psy/Command/HistoryCommand.php
@@ -59,10 +59,14 @@ protected function configure()
new InputOption('clear', '', InputOption::VALUE_NONE, 'Clear the history.')
))
->setDescription('Show the Psy Shell history.')
-
- // TODO: help!
->setHelp(<<>>> history --grep /[bB]acon/
+>>> history --show 0..10 --replay
+>>> history --clear
+>>> history --tail 1000 --save somefile.txt
HELP
);
}
diff --git a/src/Psy/Command/ListCommand.php b/src/Psy/Command/ListCommand.php
index 18306649a..ef9f5f521 100644
--- a/src/Psy/Command/ListCommand.php
+++ b/src/Psy/Command/ListCommand.php
@@ -29,6 +29,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Helper\TableHelper;
/**
* List available local variables, object properties, etc.
@@ -119,9 +120,18 @@ protected function execute(InputInterface $input, OutputInterface $output)
$reflector = null;
}
+ // TODO: something cleaner than this :-/
+ if ($input->getOption('long')) {
+ $output->startPaging();
+ }
+
foreach ($this->enumerators as $enumerator) {
$this->$method($output, $enumerator->enumerate($input, $reflector, $target));
}
+
+ if ($input->getOption('long')) {
+ $output->stopPaging();
+ }
}
/**
@@ -175,15 +185,18 @@ protected function writeLong(OutputInterface $output, array $result = null)
{
if ($result === null) return;
+ $table = $this->getTable();
+
foreach ($result as $label => $items) {
$output->writeln('');
$output->writeln(sprintf('%s:', $label));
- $pad = max(array_map('strlen', array_keys($items)));
+ $table->setRows(array());
foreach ($items as $item) {
- $itemPad = $pad + (2 * strlen($item['style'])) + 5;
- $output->writeln(sprintf(" %-${itemPad}s %s", $this->formatItemName($item), $item['value']));
+ $table->addRow(array($this->formatItemName($item), $item['value']));
}
+
+ $table->render($output);
}
}
@@ -256,4 +269,17 @@ private function validateInput(InputInterface $input)
$input->setOption('methods', true);
}
}
+
+ /**
+ * Get a TableHelper instance.
+ *
+ * @return TableHelper
+ */
+ private function getTable()
+ {
+ return $this->getApplication()->getHelperSet()->get('table')
+ ->setLayout(TableHelper::LAYOUT_BORDERLESS)
+ ->setHorizontalBorderChar('')
+ ->setCrossingChar('');
+ }
}
diff --git a/src/Psy/Command/ShowCommand.php b/src/Psy/Command/ShowCommand.php
index 037805ed8..3f840077d 100644
--- a/src/Psy/Command/ShowCommand.php
+++ b/src/Psy/Command/ShowCommand.php
@@ -34,10 +34,12 @@ protected function configure()
new InputArgument('value', InputArgument::REQUIRED, 'Function, class, instance, constant, method or property to show.'),
))
->setDescription('Show the code for an object, class, constant, method or property.')
-
- // TODO: help!
->setHelp(<<>>> show \$myObject
+>>> show Psy\Shell::debug
HELP
);
}
diff --git a/src/Psy/Command/TraceCommand.php b/src/Psy/Command/TraceCommand.php
index 0e6cb8fe3..e209832db 100644
--- a/src/Psy/Command/TraceCommand.php
+++ b/src/Psy/Command/TraceCommand.php
@@ -15,6 +15,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Formatter\OutputFormatter;
/**
* Show the current stack trace.
@@ -68,6 +69,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
*/
protected function getBacktrace(\Exception $e, $count = null, $includePsy = true)
{
+ if ($cwd = getcwd()) {
+ $cwd = rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+ }
+
if ($count === null) {
$count = PHP_INT_MAX;
}
@@ -96,12 +101,36 @@ protected function getBacktrace(\Exception $e, $count = null, $includePsy = true
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$function = $trace[$i]['function'];
- $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
+ $file = isset($trace[$i]['file']) ? $this->replaceCwd($cwd, $trace[$i]['file']) : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
- $lines[] = sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line);
+ $lines[] = sprintf(
+ ' %s%s%s() at %s:%s',
+ OutputFormatter::escape($class),
+ OutputFormatter::escape($type),
+ OutputFormatter::escape($function),
+ OutputFormatter::escape($file),
+ OutputFormatter::escape($line)
+ );
}
return $lines;
}
+
+ /**
+ * Replace the given directory from the start of a filepath.
+ *
+ * @param string $cwd
+ * @param string $file
+ *
+ * @return string
+ */
+ private function replaceCwd($cwd, $file)
+ {
+ if ($cwd === false) {
+ return $file;
+ } else {
+ return preg_replace('/^'.preg_quote($cwd, '/').'/', '', $file);
+ }
+ }
}
diff --git a/src/Psy/Command/WtfCommand.php b/src/Psy/Command/WtfCommand.php
index 9da2466cb..efa6135ea 100644
--- a/src/Psy/Command/WtfCommand.php
+++ b/src/Psy/Command/WtfCommand.php
@@ -50,7 +50,7 @@ protected function configure()
->setName('wtf')
->setAliases(array('last-exception', 'wtf?'))
->setDefinition(array(
- new InputArgument('incredulity', InputArgument::OPTIONAL, 'Number of lines to show'),
+ new InputArgument('incredulity', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Number of lines to show'),
new InputOption('verbose', 'v', InputOption::VALUE_NONE, 'Show entire backtrace.'),
))
->setDescription('Show the backtrace of the most recent exception.')
@@ -89,12 +89,20 @@ protected function getHiddenOptions()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
- $incredulity = $input->getArgument('incredulity');
+ $incredulity = implode('', $input->getArgument('incredulity'));
if (strlen(preg_replace('/[\\?!]/', '', $incredulity))) {
throw new \InvalidArgumentException('Incredulity must include only "?" and "!".');
}
- $count = $input->getOption('verbose') ? PHP_INT_MAX : pow(2, max(0, (strlen($incredulity) - 1)));
- $output->page($this->getBacktrace($this->context->getLastException(), $count), ShellOutput::NUMBER_LINES | ShellOutput::OUTPUT_RAW);
+ $exception = $this->context->getLastException();
+ $count = $input->getOption('verbose') ? PHP_INT_MAX : pow(2, max(0, (strlen($incredulity) - 1)));
+ $trace = $this->getBacktrace($exception, $count);
+
+ $shell = $this->getApplication();
+ $output->page(function($output) use ($exception, $trace, $shell) {
+ $shell->renderException($exception, $output);
+ $output->writeln('--');
+ $output->write($trace, true, ShellOutput::NUMBER_LINES);
+ });
}
}
diff --git a/src/Psy/Output/ShellOutput.php b/src/Psy/Output/ShellOutput.php
index 434273882..f54b07ce3 100644
--- a/src/Psy/Output/ShellOutput.php
+++ b/src/Psy/Output/ShellOutput.php
@@ -72,7 +72,7 @@ public function page($messages, $type = 0)
throw new \InvalidArgumentException('Paged output requires a string, array or callback.');
}
- $this->paging++;
+ $this->startPaging();
if (is_callable($messages)) {
$messages($this);
@@ -80,6 +80,22 @@ public function page($messages, $type = 0)
$this->write($messages, true, $type);
}
+ $this->stopPaging();
+ }
+
+ /**
+ * Start sending output to the output pager.
+ */
+ public function startPaging()
+ {
+ $this->paging++;
+ }
+
+ /**
+ * Stop paging output and flush the output pager.
+ */
+ public function stopPaging()
+ {
$this->paging--;
$this->closePager();
}
@@ -106,7 +122,7 @@ public function write($messages, $newline = false, $type = 0)
if ($type & self::NUMBER_LINES) {
$pad = strlen((string) count($messages));
- $template = $this->isDecorated() ? ": %s" : "%-{$pad}s: %s";
+ $template = $this->isDecorated() ? ": %s" : "%{$pad}s: %s";
if ($type & self::OUTPUT_RAW) {
$messages = array_map(array('Symfony\Component\Console\Formatter\OutputFormatter', 'escape'), $messages);
diff --git a/src/Psy/Shell.php b/src/Psy/Shell.php
index ed8c0fdb9..64ae2c2a5 100644
--- a/src/Psy/Shell.php
+++ b/src/Psy/Shell.php
@@ -39,7 +39,7 @@
*/
class Shell extends Application
{
- const VERSION = 'v0.1.5';
+ const VERSION = 'v0.1.6';
const PROMPT = '>>> ';
const BUFF_PROMPT = '... ';
@@ -69,7 +69,7 @@ public function __construct(Configuration $config = null)
$this->cleaner = $this->config->getCodeCleaner();
$this->loop = $this->config->getLoop();
$this->context = new Context;
- $this->includes = $this->config->getDefaultIncludes();
+ $this->includes = array();
$this->readline = $this->config->getReadline();
parent::__construct('Psy Shell', self::VERSION);
@@ -369,7 +369,7 @@ public function setIncludes(array $includes = array())
*/
public function getIncludes()
{
- return $this->includes;
+ return array_merge($this->config->getDefaultIncludes(), $this->includes);
}
/**
diff --git a/test/Psy/Test/ConfigurationTest.php b/test/Psy/Test/ConfigurationTest.php
index a89719820..87f5d17fb 100644
--- a/test/Psy/Test/ConfigurationTest.php
+++ b/test/Psy/Test/ConfigurationTest.php
@@ -108,4 +108,16 @@ private function joinPath()
{
return implode(DIRECTORY_SEPARATOR, func_get_args());
}
+
+ public function testConfigIncludes()
+ {
+ $config = new Configuration(array(
+ 'defaultIncludes' => array('/file.php'),
+ 'configFile' => '(ignore user config)'
+ ));
+
+ $includes = $config->getDefaultIncludes();
+ $this->assertCount(1, $includes);
+ $this->assertEquals('/file.php', $includes[0]);
+ }
}
diff --git a/test/Psy/Test/ShellTest.php b/test/Psy/Test/ShellTest.php
index 52c9fc4d8..4fc14456f 100644
--- a/test/Psy/Test/ShellTest.php
+++ b/test/Psy/Test/ShellTest.php
@@ -14,6 +14,7 @@
use Psy\Exception\ErrorException;
use Psy\Exception\ParseErrorException;
use Psy\Shell;
+use Psy\Configuration;
use Symfony\Component\Console\Output\StreamOutput;
class ShellTest extends \PHPUnit_Framework_TestCase
@@ -62,12 +63,27 @@ public function testUnknownScopeVariablesThrowExceptions()
public function testIncludes()
{
- $shell = new Shell;
+ $config = new Configuration(array('configFile' => '(ignore user config)'));
+
+ $shell = new Shell($config);
$this->assertEmpty($shell->getIncludes());
$shell->setIncludes(array('foo', 'bar', 'baz'));
$this->assertEquals(array('foo', 'bar', 'baz'), $shell->getIncludes());
}
+ public function testIncludesConfig()
+ {
+ $config = new Configuration(array(
+ 'defaultIncludes' => array('/file.php'),
+ 'configFile' => '(ignore user config)'
+ ));
+
+ $shell = new Shell($config);
+
+ $includes = $shell->getIncludes();
+ $this->assertEquals('/file.php', $includes[0]);
+ }
+
public function testRenderingExceptions()
{
$shell = new Shell;