Changeset 116185 in spip-zone


Ignore:
Timestamp:
Jul 31, 2019, 1:56:53 PM (8 months ago)
Author:
cedric@…
Message:

Mise a jour de la lib SCSSPHP

Location:
_plugins_/scssphp/trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • _plugins_/scssphp/trunk/lib/scssphp/bin/pscss

    r114927 r116185  
    44 * SCSSPHP
    55 *
    6  * @copyright 2012-2015 Leaf Corcoran
     6 * @copyright 2012-2019 Leaf Corcoran
    77 *
    88 * @license http://opensource.org/licenses/MIT MIT
    99 *
    10  * @link http://leafo.github.io/scssphp
     10 * @link http://scssphp.github.io/scssphp
    1111 */
    1212
    1313error_reporting(E_ALL);
    1414
    15 if (version_compare(PHP_VERSION, '5.4') < 0) {
    16     die('Requires PHP 5.4 or above');
     15if (version_compare(PHP_VERSION, '5.6') < 0) {
     16    die('Requires PHP 5.6 or above');
    1717}
    1818
    1919include __DIR__ . '/../scss.inc.php';
    2020
    21 use Leafo\ScssPhp\Compiler;
    22 use Leafo\ScssPhp\Parser;
    23 use Leafo\ScssPhp\Version;
     21use ScssPhp\ScssPhp\Compiler;
     22use ScssPhp\ScssPhp\Parser;
     23use ScssPhp\ScssPhp\Version;
    2424
    2525$style = null;
     
    198198
    199199if ($style) {
    200     $scss->setFormatter('Leafo\\ScssPhp\\Formatter\\' . ucfirst($style));
     200    $scss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\' . ucfirst($style));
    201201}
    202202
  • _plugins_/scssphp/trunk/lib/scssphp/src/Cache.php

    r115547 r116185  
    4747    public static $gcLifetime = 604800;
    4848
    49     // array of already refreshed cache if $forceFefresh==='once'
     49    // array of already refreshed cache if $forceRefresh==='once'
    5050    protected static $refreshed = [];
    5151
     
    7575
    7676        if (isset($options['forceRefresh'])) {
    77             self::$forceFefresh = $options['force_refresh'];
     77            self::$forceRefresh = $options['force_refresh'];
    7878        }
    7979
     
    9898        $fileCache = self::$cacheDir . self::cacheName($operation, $what, $options);
    9999
    100         if ((! self::$forceRefresh || (self::$forceRefresh === 'once' && isset(self::$refreshed[$fileCache])))
    101             && file_exists($fileCache)
     100        if ((! self::$forceRefresh || (self::$forceRefresh === 'once' &&
     101            isset(self::$refreshed[$fileCache]))) && file_exists($fileCache)
    102102        ) {
    103103            $cacheTime = filemtime($fileCache);
    104104
    105             if ((is_null($lastModified) || $cacheTime > $lastModified)
    106                 && $cacheTime + self::$gcLifetime > time()
     105            if ((is_null($lastModified) || $cacheTime > $lastModified) &&
     106                $cacheTime + self::$gcLifetime > time()
    107107            ) {
    108108                $c = file_get_contents($fileCache);
  • _plugins_/scssphp/trunk/lib/scssphp/src/Compiler.php

    r115547 r116185  
    204204    {
    205205        if ($this->cache) {
    206             $cacheKey = ($path ? $path : "(stdin)") . ":" . md5($code);
     206            $cacheKey       = ($path ? $path : "(stdin)") . ":" . md5($code);
    207207            $compileOptions = $this->getCompileOptions();
    208             $cache = $this->cache->getCache("compile", $cacheKey, $compileOptions);
    209 
    210             if (is_array($cache)
    211                 && isset($cache['dependencies'])
    212                 && isset($cache['out'])
    213             ) {
     208            $cache          = $this->cache->getCache("compile", $cacheKey, $compileOptions);
     209
     210            if (is_array($cache) && isset($cache['dependencies']) && isset($cache['out'])) {
    214211                // check if any dependency file changed before accepting the cache
    215212                foreach ($cache['dependencies'] as $file => $mtime) {
    216                     if (! file_exists($file)
    217                         || filemtime($file) !== $mtime
    218                     ) {
     213                    if (! file_exists($file) || filemtime($file) !== $mtime) {
    219214                        unset($cache);
    220215                        break;
     
    243238
    244239        $this->parser = $this->parserFactory($path);
    245         $tree = $this->parser->parse($code);
     240        $tree         = $this->parser->parse($code);
    246241        $this->parser = null;
    247242
     
    504499                // a selector part finishing with a ) is the last part of a :not( or :nth-child(
    505500                // and need to be joined to this
    506                 if (count($new) && is_string($new[count($new) - 1])
    507                     && strlen($part) && substr($part, -1) === ')' && strpos($part, '(') === false
     501                if (count($new) && is_string($new[count($new) - 1]) &&
     502                    strlen($part) && substr($part, -1) === ')' && strpos($part, '(') === false
    508503                ) {
    509504                    $new[count($new) - 1] .= $part;
     
    530525
    531526        $selector = $this->glueFunctionSelectors($selector);
     527
     528        if (count($selector) == 1 && in_array(reset($selector), $partsPile)) {
     529            return;
     530        }
    532531
    533532        foreach ($selector as $i => $part) {
     
    926925        $env     = $this->pushEnv($block);
    927926        $envs    = $this->compactEnv($env);
    928         $without = isset($block->with) ? $this->compileWith($block->with) : static::WITH_RULE;
     927        list($with, $without) = $this->compileWith(isset($block->with) ? $block->with : null);
    929928
    930929        // wrap inline selector
     
    953952        }
    954953
    955         $this->env = $this->filterWithout($envs, $without);
     954        $this->env = $this->filterWithWithout($envs, $with, $without);
    956955
    957956        $saveScope   = $this->scope;
    958         $this->scope = $this->filterScopeWithout($saveScope, $without);
     957        $this->scope = $this->filterScopeWithWithout($saveScope, $with, $without);
    959958
    960959        // propagate selfParent to the children where they still can be useful
     
    972971     *
    973972     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
    974      * @param mixed                                  $without
     973     * @param array                                  $with
     974     * @param array                                  $without
    975975     *
    976976     * @return mixed
    977977     */
    978     protected function filterScopeWithout($scope, $without)
     978    protected function filterScopeWithWithout($scope, $with, $without)
    979979    {
    980980        $filteredScopes = [];
     
    994994            }
    995995
    996             if (! $this->isWithout($without, $scope)) {
     996            if ($this->isWith($scope, $with, $without)) {
    997997                $s = clone $scope;
    998998                $s->children = [];
     
    10851085
    10861086    /**
    1087      * Compile @at-root's with: inclusion / without: exclusion into filter flags
    1088      *
     1087     * Compile @at-root's with: inclusion / without: exclusion into 2 lists uses to filter scope/env later
     1088     *
     1089     * @param array $withCondition
     1090     *
     1091     * @return array
     1092     */
     1093    protected function compileWith($withCondition)
     1094    {
     1095        // just compile what we have in 2 lists
     1096        $with = [];
     1097        $without = ['rule' => true];
     1098
     1099        if ($withCondition) {
     1100            if ($this->libMapHasKey([$withCondition, static::$with])) {
     1101                $without = []; // cancel the default
     1102                $list = $this->coerceList($this->libMapGet([$withCondition, static::$with]));
     1103
     1104                foreach ($list[2] as $item) {
     1105                    $keyword = $this->compileStringContent($this->coerceString($item));
     1106
     1107                    $with[$keyword] = true;
     1108                }
     1109            }
     1110
     1111            if ($this->libMapHasKey([$withCondition, static::$without])) {
     1112                $without = []; // cancel the default
     1113                $list = $this->coerceList($this->libMapGet([$withCondition, static::$without]));
     1114
     1115                foreach ($list[2] as $item) {
     1116                    $keyword = $this->compileStringContent($this->coerceString($item));
     1117
     1118                    $without[$keyword] = true;
     1119                }
     1120            }
     1121        }
     1122
     1123        return [$with, $without];
     1124    }
     1125
     1126    /**
     1127     * Filter env stack
     1128     *
     1129     * @param array   $envs
    10891130     * @param array $with
    1090      *
    1091      * @return integer
    1092      */
    1093     protected function compileWith($with)
    1094     {
    1095         static $mapping = [
    1096             'rule'     => self::WITH_RULE,
    1097             'media'    => self::WITH_MEDIA,
    1098             'supports' => self::WITH_SUPPORTS,
    1099             'all'      => self::WITH_ALL,
    1100         ];
    1101 
    1102         // exclude selectors by default
    1103         $without = static::WITH_RULE;
    1104 
    1105         if ($this->libMapHasKey([$with, static::$with])) {
    1106             $without = static::WITH_ALL;
    1107 
    1108             $list = $this->coerceList($this->libMapGet([$with, static::$with]));
    1109 
    1110             foreach ($list[2] as $item) {
    1111                 $keyword = $this->compileStringContent($this->coerceString($item));
    1112 
    1113                 if (array_key_exists($keyword, $mapping)) {
    1114                     $without &= ~($mapping[$keyword]);
    1115                 }
    1116             }
    1117         }
    1118 
    1119         if ($this->libMapHasKey([$with, static::$without])) {
    1120             $without = 0;
    1121 
    1122             $list = $this->coerceList($this->libMapGet([$with, static::$without]));
    1123 
    1124             foreach ($list[2] as $item) {
    1125                 $keyword = $this->compileStringContent($this->coerceString($item));
    1126 
    1127                 if (array_key_exists($keyword, $mapping)) {
    1128                     $without |= $mapping[$keyword];
    1129                 }
    1130             }
    1131         }
    1132 
    1133         return $without;
    1134     }
    1135 
    1136     /**
    1137      * Filter env stack
    1138      *
    1139      * @param array   $envs
    1140      * @param integer $without
     1131     * @param array $without
    11411132     *
    11421133     * @return \ScssPhp\ScssPhp\Compiler\Environment
    11431134     */
    1144     protected function filterWithout($envs, $without)
     1135    protected function filterWithWithout($envs, $with, $without)
    11451136    {
    11461137        $filtered = [];
    11471138
    11481139        foreach ($envs as $e) {
    1149             if ($e->block && $this->isWithout($without, $e->block)) {
     1140            if ($e->block && ! $this->isWith($e->block, $with, $without)) {
    11501141                $ec = clone $e;
    11511142                $ec->block = null;
     
    11631154     * Filter WITH rules
    11641155     *
    1165      * @param integer                                                       $without
    11661156     * @param \ScssPhp\ScssPhp\Block|\ScssPhp\ScssPhp\Formatter\OutputBlock $block
     1157     * @param array                                                         $with
     1158     * @param array                                                         $without
    11671159     *
    11681160     * @return boolean
    11691161     */
    1170     protected function isWithout($without, $block)
     1162    protected function isWith($block, $with, $without)
    11711163    {
    11721164        if (isset($block->type)) {
    11731165            if ($block->type === Type::T_MEDIA) {
    1174                 return ($without & static::WITH_MEDIA) ? true : false;
     1166                return $this->testWithWithout('media', $with, $without);
    11751167            }
    11761168
    11771169            if ($block->type === Type::T_DIRECTIVE) {
    1178                 if (isset($block->name) && $block->name === 'supports') {
    1179                     return ($without & static::WITH_SUPPORTS) ? true : false;
    1180                 }
    1181 
    1182                 if (isset($block->selectors) && strpos(serialize($block->selectors), '@supports') !== false) {
    1183                     return ($without & static::WITH_SUPPORTS) ? true : false;
    1184                 }
    1185             }
    1186         }
    1187 
    1188         if ((($without & static::WITH_RULE) && isset($block->selectors))) {
    1189             return true;
    1190         }
    1191 
    1192         return false;
    1193     }
     1170                if (isset($block->name)) {
     1171                    return $this->testWithWithout($block->name, $with, $without);
     1172                } elseif (isset($block->selectors) && preg_match(',@(\w+),ims', json_encode($block->selectors), $m)) {
     1173                    return $this->testWithWithout($m[1], $with, $without);
     1174                } else {
     1175                    return $this->testWithWithout('???', $with, $without);
     1176                }
     1177            }
     1178        } elseif (isset($block->selectors)) {
     1179            return $this->testWithWithout('rule', $with, $without);
     1180        }
     1181
     1182        return true;
     1183    }
     1184
     1185    /**
     1186     * Test a single type of block against with/without lists
     1187     *
     1188     * @param string $what
     1189     * @param array  $with
     1190     * @param array  $without
     1191     * @return bool
     1192     *   true if the block should be kept, false to reject
     1193     */
     1194    protected function testWithWithout($what, $with, $without)
     1195    {
     1196
     1197        // if without, reject only if in the list (or 'all' is in the list)
     1198        if (count($without)) {
     1199            return (isset($without[$what]) || isset($without['all'])) ? false : true;
     1200        }
     1201
     1202        // otherwise reject all what is not in the with list
     1203        return (isset($with[$what]) || isset($with['all'])) ? true : false;
     1204    }
     1205
    11941206
    11951207    /**
     
    12191231
    12201232        $this->popEnv();
     1233    }
     1234
     1235    /**
     1236     * Compile nested properties lines
     1237     *
     1238     * @param \ScssPhp\ScssPhp\Block $block
     1239     * @param OutputBlock            $out
     1240     */
     1241    protected function compileNestedPropertiesBlock(Block $block, OutputBlock $out)
     1242    {
     1243        $prefix = $this->compileValue($block->prefix) . '-';
     1244
     1245        $nested = $this->makeOutputBlock($block->type);
     1246        $nested->parent = $out;
     1247
     1248        if ($block->hasValue) {
     1249            $nested->depth = $out->depth + 1;
     1250        }
     1251
     1252        $out->children[] = $nested;
     1253
     1254        foreach ($block->children as $child) {
     1255            switch ($child[0]) {
     1256                case Type::T_ASSIGN:
     1257                    array_unshift($child[1][2], $prefix);
     1258                    break;
     1259
     1260                case Type::T_NESTED_PROPERTY:
     1261                    array_unshift($child[1]->prefix[2], $prefix);
     1262                    break;
     1263            }
     1264            $this->compileChild($child, $nested);
     1265        }
    12211266    }
    12221267
     
    17111756    protected function evaluateMediaQuery($queryList)
    17121757    {
     1758        static $parser = null;
     1759        $outQueryList = [];
    17131760        foreach ($queryList as $kql => $query) {
     1761            $shouldReparse = false;
    17141762            foreach ($query as $kq => $q) {
    17151763                for ($i = 1; $i < count($q); $i++) {
     
    17171765
    17181766                    // the parser had no mean to know if media type or expression if it was an interpolation
     1767                    // so you need to reparse if the T_MEDIA_TYPE looks like anything else a media type
    17191768                    if ($q[0] == Type::T_MEDIA_TYPE &&
    17201769                        (strpos($value, '(') !== false ||
    17211770                        strpos($value, ')') !== false ||
    1722                         strpos($value, ':') !== false)
     1771                        strpos($value, ':') !== false ||
     1772                        strpos($value, ',') !== false)
    17231773                    ) {
    1724                         $queryList[$kql][$kq][0] = Type::T_MEDIA_EXPRESSION;
    1725 
    1726                         if (strpos($value, 'and') !== false) {
    1727                             $values = explode('and', $value);
    1728                             $value = trim(array_pop($values));
    1729 
    1730                             while ($v = trim(array_pop($values))) {
    1731                                 $type = Type::T_MEDIA_EXPRESSION;
    1732 
    1733                                 if (strpos($v, '(') === false &&
    1734                                     strpos($v, ')') === false &&
    1735                                     strpos($v, ':') === false
    1736                                 ) {
    1737                                     $type = Type::T_MEDIA_TYPE;
    1738                                 }
    1739 
    1740                                 if (substr($v, 0, 1) === '(' && substr($v, -1) === ')') {
    1741                                     $v = substr($v, 1, -1);
    1742                                 }
    1743 
    1744                                 $queryList[$kql][] = [$type,[Type::T_KEYWORD, $v]];
    1745                             }
     1774                        $shouldReparse = true;
     1775                    }
     1776
     1777                    $queryList[$kql][$kq][$i] = [Type::T_KEYWORD, $value];
     1778                }
     1779            }
     1780            if ($shouldReparse) {
     1781                if (is_null($parser)) {
     1782                    $parser = $this->parserFactory(__METHOD__);
     1783                }
     1784                $queryString = $this->compileMediaQuery([$queryList[$kql]]);
     1785                $queryString = reset($queryString);
     1786                if (strpos($queryString, '@media ') === 0) {
     1787                    $queryString = substr($queryString, 7);
     1788                    $queries = [];
     1789                    if ($parser->parseMediaQueryList($queryString, $queries)) {
     1790                        $queries = $this->evaluateMediaQuery($queries[2]);
     1791                        while (count($queries)) {
     1792                            $outQueryList[] = array_shift($queries);
    17461793                        }
    1747 
    1748                         if (substr($value, 0, 1) === '(' && substr($value, -1) === ')') {
    1749                             $value = substr($value, 1, -1);
    1750                         }
     1794                        continue;
    17511795                    }
    1752 
    1753                     $queryList[$kql][$kq][$i] = [Type::T_KEYWORD, $value];
    1754                 }
    1755             }
    1756         }
    1757 
    1758         return $queryList;
     1796                }
     1797            }
     1798            $outQueryList[] = $queryList[$kql];
     1799        }
     1800
     1801        return $outQueryList;
    17591802    }
    17601803
     
    20392082    }
    20402083
     2084
     2085    /**
     2086     * Append a root directive like @import or @charset as near as the possible from the source code
     2087     * (keeping before comments, @import and @charset coming before in the source code)
     2088     *
     2089     * @param string                                        $line
     2090     * @param @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
     2091     * @param array                                         $allowed
     2092     */
     2093    protected function appendRootDirective($line, $out, $allowed = [Type::T_COMMENT])
     2094    {
     2095        $root = $out;
     2096
     2097        while ($root->parent) {
     2098            $root = $root->parent;
     2099        }
     2100
     2101        $i = 0;
     2102
     2103        while ($i < count($root->children)) {
     2104            if (! isset($root->children[$i]->type) || ! in_array($root->children[$i]->type, $allowed)) {
     2105                break;
     2106            }
     2107
     2108            $i++;
     2109        }
     2110
     2111        // remove incompatible children from the bottom of the list
     2112        $saveChildren = [];
     2113
     2114        while ($i < count($root->children)) {
     2115            $saveChildren[] = array_pop($root->children);
     2116        }
     2117
     2118        // insert the directive as a comment
     2119        $child = $this->makeOutputBlock(Type::T_COMMENT);
     2120        $child->lines[] = $line;
     2121        $child->sourceName = $this->sourceNames[$this->sourceIndex];
     2122        $child->sourceLine = $this->sourceLine;
     2123        $child->sourceColumn = $this->sourceColumn;
     2124
     2125        $root->children[] = $child;
     2126
     2127        // repush children
     2128        while (count($saveChildren)) {
     2129            $root->children[] = array_pop($saveChildren);
     2130        }
     2131    }
     2132
     2133    /**
     2134     * Append lines to the courrent output block:
     2135     * directly to the block or through a child if necessary
     2136     *
     2137     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
     2138     * @param string                                 $type
     2139     * @param string                                 $line
     2140     */
     2141    protected function appendOutputLine(OutputBlock $out, $type, $line)
     2142    {
     2143        $outWrite = &$out;
     2144
     2145        if ($type === Type::T_COMMENT) {
     2146            $parent = $out->parent;
     2147
     2148            if (end($parent->children) !== $out) {
     2149                $outWrite = &$parent->children[count($parent->children)-1];
     2150            }
     2151
     2152            if (!is_string($line)) {
     2153                $line = $this->compileValue($line);
     2154            }
     2155        }
     2156
     2157        // check if it's a flat output or not
     2158        if (count($out->children)) {
     2159            $lastChild = &$out->children[count($out->children) -1];
     2160
     2161            if ($lastChild->depth === $out->depth && is_null($lastChild->selectors) && ! count($lastChild->children)) {
     2162                $outWrite = $lastChild;
     2163            } else {
     2164                $nextLines = $this->makeOutputBlock($type);
     2165                $nextLines->parent = $out;
     2166                $nextLines->depth = $out->depth;
     2167
     2168                $out->children[] = $nextLines;
     2169                $outWrite = &$nextLines;
     2170            }
     2171        }
     2172
     2173        $outWrite->lines[] = $line;
     2174    }
     2175
    20412176    /**
    20422177     * Compile child; returns a value to halt execution
     
    20712206
    20722207                if (! $this->compileImport($rawPath, $out, true)) {
    2073                     $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';';
     2208                    $this->appendRootDirective('@import ' . $this->compileValue($rawPath) . ';', $out);
    20742209                }
    20752210                break;
     
    20792214
    20802215                if (! $this->compileImport($rawPath, $out)) {
    2081                     $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';';
     2216                    $this->appendRootDirective('@import ' . $this->compileValue($rawPath) . ';', $out);
    20822217                }
    20832218                break;
     
    21022237                if (! $this->charsetSeen) {
    21032238                    $this->charsetSeen = true;
    2104 
    2105                     $out->lines[] = '@charset ' . $this->compileValue($child[1]) . ';';
     2239                    $this->appendRootDirective('@charset ' . $this->compileValue($child[1]) . ';', $out);
    21062240                }
    21072241                break;
     
    21212255
    21222256                    $shouldSet = $isDefault &&
    2123                         (($result = $this->get($name[1], false)) === null
    2124                         || $result === static::$null);
     2257                        (($result = $this->get($name[1], false)) === null ||
     2258                        $result === static::$null);
    21252259
    21262260                    if (! $isDefault || $shouldSet) {
     
    21712305                $compiledValue = $this->compileValue($value);
    21722306
    2173                 $out->lines[] = $this->formatter->property(
     2307                $line = $this->formatter->property(
    21742308                    $compiledName,
    21752309                    $compiledValue
    21762310                );
     2311                $this->appendOutputLine($out, Type::T_ASSIGN, $line);
    21772312                break;
    21782313
     
    21832318                }
    21842319
    2185                 $out->lines[] = $child[1];
     2320                $this->appendOutputLine($out, Type::T_COMMENT, $child[1]);
    21862321                break;
    21872322
     
    21902325                list(, $block) = $child;
    21912326
    2192                 $this->set(static::$namespaces[$block->type] . $block->name, $block);
     2327                $this->set(static::$namespaces[$block->type] . $block->name, $block, true);
    21932328                break;
    21942329
     
    23282463
    23292464            case Type::T_NESTED_PROPERTY:
    2330                 list(, $prop) = $child;
    2331 
    2332                 $prefixed = [];
    2333                 $prefix = $this->compileValue($prop->prefix) . '-';
    2334 
    2335                 foreach ($prop->children as $child) {
    2336                     switch ($child[0]) {
    2337                         case Type::T_ASSIGN:
    2338                             array_unshift($child[1][2], $prefix);
    2339                             break;
    2340 
    2341                         case Type::T_NESTED_PROPERTY:
    2342                             array_unshift($child[1]->prefix[2], $prefix);
    2343                             break;
    2344                     }
    2345 
    2346                     $prefixed[] = $child;
    2347                 }
    2348 
    2349                 $this->compileChildrenNoReturn($prefixed, $out);
     2465                $this->compileNestedPropertiesBlock($child[1], $out);
    23502466                break;
    23512467
    23522468            case Type::T_INCLUDE:
    23532469                // including a mixin
    2354                 list(, $name, $argValues, $content) = $child;
     2470                list(, $name, $argValues, $content, $argUsing) = $child;
    23552471
    23562472                $mixin = $this->get(static::$namespaces['mixin'] . $name, false);
     
    23952511                if (isset($content)) {
    23962512                    $copyContent = clone $content;
    2397                     $copyContent->scope = $callingScope;
     2513                    $copyContent->scope = clone $callingScope;
    23982514
    23992515                    $this->setRaw(static::$namespaces['special'] . 'content', $copyContent, $this->env);
     2516                } else {
     2517                    $this->setRaw(static::$namespaces['special'] . 'content', null, $this->env);
     2518                }
     2519
     2520                // save the "using" argument list for applying it to when "@content" is invoked
     2521                if (isset($argUsing)) {
     2522                    $this->setRaw(static::$namespaces['special'] . 'using', $argUsing, $this->env);
     2523                } else {
     2524                    $this->setRaw(static::$namespaces['special'] . 'using', null, $this->env);
    24002525                }
    24012526
     
    24162541                $env = isset($this->storeEnv) ? $this->storeEnv : $this->env;
    24172542                $content = $this->get(static::$namespaces['special'] . 'content', false, $env);
     2543                $argUsing = $this->get(static::$namespaces['special'] . 'using', false, $env);
     2544                $argContent = $child[1];
    24182545
    24192546                if (! $content) {
    24202547                    $content = new \stdClass();
    24212548                    $content->scope = new \stdClass();
    2422                     $content->children = $this->storeEnv->parent->block->children;
     2549                    $content->children = $env->parent->block->children;
    24232550                    break;
    24242551                }
    24252552
    24262553                $storeEnv = $this->storeEnv;
     2554
     2555                $varsUsing = [];
     2556                if (isset($argUsing) && isset($argContent)) {
     2557                    // Get the arguments provided for the content with the names provided in the "using" argument list
     2558                    $this->storeEnv = $this->env;
     2559                    $varsUsing = $this->applyArguments($argUsing, $argContent, false);
     2560                }
     2561
     2562                // restore the scope from the @content
    24272563                $this->storeEnv = $content->scope;
     2564                // append the vars from using if any
     2565                foreach ($varsUsing as $name => $val) {
     2566                    $this->set($name, $val, true, $this->storeEnv);
     2567                }
     2568
    24282569                $this->compileChildrenNoReturn($content->children, $out);
    24292570
     
    25682709
    25692710                // special case: looks like css shorthand
    2570                 if ($opName == 'div' && ! $inParens && ! $inExp && isset($right[2])
    2571                     && (($right[0] !== Type::T_NUMBER && $right[2] != '')
    2572                     || ($right[0] === Type::T_NUMBER && ! $right->unitless()))
     2711                if ($opName == 'div' && ! $inParens && ! $inExp && isset($right[2]) &&
     2712                    (($right[0] !== Type::T_NUMBER && $right[2] != '') ||
     2713                    ($right[0] === Type::T_NUMBER && ! $right->unitless()))
    25732714                ) {
    25742715                    return $this->expToString($value);
     
    33123453                list(,, $whiteLeft, $whiteRight) = $interpolate;
    33133454
     3455                $delim = $left[1];
     3456                if ($delim && $delim !== ' ' && !$whiteLeft) {
     3457                    $delim .= ' ';
     3458                }
    33143459                $left = count($left[2]) > 0 ?
    3315                     $this->compileValue($left) . $whiteLeft : '';
    3316 
     3460                    $this->compileValue($left) . $delim . $whiteLeft: '';
     3461
     3462                $delim = $right[1];
     3463                if ($delim && $delim !== ' ') {
     3464                    $delim .= ' ';
     3465                }
    33173466                $right = count($right[2]) > 0 ?
    3318                     $whiteRight . $this->compileValue($right) : '';
     3467                    $whiteRight . $delim . $this->compileValue($right) : '';
    33193468
    33203469                return $left . $this->compileValue($interpolate) . $right;
     
    33723521
    33733522            default:
    3374                 $this->throwError("unknown value type: $value[0]");
     3523                $this->throwError("unknown value type: ".json_encode($value));
    33753524        }
    33763525    }
     
    43184467        }
    43194468
    4320         @list($sorted, $kwargs) = $this->sortArgs($prototype, $args);
     4469        @list($sorted, $kwargs) = $this->sortNativeFunctionArgs($prototype, $args);
    43214470
    43224471        if ($name !== 'if' && $name !== 'call') {
     4472            $inExp = true;
     4473            if ($name === 'join') {
     4474                $inExp = false;
     4475            }
    43234476            foreach ($sorted as &$val) {
    4324                 $val = $this->reduce($val, true);
     4477                $val = $this->reduce($val, $inExp);
    43254478            }
    43264479        }
     
    43654518     * @return array
    43664519     */
    4367     protected function sortArgs($prototype, $args)
    4368     {
     4520    protected function sortNativeFunctionArgs($prototypes, $args)
     4521    {
     4522        static $parser = null;
     4523
     4524        if (! isset($prototypes)) {
     4525            $keyArgs = [];
     4526            $posArgs = [];
     4527
     4528            // separate positional and keyword arguments
     4529            foreach ($args as $arg) {
     4530                list($key, $value) = $arg;
     4531
     4532                $key = $key[1];
     4533
     4534                if (empty($key)) {
     4535                    $posArgs[] = empty($arg[2]) ? $value : $arg;
     4536                } else {
     4537                    $keyArgs[$key] = $value;
     4538                }
     4539            }
     4540
     4541            return [$posArgs, $keyArgs];
     4542        }
     4543
     4544        $finalArgs = [];
     4545
     4546        if (! is_array(reset($prototypes))) {
     4547            $prototypes = [$prototypes];
     4548        }
     4549
    43694550        $keyArgs = [];
    4370         $posArgs = [];
    4371 
    4372         // separate positional and keyword arguments
    4373         foreach ($args as $arg) {
    4374             list($key, $value) = $arg;
    4375 
    4376             $key = $key[1];
    4377 
    4378             if (empty($key)) {
    4379                 $posArgs[] = empty($arg[2]) ? $value : $arg;
    4380             } else {
    4381                 $keyArgs[$key] = $value;
    4382             }
    4383         }
    4384 
    4385         if (! isset($prototype)) {
    4386             return [$posArgs, $keyArgs];
    4387         }
    4388 
    4389         // copy positional args
    4390         $finalArgs = array_pad($posArgs, count($prototype), null);
    4391 
    4392         // overwrite positional args with keyword args
    4393         foreach ($prototype as $i => $names) {
    4394             foreach ((array) $names as $name) {
    4395                 if (isset($keyArgs[$name])) {
    4396                     $finalArgs[$i] = $keyArgs[$name];
    4397                 }
    4398             }
     4551
     4552        // trying each prototypes
     4553        $prototypeHasMatch = false;
     4554        $exceptionMessage = '';
     4555
     4556        foreach ($prototypes as $prototype) {
     4557            $argDef = [];
     4558
     4559            foreach ($prototype as $i => $p) {
     4560                $default = null;
     4561                $p       = explode(':', $p, 2);
     4562                $name    = array_shift($p);
     4563
     4564                if (count($p)) {
     4565                    $p = trim(reset($p));
     4566
     4567                    if ($p === 'null') {
     4568                        // differentiate this null from the static::$null
     4569                        $default = [Type::T_KEYWORD, 'null'];
     4570                    } else {
     4571                        if (is_null($parser)) {
     4572                            $parser = $this->parserFactory(__METHOD__);
     4573                        }
     4574
     4575                        $parser->parseValue($p, $default);
     4576                    }
     4577                }
     4578
     4579                $isVariable = false;
     4580
     4581                if (substr($name, -3) === '...') {
     4582                    $isVariable = true;
     4583                    $name = substr($name, 0, -3);
     4584                }
     4585
     4586                $argDef[] = [$name, $default, $isVariable];
     4587            }
     4588
     4589            try {
     4590                $vars = $this->applyArguments($argDef, $args, false, false);
     4591
     4592                // ensure all args are populated
     4593                foreach ($prototype as $i => $p) {
     4594                    $name = explode(':', $p)[0];
     4595
     4596                    if (! isset($finalArgs[$i])) {
     4597                        $finalArgs[$i] = null;
     4598                    }
     4599                }
     4600
     4601                // apply positional args
     4602                foreach (array_values($vars) as $i => $val) {
     4603                    $finalArgs[$i] = $val;
     4604                }
     4605
     4606                $keyArgs = array_merge($keyArgs, $vars);
     4607                $prototypeHasMatch = true;
     4608
     4609                // overwrite positional args with keyword args
     4610                foreach ($prototype as $i => $p) {
     4611                    $name = explode(':', $p)[0];
     4612
     4613                    if (isset($keyArgs[$name])) {
     4614                        $finalArgs[$i] = $keyArgs[$name];
     4615                    }
     4616
     4617                    // special null value as default: translate to real null here
     4618                    if ($finalArgs[$i] === [Type::T_KEYWORD, 'null']) {
     4619                        $finalArgs[$i] = null;
     4620                    }
     4621                }
     4622                // should we break if this prototype seems fulfilled?
     4623            } catch (CompilerException $e) {
     4624                $exceptionMessage = $e->getMessage();
     4625            }
     4626        }
     4627
     4628        if ($exceptionMessage && ! $prototypeHasMatch) {
     4629            $this->throwError($exceptionMessage);
    43994630        }
    44004631
     
    44074638     * @param array $argDef
    44084639     * @param array $argValues
    4409      *
     4640     * @param bool $storeInEnv
     4641     * @param bool $reduce
     4642     *   only used if $storeInEnv = false
    44104643     * @throws \Exception
    44114644     */
    4412     protected function applyArguments($argDef, $argValues)
    4413     {
    4414         $storeEnv = $this->getStoreEnv();
    4415 
    4416         $env = new Environment;
    4417         $env->store = $storeEnv->store;
     4645    protected function applyArguments($argDef, $argValues, $storeInEnv = true, $reduce = true)
     4646    {
     4647        $output = [];
     4648
     4649        if ($storeInEnv) {
     4650            $storeEnv = $this->getStoreEnv();
     4651
     4652            $env = new Environment;
     4653            $env->store = $storeEnv->store;
     4654        }
    44184655
    44194656        $hasVariable = false;
     
    44274664        }
    44284665
    4429         $keywordArgs = [];
     4666        $splatSeparator      = null;
     4667        $keywordArgs         = [];
    44304668        $deferredKeywordArgs = [];
    4431         $remaining = [];
     4669        $remaining           = [];
     4670        $hasKeywordArgument  = false;
    44324671
    44334672        // assign the keyword args
    44344673        foreach ((array) $argValues as $arg) {
    44354674            if (! empty($arg[0])) {
    4436                 if (! isset($args[$arg[0][1]])) {
     4675                $hasKeywordArgument = true;
     4676
     4677                if (! isset($args[$arg[0][1]]) || $args[$arg[0][1]][3]) {
    44374678                    if ($hasVariable) {
    44384679                        $deferredKeywordArgs[$arg[0][1]] = $arg[1];
     
    44474688                    $keywordArgs[$arg[0][1]] = $arg[1];
    44484689                }
    4449             } elseif (count($keywordArgs)) {
    4450                 $this->throwError('Positional arguments must come before keyword arguments.');
    4451                 break;
    44524690            } elseif ($arg[2] === true) {
    44534691                $val = $this->reduce($arg[1], true);
     
    44564694                    foreach ($val[2] as $name => $item) {
    44574695                        if (! is_numeric($name)) {
    4458                             $keywordArgs[$name] = $item;
     4696                            if (!isset($args[$name])) {
     4697                                foreach (array_keys($args) as $an) {
     4698                                    if (str_replace("_", "-", $an) === str_replace("_", "-", $name)) {
     4699                                        $name = $an;
     4700                                        break;
     4701                                    }
     4702                                }
     4703                            }
     4704
     4705                            if ($hasVariable) {
     4706                                $deferredKeywordArgs[$name] = $item;
     4707                            } else {
     4708                                $keywordArgs[$name] = $item;
     4709                            }
    44594710                        } else {
     4711                            if (is_null($splatSeparator)) {
     4712                                $splatSeparator = $val[1];
     4713                            }
    44604714                            $remaining[] = $item;
    44614715                        }
     
    44674721
    44684722                        if (! is_numeric($name)) {
    4469                             $keywordArgs[$name] = $item;
     4723                            if (!isset($args[$name])) {
     4724                                foreach (array_keys($args) as $an) {
     4725                                    if (str_replace("_", "-", $an) === str_replace("_", "-", $name)) {
     4726                                        $name = $an;
     4727                                        break;
     4728                                    }
     4729                                }
     4730                            }
     4731                            if ($hasVariable) {
     4732                                $deferredKeywordArgs[$name] = $item;
     4733                            } else {
     4734                                $keywordArgs[$name] = $item;
     4735                            }
    44704736                        } else {
     4737                            if (is_null($splatSeparator)) {
     4738                                $splatSeparator = $val[1];
     4739                            }
    44714740                            $remaining[] = $item;
    44724741                        }
     
    44754744                    $remaining[] = $val;
    44764745                }
     4746            } elseif ($hasKeywordArgument) {
     4747                $this->throwError('Positional arguments must come before keyword arguments.');
     4748                break;
    44774749            } else {
    44784750                $remaining[] = $arg[1];
     
    44844756
    44854757            if ($isVariable) {
    4486                 $val = [Type::T_LIST, ',', [], $isVariable];
     4758                $val = [Type::T_LIST, is_null($splatSeparator) ? ',' : $splatSeparator , [], $isVariable];
    44874759
    44884760                for ($count = count($remaining); $i < $count; $i++) {
     
    45044776            }
    45054777
    4506             $this->set($name, $this->reduce($val, true), true, $env);
    4507         }
    4508 
    4509         $storeEnv->store = $env->store;
     4778            if ($storeInEnv) {
     4779                $this->set($name, $this->reduce($val, true), true, $env);
     4780            } else {
     4781                $output[$name] = ($reduce ? $this->reduce($val, true) : $val);
     4782            }
     4783        }
     4784
     4785        if ($storeInEnv) {
     4786            $storeEnv->store = $env->store;
     4787        }
    45104788
    45114789        foreach ($args as $arg) {
     
    45164794            }
    45174795
    4518             $this->set($name, $this->reduce($default, true), true);
    4519         }
     4796            if ($storeInEnv) {
     4797                $this->set($name, $this->reduce($default, true), true);
     4798            } else {
     4799                $output[$name] = ($reduce ? $this->reduce($default, true) : $default);
     4800            }
     4801        }
     4802
     4803        return $output;
    45204804    }
    45214805
     
    46184902                $value = $values[$i];
    46194903
     4904                switch ($key[0]) {
     4905                    case Type::T_LIST:
     4906                    case Type::T_MAP:
     4907                        break;
     4908
     4909                    default:
     4910                        $key = [Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))];
     4911                        break;
     4912                }
     4913
    46204914                $list[] = [
    46214915                    Type::T_LIST,
    46224916                    '',
    4623                     [[Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))], $value]
     4917                    [$key, $value]
    46244918                ];
    46254919            }
     
    49245218    // Built in functions
    49255219
    4926     //protected static $libCall = ['name', 'args...'];
     5220    protected static $libCall = ['name', 'args...'];
    49275221    protected function libCall($args, $kwargs)
    49285222    {
    49295223        $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true)));
    4930 
    4931         $posArgs = [];
    4932 
    4933         foreach ($args as $arg) {
    4934             if (empty($arg[0])) {
    4935                 if ($arg[2] === true) {
    4936                     $tmp = $this->reduce($arg[1]);
    4937 
    4938                     if ($tmp[0] === Type::T_LIST) {
    4939                         foreach ($tmp[2] as $item) {
    4940                             $posArgs[] = [null, $item, false];
    4941                         }
    4942                     } else {
    4943                         $posArgs[] = [null, $tmp, true];
    4944                     }
    4945 
    4946                     continue;
    4947                 }
    4948 
    4949                 $posArgs[] = [null, $this->reduce($arg), false];
    4950                 continue;
    4951             }
    4952 
    4953             $posArgs[] = [null, $arg, false];
    4954         }
    4955 
    4956         if (count($kwargs)) {
    4957             foreach ($kwargs as $key => $value) {
    4958                 $posArgs[] = [[Type::T_VARIABLE, $key], $value, false];
    4959             }
    4960         }
    4961 
    4962         return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]);
    4963     }
    4964 
    4965     protected static $libIf = ['condition', 'if-true', 'if-false'];
     5224        $callArgs = [];
     5225
     5226        // $kwargs['args'] is [Type::T_LIST, ',', [..]]
     5227        foreach ($kwargs['args'][2] as $varname => $arg) {
     5228            if (is_numeric($varname)) {
     5229                $varname = null;
     5230            } else {
     5231                $varname = [ 'var', $varname];
     5232            }
     5233
     5234            $callArgs[] = [$varname, $arg, false];
     5235        }
     5236
     5237        return $this->reduce([Type::T_FUNCTION_CALL, $name, $callArgs]);
     5238    }
     5239
     5240    protected static $libIf = ['condition', 'if-true', 'if-false:'];
    49665241    protected function libIf($args)
    49675242    {
     
    50165291
    50175292    protected static $libRgba = [
    5018         ['red', 'color'],
    5019         'green', 'blue', 'alpha'];
     5293        ['color', 'alpha:1'],
     5294        ['red', 'green', 'blue', 'alpha:1'] ];
    50205295    protected function libRgba($args)
    50215296    {
     
    50465321        }
    50475322
    5048         if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
     5323        if (! empty($args[4]) || ! empty($args[5]) || ! empty($args[6])) {
    50495324            $hsl = $this->toHSL($color[1], $color[2], $color[3]);
    50505325
    50515326            foreach ([4, 5, 6] as $i) {
    5052                 if (isset($args[$i])) {
     5327                if (! empty($args[$i])) {
    50535328                    $val = $this->assertNumber($args[$i]);
    50545329                    $hsl[$i - 3] = call_user_func($fn, $hsl[$i - 3], $val, $i);
     
    50695344
    50705345    protected static $libAdjustColor = [
    5071         'color', 'red', 'green', 'blue',
    5072         'hue', 'saturation', 'lightness', 'alpha'
     5346        'color', 'red:null', 'green:null', 'blue:null',
     5347        'hue:null', 'saturation:null', 'lightness:null', 'alpha:null'
    50735348    ];
    50745349    protected function libAdjustColor($args)
     
    50805355
    50815356    protected static $libChangeColor = [
    5082         'color', 'red', 'green', 'blue',
    5083         'hue', 'saturation', 'lightness', 'alpha'
     5357        'color', 'red:null', 'green:null', 'blue:null',
     5358        'hue:null', 'saturation:null', 'lightness:null', 'alpha:null'
    50845359    ];
    50855360    protected function libChangeColor($args)
     
    50915366
    50925367    protected static $libScaleColor = [
    5093         'color', 'red', 'green', 'blue',
    5094         'hue', 'saturation', 'lightness', 'alpha'
     5368        'color', 'red:null', 'green:null', 'blue:null',
     5369        'hue:null', 'saturation:null', 'lightness:null', 'alpha:null'
    50955370    ];
    50965371    protected function libScaleColor($args)
     
    51865461
    51875462    // mix two colors
    5188     protected static $libMix = ['color-1', 'color-2', 'weight'];
     5463    protected static $libMix = ['color-1', 'color-2', 'weight:0.5'];
    51895464    protected function libMix($args)
    51905465    {
     
    53085583    }
    53095584
    5310     protected static $libSaturate = ['color', 'amount'];
     5585    protected static $libSaturate = [['color', 'amount'], ['number']];
    53115586    protected function libSaturate($args)
    53125587    {
     
    56005875    {
    56015876        $map = $this->assertMap($args[0]);
    5602         $key = $this->compileStringContent($this->coerceString($args[1]));
    5603 
    5604         for ($i = count($map[1]) - 1; $i >= 0; $i--) {
    5605             if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
    5606                 return $map[2][$i];
     5877        $key = $args[1];
     5878
     5879        if (! is_null($key)) {
     5880            $key = $this->compileStringContent($this->coerceString($key));
     5881            for ($i = count($map[1]) - 1; $i >= 0; $i--) {
     5882                if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
     5883                    return $map[2][$i];
     5884                }
    56075885            }
    56085886        }
     
    57105988
    57115989            case 'space':
    5712                 return '';
     5990                return ' ';
    57135991
    57145992            default:
     
    57175995    }
    57185996
    5719     protected static $libJoin = ['list1', 'list2', 'separator'];
     5997    protected static $libJoin = ['list1', 'list2', 'separator:null'];
    57205998    protected function libJoin($args)
    57215999    {
     
    57296007    }
    57306008
    5731     protected static $libAppend = ['list', 'val', 'separator'];
     6009    protected static $libAppend = ['list', 'val', 'separator:null'];
    57326010    protected function libAppend($args)
    57336011    {
     
    58746152    }
    58756153
    5876     protected static $libStrSlice = ['string', 'start-at', 'end-at'];
     6154    protected static $libStrSlice = ['string', 'start-at', 'end-at:null'];
    58776155    protected function libStrSlice($args)
    58786156    {
     
    61836461    }
    61846462
    6185     //protected static $libSelectorAppend = ['selector...'];
     6463    protected static $libSelectorAppend = ['selector...'];
    61866464    protected function libSelectorAppend($args)
    61876465    {
     6466        // get the selector... list
     6467        $args = reset($args);
     6468        $args = $args[2];
    61886469        if (count($args) < 1) {
    61896470            $this->throwError("selector-append() needs at least 1 argument");
     
    63296610    }
    63306611
    6331     //protected static $libSelectorNest = ['selector...'];
     6612    protected static $libSelectorNest = ['selector...'];
    63326613    protected function libSelectorNest($args)
    63336614    {
     6615        // get the selector... list
     6616        $args = reset($args);
     6617        $args = $args[2];
    63346618        if (count($args) < 1) {
    63356619            $this->throwError("selector-nest() needs at least 1 argument");
  • _plugins_/scssphp/trunk/lib/scssphp/src/Formatter.php

    r115547 r116185  
    127127        }
    128128
    129         if (($count = count($lines))
    130             && substr($lines[$count - 1], -1) === ';'
    131         ) {
     129        if (($count = count($lines)) && substr($lines[$count - 1], -1) === ';') {
    132130            $lines[$count - 1] = substr($lines[$count - 1], 0, -1);
    133131        }
     
    216214            $this->write($pre . $this->close . $this->break);
    217215        }
     216    }
     217
     218    /**
     219     * Test and clean safely empty children
     220     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
     221     * @return bool
     222     */
     223    protected function testEmptyChildren($block)
     224    {
     225        $isEmpty = empty($block->lines);
     226
     227        if ($block->children) {
     228            foreach ($block->children as $k => &$child) {
     229                if (! $this->testEmptyChildren($child)) {
     230                    $isEmpty = false;
     231                } else {
     232                    if ($child->type === Type::T_MEDIA || $child->type === Type::T_DIRECTIVE) {
     233                        $child->children = [];
     234                        $child->selectors = null;
     235                    }
     236                }
     237            }
     238        }
     239        return $isEmpty;
    218240    }
    219241
     
    237259            $this->sourceMapGenerator = $sourceMapGenerator;
    238260        }
     261
     262        $this->testEmptyChildren($block);
    239263
    240264        ob_start();
  • _plugins_/scssphp/trunk/lib/scssphp/src/Formatter/Nested.php

    r115547 r116185  
    1414use ScssPhp\ScssPhp\Formatter;
    1515use ScssPhp\ScssPhp\Formatter\OutputBlock;
     16use ScssPhp\ScssPhp\Type;
    1617
    1718/**
     
    6869
    6970        $this->write($inner . implode($glue, $block->lines));
    70 
    71         if (! empty($block->children)) {
    72             $this->write($this->break);
    73         }
    74     }
    75 
    76     /**
    77      * {@inheritdoc}
    78      */
    79     protected function blockSelectors(OutputBlock $block)
    80     {
    81         $inner = $this->indentStr();
    82 
    83         $this->write($inner
    84             . implode($this->tagSeparator, $block->selectors)
    85             . $this->open . $this->break);
    86     }
    87 
    88     /**
    89      * {@inheritdoc}
    90      */
    91     protected function blockChildren(OutputBlock $block)
    92     {
    93         foreach ($block->children as $i => $child) {
    94             $this->block($child);
    95 
    96             if ($i < count($block->children) - 1) {
    97                 $this->write($this->break);
    98 
    99                 if (isset($block->children[$i + 1])) {
    100                     $next = $block->children[$i + 1];
    101 
    102                     if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) {
    103                         $this->write($this->break);
    104                     }
    105                 }
    106             }
    107         }
     71    }
     72
     73    protected function hasFlatChild($block)
     74    {
     75        foreach ($block->children as $child) {
     76            if (empty($child->selectors)) {
     77                return true;
     78            }
     79        }
     80
     81        return false;
    10882    }
    10983
     
    11387    protected function block(OutputBlock $block)
    11488    {
     89        static $depths;
     90        static $downLevel;
     91        static $closeBlock;
     92        static $previousEmpty;
     93        static $previousHasSelector;
     94
    11595        if ($block->type === 'root') {
    116             $this->adjustAllChildren($block);
     96            $depths = [ 0 ];
     97            $downLevel = '';
     98            $closeBlock = '';
     99            $this->depth = 0;
     100            $previousEmpty = false;
     101            $previousHasSelector = false;
     102        }
     103
     104        $isMediaOrDirective = in_array($block->type, [Type::T_DIRECTIVE, Type::T_MEDIA]);
     105        $isSupport = ($block->type === Type::T_DIRECTIVE
     106            && $block->selectors && strpos(implode('', $block->selectors), '@supports') !== false);
     107
     108        while ($block->depth < end($depths) || ($block->depth == 1 && end($depths) == 1)) {
     109            array_pop($depths);
     110            $this->depth--;
     111
     112            if (!$this->depth && ($block->depth <= 1 || (!$this->indentLevel && $block->type === Type::T_COMMENT)) &&
     113                (($block->selectors && ! $isMediaOrDirective) || $previousHasSelector)
     114            ) {
     115                $downLevel = $this->break;
     116            }
     117
     118            if (empty($block->lines) && empty($block->children)) {
     119                $previousEmpty = true;
     120            }
    117121        }
    118122
     
    123127        $this->currentBlock = $block;
    124128
    125 
    126         $this->depth = $block->depth;
     129        if (! empty($block->lines) || (! empty($block->children) && ($this->depth < 1 || $isSupport))) {
     130            if ($block->depth > end($depths)) {
     131                if (! $previousEmpty || $this->depth < 1) {
     132                    $this->depth++;
     133                    $depths[] = $block->depth;
     134                } else {
     135                    // keep the current depth unchanged but take the block depth as a new reference for following blocks
     136                    array_pop($depths);
     137                    $depths[] = $block->depth;
     138                }
     139            }
     140        }
     141
     142        $previousEmpty = ($block->type === Type::T_COMMENT);
     143        $previousHasSelector = false;
    127144
    128145        if (! empty($block->selectors)) {
     146            if ($closeBlock) {
     147                $this->write($closeBlock);
     148                $closeBlock = '';
     149            }
     150
     151            if ($downLevel) {
     152                $this->write($downLevel);
     153                $downLevel = '';
     154            }
     155
    129156            $this->blockSelectors($block);
    130157
     
    133160
    134161        if (! empty($block->lines)) {
     162            if ($closeBlock) {
     163                $this->write($closeBlock);
     164                $closeBlock = '';
     165            }
     166
     167            if ($downLevel) {
     168                $this->write($downLevel);
     169                $downLevel = '';
     170            }
     171
    135172            $this->blockLines($block);
     173            $closeBlock = $this->break;
    136174        }
    137175
    138176        if (! empty($block->children)) {
    139             $this->blockChildren($block);
     177            if ($this->depth>0 && ($isMediaOrDirective || ! $this->hasFlatChild($block))) {
     178                array_pop($depths);
     179                $this->depth--;
     180                $this->blockChildren($block);
     181                $this->depth++;
     182                $depths[] = $block->depth;
     183            } else {
     184                $this->blockChildren($block);
     185            }
     186        }
     187
     188        // reclear to not be spoiled by children if T_DIRECTIVE
     189        if ($block->type === Type::T_DIRECTIVE) {
     190            $previousHasSelector = false;
    140191        }
    141192
     
    144195
    145196            $this->write($this->close);
     197            $closeBlock = $this->break;
     198
     199            if ($this->depth > 1 && ! empty($block->children)) {
     200                array_pop($depths);
     201                $this->depth--;
     202            }
     203            if (! $isMediaOrDirective) {
     204                $previousHasSelector = true;
     205            }
    146206        }
    147207
     
    150210        }
    151211    }
    152 
    153     /**
    154      * Adjust the depths of all children, depth first
    155      *
    156      * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
    157      */
    158     private function adjustAllChildren(OutputBlock $block)
    159     {
    160         // flatten empty nested blocks
    161         $children = [];
    162 
    163         foreach ($block->children as $i => $child) {
    164             if (empty($child->lines) && empty($child->children)) {
    165                 if (isset($block->children[$i + 1])) {
    166                     $block->children[$i + 1]->depth = $child->depth;
    167                 }
    168 
    169                 continue;
    170             }
    171 
    172             $children[] = $child;
    173         }
    174 
    175         $count = count($children);
    176 
    177         for ($i = 0; $i < $count; $i++) {
    178             $depth = $children[$i]->depth;
    179             $j = $i + 1;
    180 
    181             if (isset($children[$j]) && $depth < $children[$j]->depth) {
    182                 $childDepth = $children[$j]->depth;
    183 
    184                 for (; $j < $count; $j++) {
    185                     if ($depth < $children[$j]->depth && $childDepth >= $children[$j]->depth) {
    186                         $children[$j]->depth = $depth + 1;
    187                     }
    188                 }
    189             }
    190         }
    191 
    192         $block->children = $children;
    193 
    194         // make relative to parent
    195         foreach ($block->children as $child) {
    196             $this->adjustAllChildren($child);
    197 
    198             $child->depth = $child->depth - $block->depth;
    199         }
    200     }
    201212}
  • _plugins_/scssphp/trunk/lib/scssphp/src/Node/Number.php

    r115547 r116185  
    149149        }
    150150
    151         if ($offset === -1
    152             || $offset === 0
    153             || $offset === 1
    154             || $offset === 2
     151        if ($offset === -1 ||
     152            $offset === 0 ||
     153            $offset === 1 ||
     154            $offset === 2
    155155        ) {
    156156            return true;
  • _plugins_/scssphp/trunk/lib/scssphp/src/Parser.php

    r115547 r116185  
    6565    private $inParens;
    6666    private $eatWhiteDefault;
     67    private $discardComments;
    6768    private $buffer;
    6869    private $utf8;
     
    8990        $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais';
    9091        $this->commentsSeen     = [];
     92        $this->discardComments  = false;
    9193
    9294        if (empty(static::$operatorPattern)) {
     
    267269
    268270    /**
     271     * Parse a media Query
     272     *
     273     * @api
     274     *
     275     * @param string $buffer
     276     * @param string $out
     277     *
     278     * @return array
     279     */
     280    public function parseMediaQueryList($buffer, &$out)
     281    {
     282        $this->count           = 0;
     283        $this->env             = null;
     284        $this->inParens        = false;
     285        $this->eatWhiteDefault = true;
     286        $this->buffer          = (string) $buffer;
     287
     288        $this->saveEncoding();
     289
     290
     291        $isMediaQuery = $this->mediaQueryList($out);
     292
     293        $this->restoreEncoding();
     294
     295        return $isMediaQuery;
     296    }
     297
     298    /**
    269299     * Parse a single chunk off the head of the buffer and append it to the
    270300     * current parse environment.
     
    314344                ($this->selectors($selector) || true) &&
    315345                ($this->map($with) || true) &&
    316                 $this->matchChar('{')
     346                $this->matchChar('{', false)
    317347            ) {
    318348                $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
     
    325355            $this->seek($s);
    326356
    327             if ($this->literal('@media', 6) && $this->mediaQueryList($mediaQueryList) && $this->matchChar('{')) {
     357            if ($this->literal('@media', 6) && $this->mediaQueryList($mediaQueryList) && $this->matchChar('{', false)) {
    328358                $media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
    329359                $media->queryList = $mediaQueryList[2];
     
    337367                $this->keyword($mixinName) &&
    338368                ($this->argumentDef($args) || true) &&
    339                 $this->matchChar('{')
     369                $this->matchChar('{', false)
    340370            ) {
    341371                $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
     
    354384                    $this->matchChar(')') || true) &&
    355385                ($this->end() ||
     386                    ($this->literal('using', 5) &&
     387                        $this->argumentDef($argUsing) &&
     388                        ($this->end() || $this->matchChar('{') && $hasBlock = true)) ||
    356389                    $this->matchChar('{') && $hasBlock = true)
    357390            ) {
    358                 $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null];
     391                $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null, isset($argUsing) ? $argUsing : null];
    359392
    360393                if (! empty($hasBlock)) {
     
    419452                $this->keyword($fnName) &&
    420453                $this->argumentDef($args) &&
    421                 $this->matchChar('{')
     454                $this->matchChar('{', false)
    422455            ) {
    423456                $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
     
    458491                $this->literal('in', 2) &&
    459492                $this->valueList($list) &&
    460                 $this->matchChar('{')
     493                $this->matchChar('{', false)
    461494            ) {
    462495                $each = $this->pushSpecialBlock(Type::T_EACH, $s);
     
    475508            if ($this->literal('@while', 6) &&
    476509                $this->expression($cond) &&
    477                 $this->matchChar('{')
     510                $this->matchChar('{', false)
    478511            ) {
    479512                $while = $this->pushSpecialBlock(Type::T_WHILE, $s);
     
    492525                    ($forUntil = true && $this->literal('to', 2))) &&
    493526                $this->expression($end) &&
    494                 $this->matchChar('{')
     527                $this->matchChar('{', false)
    495528            ) {
    496529                $for = $this->pushSpecialBlock(Type::T_FOR, $s);
     
    505538            $this->seek($s);
    506539
    507             if ($this->literal('@if', 3) && $this->valueList($cond) && $this->matchChar('{')) {
     540            if ($this->literal('@if', 3) && $this->valueList($cond) && $this->matchChar('{', false)) {
    508541                $if = $this->pushSpecialBlock(Type::T_IF, $s);
    509542                $if->cond = $cond;
     
    548581            $this->seek($s);
    549582
    550             if ($this->literal('@content', 8) && $this->end()) {
    551                 $this->append([Type::T_MIXIN_CONTENT], $s);
     583            #if ($this->literal('@content', 8))
     584
     585            if ($this->literal('@content', 8) &&
     586                ($this->end() ||
     587                    $this->matchChar('(') &&
     588                        $this->argValues($argContent) &&
     589                        $this->matchChar(')') &&
     590                    $this->end())) {
     591                $this->append([Type::T_MIXIN_CONTENT, isset($argContent) ? $argContent : null], $s);
    552592
    553593                return true;
     
    562602
    563603                if ($this->literal('@else', 5)) {
    564                     if ($this->matchChar('{')) {
     604                    if ($this->matchChar('{', false)) {
    565605                        $else = $this->pushSpecialBlock(Type::T_ELSE, $s);
    566                     } elseif ($this->literal('if', 2) && $this->valueList($cond) && $this->matchChar('{')) {
     606                    } elseif ($this->literal('if', 2) && $this->valueList($cond) && $this->matchChar('{', false)) {
    567607                        $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s);
    568608                        $else->cond = $cond;
     
    602642            $this->seek($s);
    603643
     644            if ($this->literal('@supports', 9) &&
     645                ($t1=$this->supportsQuery($supportQuery)) &&
     646                ($t2=$this->matchChar('{', false)) ) {
     647                $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
     648                $directive->name = 'supports';
     649                $directive->value = $supportQuery;
     650
     651                return true;
     652            }
     653
     654            $this->seek($s);
     655
    604656            // doesn't match built in directive, do generic one
    605657            if ($this->matchChar('@', false) &&
    606658                $this->keyword($dirName) &&
    607659                ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) &&
    608                 $this->matchChar('{')
     660                $this->matchChar('{', false)
    609661            ) {
    610662                if ($dirName === 'media') {
     
    689741            }
    690742
    691             if ($this->matchChar('{')) {
     743            if ($this->matchChar('{', false)) {
    692744                $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
    693745                $propBlock->prefix = $name;
     746                $propBlock->hasValue = $foundSomething;
     747
    694748                $foundSomething = true;
    695749            } elseif ($foundSomething) {
     
    705759
    706760        // closing a block
    707         if ($this->matchChar('}')) {
     761        if ($this->matchChar('}', false)) {
    708762            $block = $this->popBlock();
     763
     764            if (!isset($block->type) || $block->type !== Type::T_IF) {
     765                if ($this->env->parent) {
     766                    $this->append(null); // collect comments before next statement if needed
     767                }
     768            }
    709769
    710770            if (isset($block->type) && $block->type === Type::T_INCLUDE) {
     
    716776                $type = isset($block->type) ? $block->type : Type::T_BLOCK;
    717777                $this->append([$type, $block], $s);
     778            }
     779
     780            // collect comments just after the block closing if needed
     781            if ($this->eatWhiteDefault) {
     782                $this->whitespace();
     783
     784                if ($this->env->comments) {
     785                    $this->append(null);
     786                }
    718787            }
    719788
     
    765834        $this->env = $b;
    766835
     836        // collect comments at the begining of a block if needed
     837        if ($this->eatWhiteDefault) {
     838            $this->whitespace();
     839
     840            if ($this->env->comments) {
     841                $this->append(null);
     842            }
     843        }
     844
    767845        return $b;
    768846    }
     
    793871    protected function popBlock()
    794872    {
     873
     874        // collect comments ending just before of a block closing
     875        if ($this->env->comments) {
     876            $this->append(null);
     877        }
     878
     879        // pop the block
    795880        $block = $this->env;
    796881
     
    807892
    808893        unset($block->parent);
    809 
    810         $comments = $block->comments;
    811 
    812         if ($comments) {
    813             $this->env->comments = $comments;
    814             unset($block->comments);
    815         }
    816894
    817895        return $block;
     
    10501128    protected function appendComment($comment)
    10511129    {
    1052         if ($comment[0] === Type::T_COMMENT && is_string($comment[1])) {
    1053             $comment[1] = substr(preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], $comment[1]), 1);
    1054         }
    1055 
    1056         $this->env->comments[] = $comment;
     1130        if (! $this->discardComments) {
     1131            if ($comment[0] === Type::T_COMMENT && is_string($comment[1])) {
     1132                $comment[1] = substr(preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], $comment[1]), 1);
     1133            }
     1134
     1135            $this->env->comments[] = $comment;
     1136        }
    10571137    }
    10581138
     
    11641244
    11651245    /**
     1246     * Parse supports query
     1247     *
     1248     * @param array $out
     1249     *
     1250     * @return boolean
     1251     */
     1252    protected function supportsQuery(&$out)
     1253    {
     1254        $expressions = null;
     1255        $parts = [];
     1256
     1257        $s = $this->count;
     1258
     1259        $not = false;
     1260        if (($this->literal('not', 3) && ($not = true) || true) &&
     1261            $this->matchChar('(') &&
     1262            ($this->expression($property)) &&
     1263            $this->literal(': ', 2) &&
     1264            $this->valueList($value) &&
     1265            $this->matchChar(')')) {
     1266            $support = [Type::T_STRING, '', [[Type::T_KEYWORD, ($not ? 'not ' : '') . '(']]];
     1267            $support[2][] = $property;
     1268            $support[2][] = [Type::T_KEYWORD, ': '];
     1269            $support[2][] = $value;
     1270            $support[2][] = [Type::T_KEYWORD, ')'];
     1271
     1272            $parts[] = $support;
     1273            $s = $this->count;
     1274        } else {
     1275            $this->seek($s);
     1276        }
     1277
     1278        if ($this->matchChar('(') &&
     1279            $this->supportsQuery($subQuery) &&
     1280            $this->matchChar(')')) {
     1281            $parts[] = [Type::T_STRING, '', [[Type::T_KEYWORD, '('], $subQuery, [Type::T_KEYWORD, ')']]];
     1282            $s = $this->count;
     1283        } else {
     1284            $this->seek($s);
     1285        }
     1286
     1287        if ($this->literal('not', 3) &&
     1288            $this->supportsQuery($subQuery)) {
     1289            $parts[] = [Type::T_STRING, '', [[Type::T_KEYWORD, 'not '], $subQuery]];
     1290            $s = $this->count;
     1291        } else {
     1292            $this->seek($s);
     1293        }
     1294
     1295        if ($this->literal('selector(', 9) &&
     1296            $this->selector($selector) &&
     1297            $this->matchChar(')')) {
     1298            $support = [Type::T_STRING, '', [[Type::T_KEYWORD, 'selector(']]];
     1299
     1300            $selectorList = [Type::T_LIST, '', []];
     1301            foreach ($selector as $sc) {
     1302                $compound = [Type::T_STRING, '', []];
     1303                foreach ($sc as $scp) {
     1304                    if (is_array($scp)) {
     1305                        $compound[2][] = $scp;
     1306                    } else {
     1307                        $compound[2][] = [Type::T_KEYWORD, $scp];
     1308                    }
     1309                }
     1310                $selectorList[2][] = $compound;
     1311            }
     1312            $support[2][] = $selectorList;
     1313            $support[2][] = [Type::T_KEYWORD, ')'];
     1314            $parts[] = $support;
     1315            $s = $this->count;
     1316        } else {
     1317            $this->seek($s);
     1318        }
     1319
     1320        if ($this->variable($var) or $this->interpolation($var)) {
     1321            $parts[] = $var;
     1322            $s = $this->count;
     1323        } else {
     1324            $this->seek($s);
     1325        }
     1326
     1327        if ($this->literal('and', 3) &&
     1328            $this->genericList($expressions, 'supportsQuery', ' and', false)) {
     1329            array_unshift($expressions[2], [Type::T_STRING, '', $parts]);
     1330            $parts = [$expressions];
     1331            $s = $this->count;
     1332        } else {
     1333            $this->seek($s);
     1334        }
     1335
     1336        if ($this->literal('or', 2) &&
     1337            $this->genericList($expressions, 'supportsQuery', ' or', false)) {
     1338            array_unshift($expressions[2], [Type::T_STRING, '', $parts]);
     1339            $parts = [$expressions];
     1340            $s = $this->count;
     1341        } else {
     1342            $this->seek($s);
     1343        }
     1344
     1345        if (count($parts)) {
     1346            if ($this->eatWhiteDefault) {
     1347                $this->whitespace();
     1348            }
     1349            $out = [Type::T_STRING, '', $parts];
     1350            return true;
     1351        }
     1352
     1353        return false;
     1354    }
     1355
     1356
     1357    /**
    11661358     * Parse media expression
    11671359     *
     
    13211513    {
    13221514        $s = $this->count;
     1515        $discard = $this->discardComments;
     1516        $this->discardComments = true;
    13231517
    13241518        if ($this->matchChar('(')) {
    13251519            if ($this->parenExpression($out, $s, ")")) {
     1520                $this->discardComments = $discard;
    13261521                return true;
    13271522            }
     
    13361531                }
    13371532
     1533                $this->discardComments = $discard;
    13381534                return true;
    13391535            }
     
    13451541            $out = $this->expHelper($lhs, 0);
    13461542
    1347             return true;
    1348         }
    1349 
     1543            $this->discardComments = $discard;
     1544            return true;
     1545        }
     1546
     1547        $this->discardComments = $discard;
    13501548        return false;
    13511549    }
     
    19792177                } elseif ($this->literal("\\", 1, false)) {
    19802178                    $content[] = $m[2] . "\\";
    1981                 } elseif ($this->literal("\r\n", 2, false)
    1982                   || $this->matchChar("\r", false)
    1983                   || $this->matchChar("\n", false)
    1984                   || $this->matchChar("\f", false)) {
     2179                } elseif ($this->literal("\r\n", 2, false) ||
     2180                  $this->matchChar("\r", false) ||
     2181                  $this->matchChar("\n", false) ||
     2182                  $this->matchChar("\f", false)
     2183                ) {
    19852184                    // this is a continuation escaping, to be ignored
    19862185                } else {
     
    20232222     * Parse keyword or interpolation
    20242223     *
    2025      * @param array $out
    2026      *
    2027      * @return boolean
    2028      */
    2029     protected function mixedKeyword(&$out)
     2224     * @param array   $out
     2225     * @param boolean $restricted
     2226     *
     2227     * @return boolean
     2228     */
     2229    protected function mixedKeyword(&$out, $restricted = false)
    20302230    {
    20312231        $parts = [];
     
    20352235
    20362236        for (;;) {
    2037             if ($this->keyword($key)) {
     2237            if ($restricted ? $this->restrictedKeyword($key) : $this->keyword($key)) {
    20382238                $parts[] = $key;
    20392239                continue;
     
    21502350            } else {
    21512351                if ($lookWhite) {
    2152                     $left = preg_match('/\s/', $this->buffer[$s - 1]) ? ' ' : '';
     2352                    $left = ($s > 0 && preg_match('/\s/', $this->buffer[$s - 1])) ? ' ' : '';
    21532353                    $right = preg_match('/\s/', $this->buffer[$this->count]) ? ' ': '';
    21542354                } else {
     
    24072607                }
    24082608
    2409                 if ($this->mixedKeyword($nameParts)) {
     2609                if ($this->mixedKeyword($nameParts, true)) {
    24102610                    $parts[] = $part;
    24112611
     
    24192619                        $nameParts === ['has'] || $nameParts === ['where']
    24202620                    ) {
    2421                         if ($this->matchChar('(') &&
     2621                        if ($this->matchChar('(', true) &&
    24222622                          ($this->selectors($subs, true) || true) &&
    24232623                          $this->matchChar(')')
     
    24902690            }
    24912691
    2492             if ($this->keyword($name)) {
     2692            if ($this->restrictedKeyword($name)) {
    24932693                $parts[] = $name;
    24942694                continue;
     
    25572757
    25582758    /**
     2759     * Parse a keyword that should not start with a number
     2760     *
     2761     * @param string  $word
     2762     * @param boolean $eatWhitespace
     2763     *
     2764     * @return boolean
     2765     */
     2766    protected function restrictedKeyword(&$word, $eatWhitespace = null)
     2767    {
     2768        $s = $this->count;
     2769
     2770        if ($this->keyword($word, $eatWhitespace) && (ord($word[0]) > 57 || ord($word[0]) < 48)) {
     2771            return true;
     2772        }
     2773
     2774        $this->seek($s);
     2775
     2776        return false;
     2777    }
     2778
     2779    /**
    25592780     * Parse a placeholder
    25602781     *
  • _plugins_/scssphp/trunk/lib/scssphp/src/Version.php

    r115547 r116185  
    1919class Version
    2020{
    21     const VERSION = 'v1.0.0';
     21    const VERSION = 'v1.0.2';
    2222}
  • _plugins_/scssphp/trunk/paquet.xml

    r115547 r116185  
    22        prefix="scssphp"
    33        categorie="outil"
    4         version="2.1.0"
     4        version="2.1.1"
    55        etat="stable"
    66        compatibilite="[3.1.0;3.2.*]"
     
    2222        <pipeline nom="formulaire_admin" />
    2323
    24         <procure nom="scssphp" version="1.0.0" />
     24        <procure nom="scssphp" version="1.0.2" />
    2525
    2626        <necessite nom="php" compatibilite="[5.6.0;[" />
Note: See TracChangeset for help on using the changeset viewer.