Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  /**
   3   * SCSSPHP
   4   *
   5   * @copyright 2012-2019 Leaf Corcoran
   6   *
   7   * @license http://opensource.org/licenses/MIT MIT
   8   *
   9   * @link http://scssphp.github.io/scssphp
  10   */
  11  
  12  namespace ScssPhp\ScssPhp\Formatter;
  13  
  14  use ScssPhp\ScssPhp\Formatter;
  15  use ScssPhp\ScssPhp\Formatter\OutputBlock;
  16  use ScssPhp\ScssPhp\Type;
  17  
  18  /**
  19   * Nested formatter
  20   *
  21   * @author Leaf Corcoran <leafot@gmail.com>
  22   */
  23  class Nested extends Formatter
  24  {
  25      /**
  26       * @var integer
  27       */
  28      private $depth;
  29  
  30      /**
  31       * {@inheritdoc}
  32       */
  33      public function __construct()
  34      {
  35          $this->indentLevel = 0;
  36          $this->indentChar = '  ';
  37          $this->break = "\n";
  38          $this->open = ' {';
  39          $this->close = ' }';
  40          $this->tagSeparator = ', ';
  41          $this->assignSeparator = ': ';
  42          $this->keepSemicolons = true;
  43      }
  44  
  45      /**
  46       * {@inheritdoc}
  47       */
  48      protected function indentStr()
  49      {
  50          $n = $this->depth - 1;
  51  
  52          return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
  53      }
  54  
  55      /**
  56       * {@inheritdoc}
  57       */
  58      protected function blockLines(OutputBlock $block)
  59      {
  60          $inner = $this->indentStr();
  61  
  62          $glue = $this->break . $inner;
  63  
  64          foreach ($block->lines as $index => $line) {
  65              if (substr($line, 0, 2) === '/*') {
  66                  $block->lines[$index] = preg_replace('/[\r\n]+/', $glue, $line);
  67              }
  68          }
  69  
  70          $this->write($inner . implode($glue, $block->lines));
  71      }
  72  
  73      /**
  74       * {@inheritdoc}
  75       */
  76      protected function block(OutputBlock $block)
  77      {
  78          static $depths;
  79          static $downLevel;
  80          static $closeBlock;
  81          static $previousEmpty;
  82          static $previousHasSelector;
  83  
  84          if ($block->type === 'root') {
  85              $depths = [ 0 ];
  86              $downLevel = '';
  87              $closeBlock = '';
  88              $this->depth = 0;
  89              $previousEmpty = false;
  90              $previousHasSelector = false;
  91          }
  92  
  93          $isMediaOrDirective = in_array($block->type, [Type::T_DIRECTIVE, Type::T_MEDIA]);
  94          $isSupport = ($block->type === Type::T_DIRECTIVE
  95              && $block->selectors && strpos(implode('', $block->selectors), '@supports') !== false);
  96  
  97          while ($block->depth < end($depths) || ($block->depth == 1 && end($depths) == 1)) {
  98              array_pop($depths);
  99              $this->depth--;
 100  
 101              if (! $this->depth && ($block->depth <= 1 || (! $this->indentLevel && $block->type === Type::T_COMMENT)) &&
 102                  (($block->selectors && ! $isMediaOrDirective) || $previousHasSelector)
 103              ) {
 104                  $downLevel = $this->break;
 105              }
 106  
 107              if (empty($block->lines) && empty($block->children)) {
 108                  $previousEmpty = true;
 109              }
 110          }
 111  
 112          if (empty($block->lines) && empty($block->children)) {
 113              return;
 114          }
 115  
 116          $this->currentBlock = $block;
 117  
 118          if (! empty($block->lines) || (! empty($block->children) && ($this->depth < 1 || $isSupport))) {
 119              if ($block->depth > end($depths)) {
 120                  if (! $previousEmpty || $this->depth < 1) {
 121                      $this->depth++;
 122                      $depths[] = $block->depth;
 123                  } else {
 124                      // keep the current depth unchanged but take the block depth as a new reference for following blocks
 125                      array_pop($depths);
 126                      $depths[] = $block->depth;
 127                  }
 128              }
 129          }
 130  
 131          $previousEmpty = ($block->type === Type::T_COMMENT);
 132          $previousHasSelector = false;
 133  
 134          if (! empty($block->selectors)) {
 135              if ($closeBlock) {
 136                  $this->write($closeBlock);
 137                  $closeBlock = '';
 138              }
 139  
 140              if ($downLevel) {
 141                  $this->write($downLevel);
 142                  $downLevel = '';
 143              }
 144  
 145              $this->blockSelectors($block);
 146  
 147              $this->indentLevel++;
 148          }
 149  
 150          if (! empty($block->lines)) {
 151              if ($closeBlock) {
 152                  $this->write($closeBlock);
 153                  $closeBlock = '';
 154              }
 155  
 156              if ($downLevel) {
 157                  $this->write($downLevel);
 158                  $downLevel = '';
 159              }
 160  
 161              $this->blockLines($block);
 162  
 163              $closeBlock = $this->break;
 164          }
 165  
 166          if (! empty($block->children)) {
 167              if ($this->depth > 0 && ($isMediaOrDirective || ! $this->hasFlatChild($block))) {
 168                  array_pop($depths);
 169  
 170                  $this->depth--;
 171                  $this->blockChildren($block);
 172                  $this->depth++;
 173  
 174                  $depths[] = $block->depth;
 175              } else {
 176                  $this->blockChildren($block);
 177              }
 178          }
 179  
 180          // reclear to not be spoiled by children if T_DIRECTIVE
 181          if ($block->type === Type::T_DIRECTIVE) {
 182              $previousHasSelector = false;
 183          }
 184  
 185          if (! empty($block->selectors)) {
 186              $this->indentLevel--;
 187  
 188              if (! $this->keepSemicolons) {
 189                  $this->strippedSemicolon = '';
 190              }
 191  
 192              $this->write($this->close);
 193  
 194              $closeBlock = $this->break;
 195  
 196              if ($this->depth > 1 && ! empty($block->children)) {
 197                  array_pop($depths);
 198                  $this->depth--;
 199              }
 200  
 201              if (! $isMediaOrDirective) {
 202                  $previousHasSelector = true;
 203              }
 204          }
 205  
 206          if ($block->type === 'root') {
 207              $this->write($this->break);
 208          }
 209      }
 210  
 211      /**
 212       * Block has flat child
 213       *
 214       * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
 215       *
 216       * @return boolean
 217       */
 218      private function hasFlatChild($block)
 219      {
 220          foreach ($block->children as $child) {
 221              if (empty($child->selectors)) {
 222                  return true;
 223              }
 224          }
 225  
 226          return false;
 227      }
 228  }