IndentShell.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. <?php
  2. //Configure::write('debug', 1);
  3. if (!defined('TB')) {
  4. define('TB', "\t");
  5. }
  6. if (!defined('NL')) {
  7. define('NL', "\n");
  8. }
  9. if (!defined('CR')) {
  10. define('CR', "\r");
  11. }
  12. App::uses('Folder', 'Utility');
  13. /**
  14. * Indend Shell
  15. *
  16. * @cakephp 2.0
  17. * @author Mark Scherer
  18. * @license MIT
  19. * 2011-11-04 ms
  20. */
  21. class IndentShell extends AppShell {
  22. protected $changes = null;
  23. public $settings = array(
  24. 'files' => array('php', 'ctp', 'inc', 'tpl'),
  25. 'spacesPerTab' => 4,
  26. 'againWithHalf' => true, # if 4, go again with 2 afterwards
  27. 'test' => false, # just count - without doing anything
  28. 'outputToTmp' => false, # write to filename_.ext
  29. 'debug' => false # add debug info after each line
  30. );
  31. protected $_paths = array();
  32. protected $_files = array();
  33. /**
  34. * Main execution function to indend a folder recursivly
  35. *
  36. * @return void
  37. */
  38. public function folder() {
  39. if (!empty($this->args)) {
  40. if (in_array('test', $this->args)) {
  41. $this->settings['test'] = true;
  42. }
  43. if (!empty($this->args[0]) && $this->args[0] != 'app') {
  44. $folder = realpath($this->args[0]);
  45. if (!file_exists($folder)) {
  46. die('folder not exists: ' . $folder . '');
  47. }
  48. $this->_paths[] = $folder;
  49. } elseif ($this->args[0] == 'app') {
  50. $this->_paths[] = APP;
  51. }
  52. if (!empty($this->params['files'])) {
  53. $this->settings['files'] = explode(',', $this->params['files']);
  54. }
  55. $this->_searchFiles();
  56. $this->out('found: ' . count($this->_files));
  57. if ($this->settings['test']) {
  58. $this->out('TEST DONE');
  59. } else {
  60. $this->_correctFiles3();
  61. $this->out('DONE');
  62. }
  63. } else {
  64. $this->out('Usage: cake intend folder');
  65. $this->out('"folder" is then intended recursivly');
  66. $this->out('default file types are');
  67. $this->out('['.implode(', ', $this->settings['files']).']');
  68. $this->out('');
  69. $this->out('Specify file types manually:');
  70. $this->out('-files php,js,css');
  71. }
  72. }
  73. public function getOptionParser() {
  74. $subcommandParser = array(
  75. 'options' => array(
  76. 'dry-run'=> array(
  77. 'short' => 'd',
  78. 'help' => __d('cake_console', 'Dry run the update, no files will actually be modified.'),
  79. 'boolean' => true
  80. ),
  81. 'log'=> array(
  82. 'short' => 'l',
  83. 'help' => __d('cake_console', 'Log all ouput to file log.txt in TMP dir'),
  84. 'boolean' => true
  85. ),
  86. 'interactive'=> array(
  87. 'short' => 'i',
  88. 'help' => __d('cake_console', 'Interactive'),
  89. 'boolean' => true
  90. ),
  91. )
  92. );
  93. return parent::getOptionParser()
  94. ->description(__d('cake_console', "Correct indentation of files"))
  95. ->addSubcommand('folder', array(
  96. 'help' => __d('cake_console', 'Indent all files in a folder'),
  97. 'parser' => $subcommandParser
  98. ));
  99. }
  100. public function _write($file, $text) {
  101. $text = implode(PHP_EOL, $text);
  102. if ($this->settings['outputToTmp']) {
  103. $filename = extractPathInfo('file', $file);
  104. if (mb_substr($filename, -1, 1) == '_') {
  105. return;
  106. }
  107. $file = extractPathInfo('dir', $file).DS.$filename.'_.'.extractPathInfo('ext', $file);
  108. }
  109. return file_put_contents($file, $text);
  110. }
  111. public function _read($file) {
  112. $text = file_get_contents($file);
  113. if (empty($text)) {
  114. return array();
  115. }
  116. $pieces = explode(NL, $text);
  117. return $pieces;
  118. }
  119. /**
  120. * NEW TRY!
  121. * idea: just count spaces and replace those
  122. *
  123. * 2010-09-12 ms
  124. */
  125. public function _correctFiles3() {
  126. foreach ($this->_files as $file) {
  127. $this->changes = false;
  128. $textCorrect = array();
  129. $pieces = $this->_read($file);
  130. foreach ($pieces as $piece) {
  131. $tmp = $this->_process($piece, $this->settings['spacesPerTab']);
  132. if ($this->settings['againWithHalf'] && ($spacesPerTab = $this->settings['spacesPerTab']) % 2 === 0 && $spacesPerTab > 3) {
  133. $tmp = $this->_process($tmp, $spacesPerTab/2);
  134. }
  135. $textCorrect[] = $tmp;
  136. }
  137. if ($this->changes) {
  138. $this->_write($file, $textCorrect);
  139. }
  140. }
  141. }
  142. public function _process($piece, $spacesPerTab) {
  143. $pos = -1;
  144. $spaces = $mod = $tabs = 0;
  145. $debug = '';
  146. $newPiece = $piece;
  147. //TODO
  148. while (mb_substr($piece, $pos+1, 1) === ' ' || mb_substr($piece, $pos+1, 1) === TB) {
  149. $pos++;
  150. }
  151. $piece1 = mb_substr($piece, 0, $pos+1);
  152. $piece1 = str_replace(str_repeat(' ', $spacesPerTab), TB, $piece1, $count);
  153. if ($count > 0) {
  154. $this->changes = true;
  155. }
  156. $piece2 = mb_substr($piece, $pos+1);
  157. $newPiece = $piece1 . $piece2;
  158. return rtrim($newPiece) . $debug;
  159. }
  160. /**
  161. * NEW TRY!
  162. * idea: hardcore replaceing
  163. *
  164. * 2010-09-12 ms
  165. */
  166. public function _correctFiles2() {
  167. foreach ($this->_files as $file) {
  168. $changes = false;
  169. $textCorrect = array();
  170. $pieces = $this->_read($file);
  171. foreach ($pieces as $piece) {
  172. $spaces = $mod = $tabs = 0;
  173. $debug = '';
  174. $newPiece = $piece;
  175. //TODO: make sure no other text is in front of it!!!
  176. $newPiece = str_replace(str_repeat(' ', $this->settings['spacesPerTab']), TB, $newPiece, $count);
  177. if ($count > 0) {
  178. $changes = true;
  179. }
  180. $textCorrect[] = rtrim($newPiece) . $debug;
  181. }
  182. if ($changes) {
  183. $this->_write($file, $textCorrect);
  184. }
  185. }
  186. }
  187. /**
  188. * Old try - sometimes TABS at the beginning are not recogized...
  189. * idea: strip tabs and spaces, remember their amount and add tabs again!
  190. * 2010-09-12 ms
  191. */
  192. public function _correctFiles() {
  193. foreach ($this->_files as $file) {
  194. $changes = false;
  195. $textCorrect = array();
  196. $pieces = $this->_read($file);
  197. foreach ($pieces as $piece) {
  198. $pos = -1;
  199. $spaces = $mod = $tabs = 0;
  200. $debug = '';
  201. $newPiece = trim($piece, CR);
  202. $newPiece = trim($newPiece, NL);
  203. //$debug .= ''.stripos($newPiece, TB);
  204. # detect tabs and whitespaces at the beginning
  205. //while (($pieceOfString = mb_substr($newPiece, 0, 1)) == ' ' || ($pieceOfString = mb_substr($newPiece, 0, 1)) == TB) {
  206. while ((stripos($newPiece, ' ')) === 0 || (stripos($newPiece, TB)) === 0) {
  207. $pieceOfString = mb_substr($newPiece, 0, 1);
  208. if ($pieceOfString === ' ') {
  209. $pos++;
  210. $spaces++;
  211. } elseif ($pieceOfString === TB) {
  212. $pos++;
  213. $spaces += $this->settings['spacesPerTab'];
  214. } else {
  215. die('???');
  216. }
  217. $newPiece = mb_substr($newPiece, 1);
  218. }
  219. if ($pos >= 1) {
  220. $changes = true;
  221. # if only spaces and tabs, we might as well trim the line
  222. //should be done
  223. # now correct
  224. //$newPiece = mb_substr($piece, $pos + 1);
  225. # clear single spaces
  226. /*
  227. if (mb_substr($newPiece, 0, 1) === ' ' && mb_substr($newPiece, 1, 1) !== '*') {
  228. $newPiece = mb_substr($newPiece, 1);
  229. }
  230. */
  231. $mod = $spaces % $this->settings['spacesPerTab'];
  232. $tabs = ($spaces - $mod) / $this->settings['spacesPerTab'];
  233. //$beginning = str_replace(' ', TB, $piece);
  234. $beginning = str_repeat(TB, $tabs);
  235. $beginning .= str_repeat(' ', $mod);
  236. $newPiece = $beginning . trim($newPiece);
  237. } else {
  238. $newPiece = rtrim($newPiece);
  239. }
  240. if ($this->settings['debug']) {
  241. $debug .= ' '. ($changes ? '[MOD]': '[]') .' (SPACES '.$tabs.', POS '.$pos.', TABS '.$tabs.', MOD '.$mod.')';
  242. }
  243. $textCorrect[] = $newPiece . $debug;
  244. }
  245. if ($changes) {
  246. $this->_write($file, $textCorrect);
  247. }
  248. //die();
  249. }
  250. }
  251. /**
  252. * Search files that may contain translateable strings
  253. *
  254. * @return void
  255. * @access private
  256. */
  257. public function _searchFiles() {
  258. foreach ($this->_paths as $path) {
  259. $Folder = new Folder($path);
  260. $files = $Folder->findRecursive('.*\.('.implode('|', $this->settings['files']).')', true);
  261. $this->_files += $files;
  262. }
  263. }
  264. }