[Enhancement] Easier auto-formatting for shell output (patch offered, awaiting feedback)
Reported by BrendonKoz | January 27th, 2010 @ 05:38 PM | in 1.3.0
Currently, the Shell class has some odd behaviors. There is a Shell::hr() method to create a horizontal rule, but it is hard coded with a static number of hyphen characters. Aside from this, if indentation is used to separate terms from their definitions (i.e.: cake acl help), currently it is up to the developer to hard code the line breaks and continuing indentations. I found this tedious and implemented changes to my own shell by overriding the hr() and out() methods. I thought I would share the code and see if it is something that the CakePHP developers may be interested in. To work properly, a convention should be followed (as I'm using string functions rather than REGEX to do the string manipulation). Those conventions simply are to not worry about newline indentation within the same string sent to a shell's $this->out method. If the first line has indentation, any newlines in the same string will continue to have the same indentation automatically.
Example of currently formatted output (notice the "help" command description):
help
Shows this help message. <-- 2 tabs Nunc mollis v
estibulum varius. Integer sodales mattis mauris lobortis pulvinar amet
Adjusted using my overloaded method:
help
Shows this help message. <-- 2 tabs Nunc mollis
vestibulum varius. Integer sodales mattis mauris lobortis
pulvinar amet
The overloaded methods:
/**
* Outputs to the stdout filehandle.
*
* @param string $string String to output.
* @param boolean $newline If true, the outputs gets an added newline.
* @access public
*/
function out($string, $newline = true){
if(!$this->silent){
//convert the array to a single string
if(is_array($string)){
$str = '';
foreach ($string as $message) {
$str .= $message ."\n";
}
$string = $str;
}
//if smart wrapping is enabled (default), modify our content
if($this->smartWrap && $string !== ''){
$matches = null;
preg_match('/(^(\n*?)(\t*))[^\t].*/s', $string, $matches);
$indentMultiplier = strlen($matches[3]); //the found tabs
$indent = $indentMultiplier * $this->spacesForTabs;
$indentText = str_repeat(' ', $indent);
$string = str_replace("\t", str_repeat(' ', $this->spacesForTabs), $string);
$string = str_replace("\n", "\n".$indentText, $string);
$string = wordwrap(ltrim($string), $this->lineLength - $indent, "\n$indentText");
$string = $indentText.$string;
}
return $this->Dispatch->stdout($string, $newline);
}
return false;
}</code>
/**
* Outputs a series of minus characters to the standard output, acts as a visual separator.
*
* @param boolean $newline If true, the outputs gets an added newline.
* @access public
*/
function hr($newline = false){
if ($newline) {
$this->out("\n");
}
$this->out(str_repeat('-', $this->lineLength));
if ($newline){
$this->out("\n");
}
}
New necessary properties to the class for configuration:
var $silent = false;
var $smartWrap = true;
var $spacesForTabs = 3;
var $lineLength = 63;
If this is something CakePHP developers would be interested in, I can supply a patch to /cake/console/libs/shell.php as well as all of the other shells/tasks to properly align with the convention of my modified Shell::out() method...or if any change requests are required I could work on that as well. I think this would severely assist with the readability of some of the shells (ACL comes to mind).
Comments and changes to this ticket
-
Mark Story January 28th, 2010 @ 08:44 AM
- → Milestone changed from to 1.3.0
- → Tag changed from "shell dispatcher output formatting" to enhancement formatting out shelldispatcher
Why do you convert tabs to spaces? Is it to get around the 8 space tabs in many cli setups? I could see a use for being able to indent sections of output though. What do you think of a change to out() that would allow automatic indentation based on parameters? So something like
$this->out($longText, true, array('indent' => 2));Resulting in the text block and any new lines in it being indented 2 tabs/equivalent spaces. I could see this being useful as well, and perhaps easier to use? -

BrendonKoz January 28th, 2010 @ 09:55 AM
At first I was using some REGEX trickery to keep any tabs that were beyond the first indentation. However, wordwrap() (nor my own code) could properly judge the actual length of a tab character to wrap at the right spot, and therefore the indentation did not occur at the proper spot. Assuming an 8 character length for tabs wouldn't work either as one could use setterm to set the actual tab length, and I've heard that not all OS will use an 8 character length for tabs. To fix this, all tabs had to be converted; at that point, there was little to no point to use REGEX except for determining the initial indent.
I like the proposed idea for a third array parameter, that would make extending it easier in the future as well. It would also protect backward compatibility, which is even better.
How do you feel about the additional class properties I mentioned (except for $smartWrap if we're to set that in the third param)? With regard to backwards compatibility I'd be concerned about $lineLength, but it might get cumbersome to set that each and every time in the call to Shell::out().
If all is well and good after that, I can get to work on a patch right away.
-

BrendonKoz January 28th, 2010 @ 09:57 AM
Also, with the limitation when dealing with tabs, I believe tabs would still have to be converted to spaces.
-
Mark Story January 30th, 2010 @ 09:16 AM
I think having lineLength as a property is good, and spacesForTabs also makes sense as a property. You could provide it as parameter as a temporary override. I don't know if auto line wrapping at a specific indent should occur, perhaps only auto line wrap when an
indentis provided even if its 0?
Please Sign in or create a free account to add a new ticket.
With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
