diff --git a/src/Block.php b/src/Block.php index a6ef8e03..2edbc8a7 100644 --- a/src/Block.php +++ b/src/Block.php @@ -62,4 +62,8 @@ class Block * @var array */ public $children; + /** + * @var \Leafo\ScssPhp\Block + */ + public $selfParent; } diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..0dbab028 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -790,6 +790,7 @@ protected function compileAtRoot(Block $block) $wrapped->comments = []; $wrapped->parent = $block; $wrapped->children = $block->children; + $wrapped->selfParent = $block->selfParent; $block->children = [[Type::T_BLOCK, $wrapped]]; } @@ -882,6 +883,7 @@ private function spliceTree($envs, Block $block, $without) $newBlock = $b; } + $newBlock->selfParent = $block->selfParent; $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; return [$type, $newBlock]; @@ -1083,7 +1085,7 @@ protected function compileBlock(Block $block) $this->scope->children[] = $out; if (count($block->children)) { - $out->selectors = $this->multiplySelectors($env); + $out->selectors = $this->multiplySelectors($env, $block->selfParent); $this->compileChildrenNoReturn($block->children, $out); } @@ -2899,15 +2901,21 @@ protected function extractInterpolation($list) * Find the final set of selectors * * @param \Leafo\ScssPhp\Compiler\Environment $env + * @param Leafo\ScssPhp\Block $selfParent * * @return array */ - protected function multiplySelectors(Environment $env) + protected function multiplySelectors(Environment $env, $selfParent = null) { $envs = $this->compactEnv($env); $selectors = []; $parentSelectors = [[]]; + $selfParentSelectors = null; + if (!is_null($selfParent) and $selfParent->selectors) { + $selfParentSelectors = $this->evalSelectors($selfParent->selectors); + } + while ($env = array_pop($envs)) { if (empty($env->selectors)) { continue; @@ -2917,7 +2925,7 @@ protected function multiplySelectors(Environment $env) foreach ($env->selectors as $selector) { foreach ($parentSelectors as $parent) { - $selectors[] = $this->joinSelectors($parent, $selector); + $selectors[] = $this->joinSelectors($parent, $selector, $selfParentSelectors); } } @@ -2932,10 +2940,10 @@ protected function multiplySelectors(Environment $env) * * @param array $parent * @param array $child - * + * @param array $selfParentSelectors * @return array */ - protected function joinSelectors($parent, $child) + protected function joinSelectors($parent, $child, $selfParentSelectors = null) { $setSelf = false; $out = []; @@ -2946,15 +2954,17 @@ protected function joinSelectors($parent, $child) foreach ($part as $p) { if ($p === static::$selfSelector) { $setSelf = true; - - foreach ($parent as $i => $parentPart) { + if (is_null($selfParentSelectors)) { + $selfParentSelectors = $parent; + } + foreach ($selfParentSelectors as $i => $parentPart) { if ($i > 0) { $out[] = $newPart; $newPart = []; } foreach ($parentPart as $pp) { - $newPart[] = $pp; + $newPart[] = (is_array($pp) ? implode($pp) : $pp); } } } else { diff --git a/src/Parser.php b/src/Parser.php index 748d38ae..318b6f3a 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -763,6 +763,11 @@ protected function popBlock() $this->throwParseError('unexpected }'); } + if ($block->type == Type::T_AT_ROOT) { + // keeps the parent in case of self selector & + $block->selfParent = $block->parent; + } + $this->env = $block->parent; unset($block->parent); @@ -1918,6 +1923,20 @@ protected function interpolation(&$out, $lookWhite = true) return true; } + $this->seek($s); + + if ($this->literal('#{') && $this->selectorSingle($sel) && $this->literal('}', false)) { + $out = $sel[0]; + + $this->eatWhiteDefault = $oldWhite; + + if ($this->eatWhiteDefault) { + $this->whitespace(); + } + + return true; + } + $this->seek($s); $this->eatWhiteDefault = $oldWhite; diff --git a/tests/inputs/at_root.scss b/tests/inputs/at_root.scss index 1f00c727..17d4334a 100644 --- a/tests/inputs/at_root.scss +++ b/tests/inputs/at_root.scss @@ -147,3 +147,9 @@ $table-padding: 10px !default; @include table; } } + +.badge { + @at-root a#{&} { + text-decoration: none; + } +} \ No newline at end of file diff --git a/tests/inputs/interpolation.scss b/tests/inputs/interpolation.scss index e3736ca5..f2a266bc 100644 --- a/tests/inputs/interpolation.scss +++ b/tests/inputs/interpolation.scss @@ -97,3 +97,9 @@ $foo: ('a', 'b'); div { prop: #{$foo}; } + +.badge { + a#{&} { + text-decoration: none; + } +} \ No newline at end of file diff --git a/tests/outputs/at_root.css b/tests/outputs/at_root.css index 5383eaab..b0c71a42 100644 --- a/tests/outputs/at_root.css +++ b/tests/outputs/at_root.css @@ -74,3 +74,6 @@ body .second { padding: 0px; } tbody { padding: 10px; } + +a.badge { + text-decoration: none; } diff --git a/tests/outputs/interpolation.css b/tests/outputs/interpolation.css index d3084575..c4e4802d 100644 --- a/tests/outputs/interpolation.css +++ b/tests/outputs/interpolation.css @@ -61,3 +61,6 @@ foo, x, y { div { prop: a, b; } + +a.badge { + text-decoration: none; } diff --git a/tests/outputs_numbered/at_root.css b/tests/outputs_numbered/at_root.css index 2ac5a07e..cb052501 100644 --- a/tests/outputs_numbered/at_root.css +++ b/tests/outputs_numbered/at_root.css @@ -107,3 +107,8 @@ margin: 0; } } /* line 139, inputs/at_root.scss */ tbody { padding: 10px; } +/* line 151, inputs/at_root.scss */ +/* line 152, inputs/at_root.scss */ + +a.badge { + text-decoration: none; } diff --git a/tests/outputs_numbered/interpolation.css b/tests/outputs_numbered/interpolation.css index f6d5f5fa..ae1d7087 100644 --- a/tests/outputs_numbered/interpolation.css +++ b/tests/outputs_numbered/interpolation.css @@ -71,3 +71,8 @@ foo, x, y { /* line 97, inputs/interpolation.scss */ div { prop: a, b; } +/* line 101, inputs/interpolation.scss */ +/* line 102, inputs/interpolation.scss */ + +a.badge { + text-decoration: none; }