1: <?php
2: namespace Pharborist;
3:
4: use Pharborist\Constants\ConstantNode;
5: use Pharborist\ControlStructures\CaseNode;
6: use Pharborist\ControlStructures\DefaultNode;
7: use Pharborist\ControlStructures\DoWhileNode;
8: use Pharborist\ControlStructures\ElseIfNode;
9: use Pharborist\ControlStructures\ForeachNode;
10: use Pharborist\ControlStructures\ForNode;
11: use Pharborist\ControlStructures\IfNode;
12: use Pharborist\ControlStructures\SwitchNode;
13: use Pharborist\ControlStructures\WhileNode;
14: use Pharborist\Exceptions\CatchNode;
15: use Pharborist\Functions\CallNode;
16: use Pharborist\Functions\FunctionDeclarationNode;
17: use Pharborist\Functions\ParameterNode;
18: use Pharborist\Namespaces\NamespaceNode;
19: use Pharborist\Objects\ClassMethodNode;
20: use Pharborist\Objects\InterfaceMethodNode;
21: use Pharborist\Objects\InterfaceNode;
22: use Pharborist\Objects\NewNode;
23: use Pharborist\Objects\ObjectMethodCallNode;
24: use Pharborist\Objects\SingleInheritanceNode;
25: use Pharborist\Operators\BinaryOperationNode;
26: use Pharborist\Operators\CastNode;
27: use Pharborist\Operators\CloneNode;
28: use Pharborist\Operators\PostDecrementNode;
29: use Pharborist\Operators\PostIncrementNode;
30: use Pharborist\Operators\PrintNode;
31: use Pharborist\Operators\UnaryOperationNode;
32: use Pharborist\Types\ArrayNode;
33: use Pharborist\Types\BooleanNode;
34: use Pharborist\Types\NullNode;
35:
36: class Formatter extends VisitorBase {
37: 38: 39: 40: 41:
42: protected $config;
43:
44: 45: 46: 47: 48:
49: protected $indentLevel = 0;
50:
51: 52: 53: 54: 55:
56: protected $nodeData;
57:
58: public function __construct($config = []) {
59: $this->nodeData = new \SplObjectStorage();
60: $this->config = $config + [
61: 'nl' => "\n",
62: 'indent' => 2,
63: 'soft_limit' => 80,
64: 'boolean_null_upper' => TRUE,
65: 'force_array_new_style' => TRUE,
66: 'else_newline' => TRUE,
67: 'declaration_brace_newline' => FALSE,
68: 'list_keep_wrap' => FALSE,
69: 'list_wrap_if_long' => FALSE,
70: 'blank_lines_around_class_body' => 1,
71: ];
72: }
73:
74: 75: 76:
77: public function format(Node $node) {
78: if ($node instanceof ParentNode) {
79: $node->acceptVisitor($this);
80: }
81: else {
82: $this->visit($node);
83: }
84: }
85:
86: 87: 88: 89: 90: 91: 92: 93:
94: public function getConfig($key) {
95: return $this->config[$key];
96: }
97:
98: 99: 100: 101: 102:
103: protected function getIndent($close = FALSE) {
104: $indent_per_level = str_repeat(' ', $this->config['indent']);
105: return str_repeat($indent_per_level, $this->indentLevel - ($close ? 1 : 0));
106: }
107:
108: 109: 110: 111: 112: 113:
114: protected function getNewlineIndent($wsNode = NULL, $close = FALSE) {
115: $indent = $this->getIndent($close);
116: $nl_count = $wsNode ? $wsNode->getNewlineCount() : 1;
117: $nl_count = max($nl_count, 1);
118: return str_repeat($this->config['nl'], $nl_count) . $indent;
119: }
120:
121: 122: 123: 124: 125: 126:
127: protected function spaceBefore(Node $node) {
128: $prev = $node->previousToken();
129: if ($prev instanceof WhitespaceNode) {
130: $prev->setText(' ');
131: }
132: else {
133: $node->before(Token::space());
134: }
135: }
136:
137: 138: 139: 140: 141: 142:
143: protected function spaceAfter(Node $node) {
144: $next = $node->nextToken();
145: if ($next instanceof WhitespaceNode) {
146: $next->setText(' ');
147: }
148: else {
149: $node->after(Token::space());
150: }
151: }
152:
153: 154: 155: 156: 157: 158:
159: protected function removeSpaceBefore(Node $node) {
160: $prev = $node->previousToken();
161: if ($prev instanceof WhitespaceNode) {
162: $prev->remove();
163: }
164: }
165:
166: 167: 168: 169: 170: 171:
172: protected function removeSpaceAfter(Node $node) {
173: $next = $node->nextToken();
174: if ($next instanceof WhitespaceNode) {
175: $next->remove();
176: }
177: }
178:
179: 180: 181: 182: 183: 184: 185: 186:
187: protected function newlineBefore(Node $node, $close = FALSE) {
188: $prev = $node->previousToken();
189: if ($prev instanceof WhitespaceNode) {
190: $prev_ws = $prev->previousToken();
191: if ($prev_ws instanceof CommentNode && $prev_ws->isLineComment() && $prev->getNewlineCount() === 0) {
192: $prev->setText($this->getIndent($close));
193: }
194: else {
195: $prev->setText($this->getNewlineIndent($prev, $close));
196: }
197: }
198: else {
199: if ($prev instanceof CommentNode && $prev->isLineComment()) {
200: if ($this->indentLevel > 0) {
201: $node->before(Token::whitespace($this->getIndent($close)));
202: }
203: }
204: else {
205: $node->before(Token::whitespace($this->getNewlineIndent(NULL, $close)));
206: }
207: }
208: }
209:
210: 211: 212: 213: 214: 215:
216: protected function newlineAfter(Node $node) {
217: $next = $node->nextToken();
218: if ($next instanceof WhitespaceNode) {
219: $next->setText($this->getNewlineIndent($next));
220: }
221: else {
222: $node->after(Token::whitespace($this->getNewlineIndent()));
223: }
224: }
225:
226: public function visitStatementNode(StatementNode $node) {
227: $this->indentLevel++;
228: }
229:
230: public function endStatementNode(StatementNode $node) {
231: $this->indentLevel--;
232: }
233:
234: public function visitBinaryOperationNode(BinaryOperationNode $node) {
235:
236: $operator = $node->getOperator();
237: $this->spaceBefore($operator);
238:
239: $this->spaceAfter($operator);
240: }
241:
242: public function visitUnaryOperationNode(UnaryOperationNode $node) {
243: $operator = $node->getOperator();
244: if ($node instanceof PostDecrementNode || $node instanceof PostIncrementNode) {
245: $this->removeSpaceBefore($operator);
246: }
247: elseif ($node instanceof CastNode || $node instanceof CloneNode || $node instanceof PrintNode) {
248: $this->spaceAfter($operator);
249: }
250: else {
251: $this->removeSpaceAfter($operator);
252: }
253: }
254:
255: public function visitDocCommentNode(DocCommentNode $node) {
256: $this->removeSpaceAfter($node);
257: if ($this->indentLevel > 0) {
258: $this->indentLevel--;
259: $this->newlineAfter($node);
260: $this->indentLevel++;
261: }
262: else {
263: $this->newlineAfter($node);
264: }
265: }
266:
267: public function visitWhitespaceNode(WhitespaceNode $node) {
268: if ($node->previousToken()->getType() === T_DOC_COMMENT) {
269: return;
270: }
271:
272:
273: $nl_count = $node->getNewlineCount();
274: if ($nl_count > 0) {
275: $node->setText($this->getNewlineIndent($node));
276: }
277: else {
278: $prev = $node->previousToken();
279: if ($prev instanceof CommentNode && $prev->isLineComment()) {
280:
281: }
282: else {
283: $node->setText(' ');
284: }
285: }
286: }
287:
288: public function visitTokenNode(TokenNode $node) {
289: switch ($node->getType()) {
290: case T_DOUBLE_ARROW:
291: $this->spaceBefore($node);
292: $this->spaceAfter($node);
293: break;
294: }
295: }
296:
297: 298: 299: 300: 301: 302:
303: protected function handleBuiltinConstantNode(ConstantNode $node) {
304: $to_upper = $this->config['boolean_null_upper'];
305: if ($to_upper) {
306: $node->toUpperCase();
307: }
308: else {
309: $node->toLowerCase();
310: }
311: }
312:
313: public function visitBooleanNode(BooleanNode $node) {
314: $this->handleBuiltinConstantNode($node);
315: }
316:
317: public function visitNullNode(NullNode $node) {
318: $this->handleBuiltinConstantNode($node);
319: }
320:
321: 322: 323: 324: 325:
326: protected function encloseBlock($node) {
327: if ($node && !($node instanceof StatementBlockNode)) {
328: $blockNode = new StatementBlockNode();
329: $blockNode->append([Token::openBrace(), clone $node, Token::closeBrace()]);
330: $node->replaceWith($blockNode);
331: }
332: }
333:
334: 335: 336: 337: 338:
339: protected function handleParens($node) {
340: $open_paren = $node->getOpenParen();
341: $this->removeSpaceAfter($open_paren);
342: $this->spaceBefore($open_paren);
343: $close_paren = $node->getCloseParen();
344: $this->removeSpaceBefore($close_paren);
345: }
346:
347: 348: 349: 350: 351:
352: protected function handleControlStructure($node) {
353: $this->handleParens($node);
354: $colons = $node->children(Filter::isTokenType(':'));
355: foreach ($colons as $colon) {
356: $this->removeSpaceBefore($colon);
357: }
358: if ($colons->isNotEmpty()) {
359: $this->newlineBefore($node->lastChild()->previous());
360: }
361: }
362:
363: public function visitIfNode(IfNode $node) {
364: $this->encloseBlock($node->getThen());
365: $this->encloseBlock($node->getElse());
366: }
367:
368: public function endIfNode(IfNode $node) {
369: $this->handleControlStructure($node);
370: if ($node->getElse()) {
371: $elseKeyword = $node->getElseKeyword();
372: $else_newline = $this->config['else_newline'];
373: if ($node->isAlterativeSyntax() || $else_newline) {
374: $this->newlineBefore($elseKeyword);
375: }
376: else {
377: $this->spaceBefore($elseKeyword);
378: }
379: }
380: }
381:
382: public function visitElseIfNode(ElseIfNode $node) {
383: $this->handleParens($node);
384: $this->encloseBlock($node->getThen());
385: $else_newline = $this->config['else_newline'];
386: if ($node->getOpenColon() || $else_newline) {
387: $this->newlineBefore($node, TRUE);
388: }
389: else {
390: $this->spaceBefore($node);
391: }
392: if ($colon = $node->getOpenColon()) {
393: $this->removeSpaceBefore($colon);
394: }
395: }
396:
397: public function visitWhileNode(WhileNode $node) {
398: $this->encloseBlock($node->getBody());
399: }
400:
401: public function endWhileNode(WhileNode $node) {
402: $this->handleControlStructure($node);
403: }
404:
405: public function visitDoWhileNode(DoWhileNode $node) {
406: $this->handleParens($node);
407: $this->encloseBlock($node->getBody());
408: $this->spaceBefore($node->getWhileKeyword());
409: }
410:
411: public function visitForNode(ForNode $node) {
412: $this->encloseBlock($node->getBody());
413: foreach ($node->children(Filter::isTokenType(';')) as $semicolon) {
414: $this->removeSpaceBefore($semicolon);
415: $this->spaceAfter($semicolon);
416: }
417: }
418:
419: public function endForNode(ForNode $node) {
420: $this->handleControlStructure($node);
421: }
422:
423: public function visitForeachNode(ForeachNode $node) {
424: $this->encloseBlock($node->getBody());
425: }
426:
427: public function endForeachNode(ForeachNode $node) {
428: $this->handleControlStructure($node);
429: }
430:
431: public function endSwitchNode(SwitchNode $node) {
432: $this->handleControlStructure($node);
433:
434:
435: $token = $node->getSwitchOn()->nextUntil(Filter::isTokenType(':', '{'), TRUE)->last()->get(0);
436: if ($token->getType() === ':') {
437: $this->removeSpaceBefore($token);
438: }
439: else {
440: $this->spaceBefore($token);
441: }
442:
443: $last = $node->lastChild();
444: if ($last instanceof TokenNode && $last->getType() === '}') {
445: $this->newlineBefore($last);
446: }
447: }
448:
449: public function endCaseNode(CaseNode $node) {
450: $this->newlineBefore($node);
451: }
452:
453: public function endDefaultNode(DefaultNode $node) {
454: $this->newlineBefore($node);
455: }
456:
457: 458: 459: 460: 461: 462: 463: 464: 465:
466: protected function isDeclaration(ParentNode $node) {
467: return $node instanceof FunctionDeclarationNode ||
468: $node instanceof SingleInheritanceNode ||
469: $node instanceof InterfaceNode ||
470: $node instanceof ClassMethodNode ||
471: $node instanceof InterfaceMethodNode;
472: }
473:
474: public function visitStatementBlockNode(StatementBlockNode $node) {
475: $nested = FALSE;
476: $first = $node->firstChild();
477: if ($first instanceof TokenNode && $first->getType() === '{') {
478: if ($node->parent() instanceof StatementBlockNode) {
479: $this->indentLevel++;
480: $nested = TRUE;
481: }
482:
483: $brace_newline = $this->config['declaration_brace_newline'];
484: if ($brace_newline && $this->isDeclaration($node->parent())) {
485: $this->newlineBefore($node, TRUE);
486: }
487: else {
488: $this->spaceBefore($node);
489: }
490: $this->newlineAfter($first);
491: }
492: $this->nodeData[$node] = $nested;
493:
494: foreach ($node->getStatements() as $statement) {
495: $this->newlineBefore($statement);
496: }
497: }
498:
499: public function endStatementBlockNode(StatementBlockNode $node) {
500: $nested = $this->nodeData[$node];
501: unset($this->nodeData[$node]);
502: if ($nested) {
503: $this->indentLevel--;
504: }
505: $last = $node->lastChild();
506: if ($last instanceof TokenNode && $last->getType() === '}') {
507: $this->newlineBefore($last, TRUE);
508: }
509: }
510:
511: public function visitLineCommentBlockNode(LineCommentBlockNode $node) {
512: if ($this->indentLevel > 0) {
513: $indent = $this->getIndent();
514: foreach ($node->children(Filter::isInstanceOf('\Pharborist\CommentNode'))->slice(1) as $line_comment) {
515: $prev = $line_comment->previous();
516: if ($prev instanceof WhitespaceNode) {
517: $prev->setText($indent);
518: }
519: else {
520: $line_comment->before(Token::whitespace($indent));
521: }
522: }
523: }
524: else {
525: $node->children(Filter::isInstanceOf('\Pharborist\WhitespaceNode'))->remove();
526: }
527: }
528:
529: 530: 531: 532: 533: 534: 535: 536: 537:
538: protected function calculateColumnPosition(Node $node) {
539:
540: $column_position = 1;
541: $start_token = $node instanceof ParentNode ? $node->firstToken() : $node;
542: $token = $start_token;
543: while ($token = $token->previousToken()) {
544: if ($token instanceof WhitespaceNode && $token->getNewlineCount() > 0) {
545: $lines = explode($this->config['nl'], $token->getText());
546: $last_line = end($lines);
547: $column_position += strlen($last_line);
548: break;
549: }
550: $column_position += strlen($token->getText());
551: }
552: return $column_position;
553: }
554:
555: public function visitArrayNode(ArrayNode $node) {
556: $nested = FALSE;
557: if ($node->parents(Filter::isInstanceOf('\Pharborist\Types\ArrayNode'))->isNotEmpty()) {
558: $this->indentLevel++;
559: $nested = TRUE;
560: }
561:
562: if ($this->config['force_array_new_style']) {
563: $first = $node->firstChild();
564:
565: if ($first->getType() === T_ARRAY) {
566: $open_paren = $first->nextUntil(Filter::isTokenType('('), TRUE)->last()->get(0);
567: $open_paren->previousAll()->remove();
568: $open_paren->replaceWith(Token::openBracket());
569: $close_paren = $node->lastChild();
570: $close_paren->replaceWith(Token::closeBracket());
571: }
572: }
573:
574:
575: $first = $node->firstChild();
576:
577: if ($first->getType() === T_ARRAY) {
578: $this->removeSpaceAfter($first);
579: }
580:
581:
582: $this->removeSpaceBefore($node->getElementList());
583:
584:
585: $this->removeSpaceAfter($node->getElementList());
586:
587:
588: $last = $node->getElementList()->lastChild();
589: if ($last instanceof TokenNode && $last->getType() === ',') {
590: $last->remove();
591: }
592:
593: $this->nodeData[$node] = $nested;
594: }
595:
596: public function endArrayNode(ArrayNode $node) {
597: $nested = $this->nodeData[$node];
598: unset($this->nodeData[$node]);
599:
600: if ($nested) {
601: $this->indentLevel--;
602: }
603:
604: $is_wrapped = $node->getElementList()->children(Filter::isNewline())->isNotEmpty();
605: if ($is_wrapped) {
606:
607: $node->getElementList()->append(Token::comma());
608:
609:
610: $this->newlineBefore($node->lastChild(), !$nested);
611: }
612: }
613:
614: public function visitCommaListNode(CommaListNode $node) {
615: if ($node->isEmpty()) {
616: return;
617: }
618: $keep_wrap = $this->config['list_keep_wrap'];
619: if (!$keep_wrap) {
620: $keep_wrap = $node->parent() instanceof ArrayNode;
621: }
622: if ($keep_wrap) {
623: $has_wrap = $node->children(Filter::isNewline())->isNotEmpty();
624: $this->nodeData[$node] = $has_wrap;
625: }
626: foreach ($node->children(Filter::isTokenType(',')) as $comma_node) {
627: $this->removeSpaceBefore($comma_node);
628: $this->spaceAfter($comma_node);
629: }
630: }
631:
632: public function endCommaListNode(CommaListNode $node) {
633: if ($node->isEmpty()) {
634: return;
635: }
636: $keep_wrap = $this->config['list_keep_wrap'];
637: $wrap_if_long = $this->config['list_wrap_if_long'];
638: if ($node->parent() instanceof ArrayNode) {
639: $keep_wrap = TRUE;
640: $wrap_if_long = TRUE;
641: }
642: $wrap_list = FALSE;
643: if ($keep_wrap) {
644: $wrap_list = $this->nodeData[$node];
645: unset($this->nodeData[$node]);
646: }
647: if (!$wrap_list && $wrap_if_long) {
648: $column_position = $this->calculateColumnPosition($node);
649: $column_position += strlen($node->getText());
650: $soft_limit = $this->config['soft_limit'];
651: $wrap_list = $column_position > $soft_limit;
652: }
653: if ($wrap_list) {
654: $this->newlineBefore($node);
655: foreach ($node->children(Filter::isTokenType(',')) as $comma_node) {
656: $this->newlineAfter($comma_node);
657: }
658: $this->newlineAfter($node, TRUE);
659: }
660: }
661:
662: 663: 664:
665: protected function handleParameters($node) {
666: $parameter_list = $node->getParameterList();
667: $this->removeSpaceBefore($parameter_list);
668: $this->removeSpaceAfter($parameter_list);
669: $this->removeSpaceBefore($node->getOpenParen());
670: }
671:
672: public function visitFunctionDeclarationNode(FunctionDeclarationNode $node) {
673: $this->handleParameters($node);
674: }
675:
676: 677: 678:
679: protected function handleParameterWrapping($node) {
680: $parameter_list = $node->getParameterList();
681: $parameter_wrapped = $parameter_list->children(Filter::isNewline())->isNotEmpty();
682: if ($parameter_wrapped) {
683: $this->newlineAfter($parameter_list);
684: if (!($node instanceof InterfaceMethodNode) && $node->getBody()) {
685: $this->spaceBefore($node->getBody());
686: }
687: }
688: }
689:
690: public function endFunctionDeclarationNode(FunctionDeclarationNode $node) {
691: $this->handleParameterWrapping($node);
692: }
693:
694: public function visitParameterNode(ParameterNode $node) {
695: if ($node->getValue()) {
696: $assign = $node->getValue()->previousUntil(Filter::isTokenType('='), TRUE)->get(0);
697: $this->spaceBefore($assign);
698: $this->spaceAfter($assign);
699: }
700: }
701:
702: public function visitCallNode(CallNode $node) {
703: $arg_list = $node->getArgumentList();
704: $this->removeSpaceBefore($arg_list);
705: $this->removeSpaceAfter($arg_list);
706: $this->removeSpaceBefore($node->getOpenParen());
707: }
708:
709: 710: 711:
712: protected function endClassTraitOrInterface($node) {
713: $nl = $this->config['nl'];
714: $indent = str_repeat(' ', $this->config['indent']);
715: $indent = str_repeat($indent, $this->indentLevel + 1);
716:
717: $whitespace = $node->getBody()->children(Filter::isInstanceOf('\Pharborist\WhitespaceNode'));
718: foreach ($whitespace->slice(1, -1) as $ws_node) {
719:
720: $ws_node->setText(str_repeat($nl, 2) . $indent);
721: }
722:
723: if ($whitespace->count() === 1) {
724: return;
725: }
726:
727: $blank_lines_around_class_body = $this->config['blank_lines_around_class_body'];
728: $nl_count = $blank_lines_around_class_body + 1;
729:
730:
731: $open_whitespace = $whitespace->get(0);
732: $open_whitespace->setText(str_repeat($nl, $nl_count) . $indent);
733:
734:
735: $close_whitespace = $whitespace->last()->get(0);
736: $indent = str_repeat($indent, $this->indentLevel);
737: $close_whitespace->setText(str_repeat($nl, $nl_count) . $indent);
738: }
739:
740: public function endSingleInheritanceNode(SingleInheritanceNode $node) {
741: $this->endClassTraitOrInterface($node);
742: }
743:
744: 745: 746:
747: protected function visitMethod($node) {
748: $this->handleParameters($node);
749:
750: if ($node->getVisibility() === NULL) {
751: $node->setVisibility('public');
752: }
753: if ($node->getStatic()) {
754:
755: $next = $node->getStatic()->nextUntil(Filter::isNotHidden(), TRUE)->last()->get(0);
756: if ($next->getType() !== T_FUNCTION) {
757: $node->getStatic()->swapWith($node->getVisibility());
758: }
759: }
760: }
761:
762: public function visitClassMethodNode(ClassMethodNode $node) {
763: if ($node->getBody()) {
764: $close_brace = $node->getBody()->lastChild();
765: $this->newlineBefore($close_brace);
766: }
767: $this->visitMethod($node);
768: if ($node->getAbstract() && $node->firstChild() !== $node->getAbstract()) {
769: $node->getAbstract()->swapWith($node->getVisibility());
770: }
771: if ($node->getFinal() && $node->firstChild() !== $node->getFinal()) {
772: $node->getFinal()->swapWith($node->getVisibility());
773: }
774: }
775:
776: public function endClassMethodNode(ClassMethodNode $node) {
777: $this->handleParameterWrapping($node);
778: }
779:
780: public function endInterfaceNode(InterfaceNode $node) {
781: $this->endClassTraitOrInterface($node);
782: }
783:
784: public function visitInterfaceMethodNode(InterfaceMethodNode $node) {
785: $this->visitMethod($node);
786: }
787:
788: public function endInterfaceMethodNode(InterfaceMethodNode $node) {
789: $this->handleParameterWrapping($node);
790: }
791:
792: public function endCatchNode(CatchNode $node) {
793: $this->handleParens($node);
794: $this->newlineBefore($node, TRUE);
795: }
796:
797: public function visitNewNode(NewNode $node) {
798: if (!$node->getArgumentList()) {
799: $node->append(Token::openParen());
800: $node->addChild(new CommaListNode(), 'arguments');
801: $node->append(Token::closeParen());
802: }
803: }
804:
805: public function visitObjectMethodCallNode(ObjectMethodCallNode $node) {
806: $this->removeSpaceAfter($node->getOperator());
807: }
808:
809: public function endRootNode(RootNode $node) {
810:
811: foreach ($node->children(Filter::isTokenType(T_OPEN_TAG)) as $open_tag) {
812: $this->removeSpaceAfter($open_tag);
813: if ($open_tag !== "<?php\n") {
814: $open_tag->setText("<?php\n");
815: }
816: }
817: }
818:
819: public function visitNamespaceNode(NamespaceNode $node) {
820: $first = $node->getBody()->firstToken();
821: $has_braces = $first->getType() === '{';
822: $this->indentLevel = $has_braces ? 1 : 0;
823: if (!$has_braces) {
824: foreach ($node->children(Filter::isTokenType(';')) as $semicolon) {
825: $next = $semicolon->next();
826: $newlines = str_repeat($this->config['nl'], 2);
827: if ($next instanceof WhitespaceNode) {
828: $next->setText($newlines);
829: }
830: else {
831: $semicolon->after(Token::whitespace($newlines));
832: }
833: }
834: }
835: }
836:
837: public function endNamespaceNode(NamespaceNode $node) {
838: $this->indentLevel = 0;
839: }
840: }
841: