From f73a054101715a2cd288658291c7425c39fdcfc8 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 15 Jun 2016 18:20:16 -0400 Subject: [PATCH 001/282] Bump version to v0.6.4 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 8dfa0e93..45db5de9 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.3'; + const VERSION = 'v0.6.4'; } From 1d700dcf86c92144857ccd390cabd51feef4e087 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 15 Jun 2016 18:32:10 -0400 Subject: [PATCH 002/282] fixes #426 keep the un-normalized name for error reporting --- src/Compiler.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 19623e6a..87ffd68f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2938,16 +2938,16 @@ protected function setRaw($name, $value, Environment $env) */ public function get($name, $shouldThrow = true, Environment $env = null) { - $name = $this->normalizeName($name); + $normalizedName = $this->normalizeName($name); if (! isset($env)) { $env = $this->getStoreEnv(); } - $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%'; + $hasNamespace = $normalizedName[0] === '^' || $normalizedName[0] === '@' || $normalizedName[0] === '%'; for (;;) { - if (array_key_exists($name, $env->store)) { - return $env->store[$name]; + if (array_key_exists($normalizedName, $env->store)) { + return $env->store[$normalizedName]; } if (! $hasNamespace && isset($env->marker)) { From b42b2e23128b06f482f8947533f7dfa0c49a3123 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 16 Jun 2016 11:33:35 -0400 Subject: [PATCH 003/282] Compiler: coerceValue support for #rgb values --- src/Compiler.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 87ffd68f..6e5e45dc 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3622,6 +3622,29 @@ private function coerceValue($value) return self::$emptyString; } + if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i', $value, $m)) { + $color = [Type::T_COLOR]; + + if (isset($m[3])) { + $num = hexdec($m[3]); + + foreach ([3, 2, 1] as $i) { + $t = $num & 0xf; + $color[$i] = $t << 4 | $t; + $num >>= 4; + } + } else { + $num = hexdec($m[2]); + + foreach ([3, 2, 1] as $i) { + $color[$i] = $num & 0xff; + $num >>= 8; + } + } + + return $color; + } + return [Type::T_KEYWORD, $value]; } From 1270b5c5437559b07f9237aa11ee035c9a07d997 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 16 Jun 2016 21:28:23 +0300 Subject: [PATCH 004/282] Fix mixin and variable scopes (#440) --- src/Compiler.php | 10 ++++++++ tests/inputs/functions.scss | 38 ++++++++++++++++++++++++++++ tests/inputs/mixins.scss | 18 +++++++++++++ tests/outputs/functions.css | 4 +++ tests/outputs/mixins.css | 7 +++++ tests/outputs_numbered/functions.css | 4 +++ tests/outputs_numbered/mixins.css | 8 ++++++ 7 files changed, 89 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 6e5e45dc..96e71692 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1696,6 +1696,9 @@ protected function compileChild($child, OutputBlock $out) $this->pushEnv(); $this->env->depth--; + $storeEnv = $this->storeEnv; + $this->storeEnv = $this->env; + if (isset($content)) { $content->scope = $callingScope; @@ -1710,6 +1713,8 @@ protected function compileChild($child, OutputBlock $out) $this->compileChildrenNoReturn($mixin->children, $out); + $this->storeEnv = $storeEnv; + $this->popEnv(); break; @@ -3346,6 +3351,9 @@ protected function callScssFunction($name, $argValues, &$returnValue) $this->pushEnv(); + $storeEnv = $this->storeEnv; + $this->storeEnv = $this->env; + // set the args if (isset($func->args)) { $this->applyArguments($func->args, $argValues); @@ -3360,6 +3368,8 @@ protected function callScssFunction($name, $argValues, &$returnValue) $ret = $this->compileChildren($func->children, $tmp); + $this->storeEnv = $storeEnv; + $this->popEnv(); $returnValue = ! isset($ret) ? self::$defaultValue : $ret; diff --git a/tests/inputs/functions.scss b/tests/inputs/functions.scss index bcceaffd..bf3edf1f 100644 --- a/tests/inputs/functions.scss +++ b/tests/inputs/functions.scss @@ -128,3 +128,41 @@ $str: 'global'; l4: inspect((a: 1, b: 2)); l5: inspect($value: (a: 1, b: 2)); } + +@function contains($list, $values...) { + @each $value in $values { + @if type-of(index($list, $value)) != "number" { + @return false; + } + } + + @return true; +} + +@function mapping($items) { + $src: (); + $map: ( + one: 1px 1px, + two: 2px 2px, + ); + + @each $key, $values in $map { + @if contains($items, $key) { + $value1: nth($values, 1); + $value2: nth($values, 2); + + $src: append($src, $value1 $value2, space); + } + } + + @return $src; +} + +@mixin test() { + padding: mapping(one two); +} + +div { + margin: mapping(one two); + @include test(); +} \ No newline at end of file diff --git a/tests/inputs/mixins.scss b/tests/inputs/mixins.scss index 63f9007b..02ed81d0 100644 --- a/tests/inputs/mixins.scss +++ b/tests/inputs/mixins.scss @@ -190,3 +190,21 @@ div.parameter-name-scope { .test{ @include test-mixin(); } + +@mixin inner($value) {} + +@mixin outer($value) { + #{$value} { + content: "#{$value}"; + @content; + content: "#{$value}"; + } +} + +@include outer(div) { + @include inner('break'); + @include outer(p) { + @include inner('break'); + } +} + diff --git a/tests/outputs/functions.css b/tests/outputs/functions.css index 252e3909..aa0bebbb 100644 --- a/tests/outputs/functions.css +++ b/tests/outputs/functions.css @@ -45,3 +45,7 @@ p { l3: 5, 6; l4: (a: 1, b: 2); l5: (a: 1, b: 2); } + +div { + margin: 1px 1px 2px 2px; + padding: 1px 1px 2px 2px; } diff --git a/tests/outputs/mixins.css b/tests/outputs/mixins.css index e2004a07..ee692574 100644 --- a/tests/outputs/mixins.css +++ b/tests/outputs/mixins.css @@ -95,3 +95,10 @@ div.parameter-name-scope { @media screen and (min-width:0\0) { .test { color: #000; } } + +div { + content: "div"; + content: "div"; } + div p { + content: "p"; + content: "p"; } diff --git a/tests/outputs_numbered/functions.css b/tests/outputs_numbered/functions.css index 9b525e8d..6583ff05 100644 --- a/tests/outputs_numbered/functions.css +++ b/tests/outputs_numbered/functions.css @@ -47,3 +47,7 @@ p { l3: 5, 6; l4: (a: 1, b: 2); l5: (a: 1, b: 2); } +/* line 165, inputs/functions.scss */ +div { + margin: 1px 1px 2px 2px; + padding: 1px 1px 2px 2px; } diff --git a/tests/outputs_numbered/mixins.css b/tests/outputs_numbered/mixins.css index 2df05142..ed1f4ffb 100644 --- a/tests/outputs_numbered/mixins.css +++ b/tests/outputs_numbered/mixins.css @@ -108,3 +108,11 @@ div.parameter-name-scope { @media screen and (min-width:0\0) { .test { color: #000; } } +/* line 197, inputs/mixins.scss */ +div { + content: "div"; + content: "div"; } +/* line 197, inputs/mixins.scss */ +div p { + content: "p"; + content: "p"; } From 24388fafc2e9e9d997da7ca753336b15022b4148 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 20 Jun 2016 11:18:53 -0400 Subject: [PATCH 005/282] fixes #444 - strip optional BOM (byte order marker) --- src/Parser.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 3cdd2c7e..ed0621ea 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -142,11 +142,16 @@ public function throwParseError($msg = 'parse error') */ public function parse($buffer) { + // strip BOM (byte order marker) + if (substr($buffer, 0, 3) === "\xef\xbb\xbf") { + $buffer = substr($buffer, 3); + } + + $this->buffer = rtrim($buffer, "\x00..\x1f"); $this->count = 0; $this->env = null; $this->inParens = false; $this->eatWhiteDefault = true; - $this->buffer = rtrim($buffer, "\x00..\x1f"); $this->saveEncoding(); $this->extractLineNumbers($buffer); From 588ebe29c5dd5c0308584fc5b758798c63f10fba Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 20 Jun 2016 11:24:44 -0400 Subject: [PATCH 006/282] #444 - add related unit test --- tests/ApiTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index ca5f3a28..8d89b588 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -105,6 +105,15 @@ public function provideSetVariables() ); } + public function testCompileByteOrderMarker() + { + // test that BOM is stripped/ignored + $this->assertEquals( + '@import "main";', + $this->compile("\xEF\xBB\xBF@import \"main\";") + ); + } + public function compile($str) { return trim($this->scss->compile($str)); From 0649d38dfef6808be1a89040a3312e8bda0b3aed Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 20 Jun 2016 11:25:08 -0400 Subject: [PATCH 007/282] Bump version to v0.6.5 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 45db5de9..3be9cc31 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.4'; + const VERSION = 'v0.6.5'; } From e9400176899e3e10c7f3da06e79599d82ca22ae4 Mon Sep 17 00:00:00 2001 From: Alexssssss Date: Thu, 23 Jun 2016 16:50:55 +0200 Subject: [PATCH 008/282] Preg_match error array > string When a null value is given to coerceValue the following error is returned: ''' _WARNING [2]: preg_match() expects parameter 2 to be string, array given /vendor/leafo/scssphp/src/Compiler.php, line: 3635 ''' This change fixes that for me. --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 96e71692..7e93fcb0 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3621,7 +3621,7 @@ private function coerceValue($value) } if ($value === null) { - $value = self::$null; + return self::$null; } if (is_numeric($value)) { From 1149b762d93204e0d4a3a42866acd6b3ce2edc67 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Tue, 12 Jul 2016 20:44:47 +0800 Subject: [PATCH 009/282] Resolve function arguments using mixin content scope --- src/Compiler.php | 8 +++++++ tests/inputs/nested_mixins.scss | 28 ++++++++++++++++++++++++ tests/outputs/nested_mixins.css | 9 ++++++++ tests/outputs_numbered/nested_mixins.css | 14 ++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 tests/inputs/nested_mixins.scss create mode 100644 tests/outputs/nested_mixins.css create mode 100644 tests/outputs_numbered/nested_mixins.css diff --git a/src/Compiler.php b/src/Compiler.php index 7e93fcb0..485e0fd4 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2944,11 +2944,13 @@ protected function setRaw($name, $value, Environment $env) public function get($name, $shouldThrow = true, Environment $env = null) { $normalizedName = $this->normalizeName($name); + $specialContentKey = self::$namespaces['special'] . 'content'; if (! isset($env)) { $env = $this->getStoreEnv(); } + $nextIsRoot = false; $hasNamespace = $normalizedName[0] === '^' || $normalizedName[0] === '@' || $normalizedName[0] === '%'; for (;;) { if (array_key_exists($normalizedName, $env->store)) { @@ -2956,6 +2958,12 @@ public function get($name, $shouldThrow = true, Environment $env = null) } if (! $hasNamespace && isset($env->marker)) { + if (! $nextIsRoot && !empty($env->store[$specialContentKey])) { + $env = $env->store[$specialContentKey]->scope; + $nextIsRoot = true; + continue; + } + $env = $this->rootEnv; continue; } diff --git a/tests/inputs/nested_mixins.scss b/tests/inputs/nested_mixins.scss new file mode 100644 index 00000000..f72ce365 --- /dev/null +++ b/tests/inputs/nested_mixins.scss @@ -0,0 +1,28 @@ +@mixin container() { + .container { + @content; + } +} +@mixin add-size($size) { + width: $size; +} +@function add($a, $b) { + @return $a + $b; +} + +div { + @include container() { + $i: 10px; + @include container() { + width: $i; + max-width: add($i, 2px); + } + @include add-size($i); + .foo { + @include add-size($i); + } + .bar { + @include add-size(add($i, 2px)); + } + } +} diff --git a/tests/outputs/nested_mixins.css b/tests/outputs/nested_mixins.css new file mode 100644 index 00000000..432580b8 --- /dev/null +++ b/tests/outputs/nested_mixins.css @@ -0,0 +1,9 @@ +div .container { + width: 10px; } + div .container .container { + width: 10px; + max-width: 12px; } + div .container .foo { + width: 10px; } + div .container .bar { + width: 12px; } diff --git a/tests/outputs_numbered/nested_mixins.css b/tests/outputs_numbered/nested_mixins.css new file mode 100644 index 00000000..ae9c6029 --- /dev/null +++ b/tests/outputs_numbered/nested_mixins.css @@ -0,0 +1,14 @@ +/* line 13, inputs/nested_mixins.scss */ +/* line 2, inputs/nested_mixins.scss */ + div .container { + width: 10px; } +/* line 2, inputs/nested_mixins.scss */ +div .container .container { + width: 10px; + max-width: 12px; } +/* line 21, inputs/nested_mixins.scss */ +div .container .foo { + width: 10px; } +/* line 24, inputs/nested_mixins.scss */ +div .container .bar { + width: 12px; } From 3e1b715325f85cf357c18c0c66856ec117b19dd8 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Fri, 22 Jul 2016 15:19:21 +0800 Subject: [PATCH 010/282] Nesting with immediate relationship combinators --- src/Compiler.php | 57 +++++++++++++++++++--- tests/inputs/extends.scss | 18 +++++++ tests/inputs/extends_nesting.scss | 44 +++++++++++++++++ tests/outputs/extends.css | 3 ++ tests/outputs/extends_nesting.css | 28 +++++++++++ tests/outputs_numbered/extends.css | 10 ++++ tests/outputs_numbered/extends_nesting.css | 37 ++++++++++++++ 7 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 tests/inputs/extends_nesting.scss create mode 100644 tests/outputs/extends_nesting.css create mode 100644 tests/outputs_numbered/extends_nesting.css diff --git a/src/Compiler.php b/src/Compiler.php index 485e0fd4..c1c14617 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -397,23 +397,46 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) } if ($this->matchExtendsSingle($part, $origin)) { - $before = array_slice($selector, 0, $i); + $after = array_slice($selector, $i + 1); - $s = count($before); + $j = $i; + $s = $i; + do { + $nonBreakableBefore = $j != $i ? array_slice($selector, $j, $i - $j) : []; + $before = array_slice($selector, 0, $j); + $slice = end($before); + $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); + if ($hasImmediateRelationshipCombinator) { + $j -= 2; + } + } while ($hasImmediateRelationshipCombinator); foreach ($origin as $new) { $k = 0; // remove shared parts if ($initial) { - while ($k < $s && isset($new[$k]) && $before[$k] === $new[$k]) { + while ($k < $s && isset($new[$k]) && $selector[$k] === $new[$k]) { $k++; } } + $replacement = []; + $tempReplacement = $k > 0 ? array_slice($new, $k) : $new; + for ($l = count($tempReplacement) - 1; $l >= 0 ; $l--) { + $slice = $tempReplacement[$l]; + array_unshift($replacement, $slice); + if (!$this->isImmediateRelationshipCombinator(end($slice))) { + break; + } + } + $afterBefore = $l != 0 ? array_slice($tempReplacement, 0, $l) : []; + $result = array_merge( $before, - $k > 0 ? array_slice($new, $k) : $new, + $afterBefore, + $nonBreakableBefore, + $replacement, $after ); @@ -424,14 +447,22 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $out[] = $result; // recursively check for more matches - $this->matchExtends($result, $out, $i, false); + $this->matchExtends($result, $out, count($before) + count($afterBefore) + count($nonBreakableBefore), false); // selector sequence merging if (! empty($before) && count($new) > 1) { + $slice = !empty($afterBefore) ? end($afterBefore) : null; + + if ($slice && $this->isImmediateRelationshipCombinator(end($slice))) { + continue; + } + $result2 = array_merge( - array_slice($new, 0, -1), + $k > 0 ? array_slice($before, 0, $k) : [], + $afterBefore, $k > 0 ? array_slice($before, $k) : $before, - array_slice($new, -1), + $nonBreakableBefore, + $replacement, $after ); @@ -1808,6 +1839,18 @@ protected function isTruthy($value) return $value !== self::$false && $value !== self::$null; } + /** + * Is the value a direct relationship combinator? + * + * @param string $value + * + * @return bool + */ + protected function isImmediateRelationshipCombinator($value) + { + return $value === '>' || $value === '+' || $value === '~'; + } + /** * Should $value cause its operand to eval * diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index 467d3624..dbff56fc 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -305,3 +305,21 @@ $color-base: white; @extend %pagination-bullet; } } + +@mixin nested-foo() { + .in-nested-foo { + @content; + } +} + +.parent-nested-foo-include { + @include nested-foo() { + .child-nested-foo-include { + color: green; + } + } +} + +.beard + .mustache { + @extend .child-nested-foo-include; +} diff --git a/tests/inputs/extends_nesting.scss b/tests/inputs/extends_nesting.scss new file mode 100644 index 00000000..0cae18dc --- /dev/null +++ b/tests/inputs/extends_nesting.scss @@ -0,0 +1,44 @@ +.btn { + padding: 1px; +} +.btn-lg { + font-size: 10px; +} +.dropup .btn-lg .caret { + border-width: 100px; +} + +.dropup .btn { + content: "dropup btn"; + .caret { + content: "dropup btn caret"; + } +} +.dropup > .btn { + content: "dropup > btn"; + > .caret { + content: "dropup > btn > caret"; + } +} +.dropup + .btn { + content: "dropup + btn"; + + .caret { + content: "dropup + btn + caret"; + } +} +.dropup.btn { + content: "dropupbtn"; + &.caret { + content: "dropupbtncaret"; + } +} + +.btn-group-lg > .btn { + @extend .btn-lg; +} + +.extend { + .the-button { + @extend .btn; + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index 8e2916a3..7ced1cad 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -138,3 +138,6 @@ body .to-extend, body .test { margin-left: 0; } .pagination-bullet.is-active { background-color: white; } + +.parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { + color: green; } diff --git a/tests/outputs/extends_nesting.css b/tests/outputs/extends_nesting.css new file mode 100644 index 00000000..42329481 --- /dev/null +++ b/tests/outputs/extends_nesting.css @@ -0,0 +1,28 @@ +.btn, .extend .the-button { + padding: 1px; } + +.btn-lg, .btn-group-lg > .btn, .extend .btn-group-lg > .the-button { + font-size: 10px; } + +.dropup .btn-lg .caret, .dropup .btn-group-lg > .btn .caret, .dropup .extend .btn-group-lg > .the-button .caret, .extend .dropup .btn-group-lg > .the-button .caret { + border-width: 100px; } + +.dropup .btn, .dropup .extend .the-button, .extend .dropup .the-button { + content: "dropup btn"; } + .dropup .btn .caret, .dropup .extend .the-button .caret, .extend .dropup .the-button .caret { + content: "dropup btn caret"; } + +.dropup > .btn, .extend .dropup > .the-button { + content: "dropup > btn"; } + .dropup > .btn > .caret, .extend .dropup > .the-button > .caret { + content: "dropup > btn > caret"; } + +.dropup + .btn, .extend .dropup + .the-button { + content: "dropup + btn"; } + .dropup + .btn + .caret, .extend .dropup + .the-button + .caret { + content: "dropup + btn + caret"; } + +.dropup.btn, .extend .the-button.dropup { + content: "dropupbtn"; } + .dropup.btn.caret, .extend .the-button.dropup.caret { + content: "dropupbtncaret"; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index e32f01b7..51297cb4 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -215,3 +215,13 @@ body .to-extend, body .test { background-color: white; } /* line 303, inputs/extends.scss */ /* line 304, inputs/extends.scss */ + +/* line 315, inputs/extends.scss */ + +/* line 310, inputs/extends.scss */ + +/* line 317, inputs/extends.scss */ + +.parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { + color: green; } +/* line 323, inputs/extends.scss */ diff --git a/tests/outputs_numbered/extends_nesting.css b/tests/outputs_numbered/extends_nesting.css new file mode 100644 index 00000000..08c3027d --- /dev/null +++ b/tests/outputs_numbered/extends_nesting.css @@ -0,0 +1,37 @@ +/* line 1, inputs/extends_nesting.scss */ +.btn, .extend .the-button { + padding: 1px; } +/* line 4, inputs/extends_nesting.scss */ +.btn-lg, .btn-group-lg > .btn, .extend .btn-group-lg > .the-button { + font-size: 10px; } +/* line 7, inputs/extends_nesting.scss */ +.dropup .btn-lg .caret, .dropup .btn-group-lg > .btn .caret, .dropup .extend .btn-group-lg > .the-button .caret, .extend .dropup .btn-group-lg > .the-button .caret { + border-width: 100px; } +/* line 11, inputs/extends_nesting.scss */ +.dropup .btn, .dropup .extend .the-button, .extend .dropup .the-button { + content: "dropup btn"; } +/* line 13, inputs/extends_nesting.scss */ +.dropup .btn .caret, .dropup .extend .the-button .caret, .extend .dropup .the-button .caret { + content: "dropup btn caret"; } +/* line 17, inputs/extends_nesting.scss */ +.dropup > .btn, .extend .dropup > .the-button { + content: "dropup > btn"; } +/* line 19, inputs/extends_nesting.scss */ +.dropup > .btn > .caret, .extend .dropup > .the-button > .caret { + content: "dropup > btn > caret"; } +/* line 23, inputs/extends_nesting.scss */ +.dropup + .btn, .extend .dropup + .the-button { + content: "dropup + btn"; } +/* line 25, inputs/extends_nesting.scss */ +.dropup + .btn + .caret, .extend .dropup + .the-button + .caret { + content: "dropup + btn + caret"; } +/* line 29, inputs/extends_nesting.scss */ +.dropup.btn, .extend .the-button.dropup { + content: "dropupbtn"; } +/* line 31, inputs/extends_nesting.scss */ +.dropup.btn.caret, .extend .the-button.dropup.caret { + content: "dropupbtncaret"; } +/* line 36, inputs/extends_nesting.scss */ +/* line 40, inputs/extends_nesting.scss */ + +/* line 41, inputs/extends_nesting.scss */ From 62536ec1b32b63499684a91d67e8b6d90508bad2 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Mon, 25 Jul 2016 19:12:10 +0800 Subject: [PATCH 011/282] Shared direct relationship are merged on extend --- src/Compiler.php | 96 +++++++++++++++++++++++------- tests/inputs/extends.scss | 20 +++++++ tests/outputs/extends.css | 10 ++++ tests/outputs_numbered/extends.css | 15 +++++ 4 files changed, 119 insertions(+), 22 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index c1c14617..d9dcc6e8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -399,24 +399,15 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) if ($this->matchExtendsSingle($part, $origin)) { $after = array_slice($selector, $i + 1); - $j = $i; - $s = $i; - do { - $nonBreakableBefore = $j != $i ? array_slice($selector, $j, $i - $j) : []; - $before = array_slice($selector, 0, $j); - $slice = end($before); - $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); - if ($hasImmediateRelationshipCombinator) { - $j -= 2; - } - } while ($hasImmediateRelationshipCombinator); + $before = array_slice($selector, 0, $i); + list($before, $nonBreakableBefore) = $this->extractRelationshipFromFragment($before); foreach ($origin as $new) { $k = 0; // remove shared parts if ($initial) { - while ($k < $s && isset($new[$k]) && $selector[$k] === $new[$k]) { + while ($k < $i && isset($new[$k]) && $selector[$k] === $new[$k]) { $k++; } } @@ -432,10 +423,12 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) } $afterBefore = $l != 0 ? array_slice($tempReplacement, 0, $l) : []; + // Merge shared direct relationships. + $mergedBefore = $this->mergeDirectRelationships($afterBefore, $nonBreakableBefore); + $result = array_merge( $before, - $afterBefore, - $nonBreakableBefore, + $mergedBefore, $replacement, $after ); @@ -447,20 +440,20 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $out[] = $result; // recursively check for more matches - $this->matchExtends($result, $out, count($before) + count($afterBefore) + count($nonBreakableBefore), false); + $this->matchExtends($result, $out, count($before) + count($mergedBefore), false); // selector sequence merging if (! empty($before) && count($new) > 1) { - $slice = !empty($afterBefore) ? end($afterBefore) : null; - if ($slice && $this->isImmediateRelationshipCombinator(end($slice))) { - continue; - } + $sharedParts = $k > 0 ? array_slice($before, 0, $k) : []; + $postSharedParts = $k > 0 ? array_slice($before, $k) : $before; + list($injectBetweenSharedParts, $nonBreakable2) = $this->extractRelationshipFromFragment($afterBefore); $result2 = array_merge( - $k > 0 ? array_slice($before, 0, $k) : [], - $afterBefore, - $k > 0 ? array_slice($before, $k) : $before, + $sharedParts, + $injectBetweenSharedParts, + $postSharedParts, + $nonBreakable2, $nonBreakableBefore, $replacement, $after @@ -543,6 +536,37 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) return $found; } + + /** + * Extract a relationship from the fragment. + * + * When extracting the last portion of a selector we will be left with a + * fragment which may end with a direction relationship combinator. This + * method will extract the relationship fragment and return it along side + * the rest. + * + * @param array $fragment The selector fragment maybe ending with a direction relationship combinator. + * @return array The selector without the relationship fragment if any, the relationship fragment. + */ + protected function extractRelationshipFromFragment(array $fragment) + { + $parents = []; + $children = []; + $j = $i = count($fragment); + + do { + $children = $j != $i ? array_slice($fragment, $j, $i - $j) : []; + $parents = array_slice($fragment, 0, $j); + $slice = end($parents); + $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); + if ($hasImmediateRelationshipCombinator) { + $j -= 2; + } + } while ($hasImmediateRelationshipCombinator); + + return [$parents, $children]; + } + /** * Combine selector single * @@ -1315,6 +1339,34 @@ protected function compileMediaQuery($queryList) return $out; } + protected function mergeDirectRelationships($selectors1, $selectors2) { + if (empty($selectors1) || empty($selectors2)) { + return array_merge($selectors1, $selectors2); + } + + $part1 = end($selectors1); + $part2 = end($selectors2); + if (!$this->isImmediateRelationshipCombinator($part1[0]) || $part1 !== $part2) { + return array_merge($selectors1, $selectors2); + } + + $merged = []; + do { + $part1 = array_pop($selectors1); + $part2 = array_pop($selectors2); + + if ($this->isImmediateRelationshipCombinator($part1[0]) && $part1 === $part2) { + array_unshift($merged, $part1); + array_unshift($merged, [array_pop($selectors1)[0] . array_pop($selectors2)[0]]); + } else { + $merged = array_merge($selectors1, [$part1], $selectors2, [$part2], $merged); + break; + } + } while (!empty($selectors1) && !empty($selectors2)); + + return $merged; + } + /** * Merge media types * diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index dbff56fc..1024c93d 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -323,3 +323,23 @@ $color-base: white; .beard + .mustache { @extend .child-nested-foo-include; } + +// Shared direct relationship combinator. +.btn-group-lg > .btn { + @extend .btn-lg; +} +.btn-group > .btn-lg { + padding-right: 12px; + padding-left: 12px; +} + +// Reverse rules with direct relationship combinators. +.fp-content-center form + div { + @extend .form-group; + padding-left: 0; +} +.form-inline + .a { + .form-group > b { + margin-bottom: 0; + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index 7ced1cad..bc953bb8 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -141,3 +141,13 @@ body .to-extend, body .test { .parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { color: green; } + +.btn-group > .btn-lg, .btn-group-lg.btn-group > .btn, .edit .actions .btn-group-lg.btn-group > button, .edit .new .actions .btn-group-lg.btn-group > button { + padding-right: 12px; + padding-left: 12px; } + +.fp-content-center form + div { + padding-left: 0; } + +.form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { + margin-bottom: 0; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index 51297cb4..c64df92a 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -225,3 +225,18 @@ body .to-extend, body .test { .parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { color: green; } /* line 323, inputs/extends.scss */ +/* line 328, inputs/extends.scss */ + +/* line 331, inputs/extends.scss */ + +.btn-group > .btn-lg, .btn-group-lg.btn-group > .btn, .edit .actions .btn-group-lg.btn-group > button, .edit .new .actions .btn-group-lg.btn-group > button { + padding-right: 12px; + padding-left: 12px; } +/* line 337, inputs/extends.scss */ +.fp-content-center form + div { + padding-left: 0; } +/* line 341, inputs/extends.scss */ +/* line 342, inputs/extends.scss */ + +.form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { + margin-bottom: 0; } From b7675976a298570c27d875b51043a632fb0358c3 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Tue, 26 Jul 2016 18:52:58 +0800 Subject: [PATCH 012/282] Do not extend decorated tags with another tag --- src/Compiler.php | 18 +++++++++++++++-- tests/inputs/extends.scss | 32 ++++++++++++++++++++++++++++++ tests/outputs/extends.css | 12 +++++++++++ tests/outputs_numbered/extends.css | 22 ++++++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index d9dcc6e8..d008a844 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -395,7 +395,6 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) if ($i < $from) { continue; } - if ($this->matchExtendsSingle($part, $origin)) { $after = array_slice($selector, $i + 1); @@ -492,6 +491,12 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) } } + $extendingDecoratedTag = false; + if (count($single) > 1) { + $matches = null; + $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i', $single[0], $matches) ? $matches[0] : false; + } + foreach ($single as $part) { if (isset($this->extendsMap[$part])) { foreach ($this->extendsMap[$part] as $idx) { @@ -521,7 +526,16 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) return false; } - $combined = $this->combineSelectorSingle(end($new), $rem); + $replacement = end($new); + + // Extending a decorated tag with another tag is not possible. + if ($extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag + && preg_match('/^[a-z0-9]+$/i', $replacement[0])) { + unset($origin[$j]); + continue; + } + + $combined = $this->combineSelectorSingle($replacement, $rem); if (count(array_diff($combined, $origin[$j][count($origin[$j]) - 1]))) { $origin[$j][count($origin[$j]) - 1] = $combined; diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index 1024c93d..f9ea2a30 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -343,3 +343,35 @@ $color-base: white; margin-bottom: 0; } } + +// Decorated tags cannot be extended by tags of another type. +canvas { + color: yellow; +} +.popup { + color: red; +} +div.popup { + color: blue; +} +.before span.popup .after { + color: green; +} +input { + @extend .popup; +} +img.bar { + @extend .popup; +} +object { + @extend canvas; +} +embed.bar { + @extend canvas; +} +.prepend { + input.foo, + div.foo { + @extend .popup + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index bc953bb8..6a8dd855 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -151,3 +151,15 @@ body .to-extend, body .test { .form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { margin-bottom: 0; } + +canvas, object, embed.bar { + color: yellow; } + +.popup, input, img.bar, .prepend input.foo, .prepend div.foo { + color: red; } + +div.popup, .prepend div.foo { + color: blue; } + +.before span.popup .after { + color: green; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index c64df92a..aa4c20ac 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -240,3 +240,25 @@ body .to-extend, body .test { .form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { margin-bottom: 0; } +/* line 348, inputs/extends.scss */ +canvas, object, embed.bar { + color: yellow; } +/* line 351, inputs/extends.scss */ +.popup, input, img.bar, .prepend input.foo, .prepend div.foo { + color: red; } +/* line 354, inputs/extends.scss */ +div.popup, .prepend div.foo { + color: blue; } +/* line 357, inputs/extends.scss */ +.before span.popup .after { + color: green; } +/* line 360, inputs/extends.scss */ +/* line 363, inputs/extends.scss */ + +/* line 366, inputs/extends.scss */ + +/* line 369, inputs/extends.scss */ + +/* line 372, inputs/extends.scss */ + +/* line 373, inputs/extends.scss */ From f7c9a494de90ce13db84fb90cca13bf24dc60eb3 Mon Sep 17 00:00:00 2001 From: Dan Diemer Date: Tue, 23 Aug 2016 11:06:04 -0400 Subject: [PATCH 013/282] =?UTF-8?q?Let=20@content=20work=20when=20a=20bloc?= =?UTF-8?q?k=20isn=E2=80=99t=20passed=20in.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Compiler.php | 4 +++- tests/inputs/content.scss | 9 +++++++++ tests/outputs/content.css | 4 ++++ tests/outputs_numbered/content.css | 4 ++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7e93fcb0..07efa95a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1723,7 +1723,9 @@ protected function compileChild($child, OutputBlock $out) ?: $this->get(self::$namespaces['special'] . 'content', false, $this->env); if (! $content) { - $this->throwError('Expected @content inside of mixin'); + $content = new \stdClass(); + $content->scope = new \stdClass(); + $content->children = $this->storeEnv->parent->block->children; break; } diff --git a/tests/inputs/content.scss b/tests/inputs/content.scss index a9c00c46..0a16ae2f 100644 --- a/tests/inputs/content.scss +++ b/tests/inputs/content.scss @@ -116,3 +116,12 @@ A { display: none; } } + +@mixin empty_content() { + @content; +} +.test_empty_content { + display: inline; + font-weight: bold; + @include empty_content() +} diff --git a/tests/outputs/content.css b/tests/outputs/content.css index 6a8ba90b..6b24aca3 100644 --- a/tests/outputs/content.css +++ b/tests/outputs/content.css @@ -36,3 +36,7 @@ A { .test { display: none; } + +.test_empty_content { + display: inline; + font-weight: bold; } diff --git a/tests/outputs_numbered/content.css b/tests/outputs_numbered/content.css index a0bf6e1e..9d49e316 100644 --- a/tests/outputs_numbered/content.css +++ b/tests/outputs_numbered/content.css @@ -45,3 +45,7 @@ A { /* line 114, inputs/content.scss */ .test { display: none; } +/* line 123, inputs/content.scss */ +.test_empty_content { + display: inline; + font-weight: bold; } From e82bdfb9a41778288871a3f11cbffff6a304e6ce Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 10 Sep 2016 21:23:35 -0400 Subject: [PATCH 014/282] coding style tweaks --- src/Compiler.php | 52 +++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 01442e18..592f53b9 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -395,10 +395,11 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) if ($i < $from) { continue; } - if ($this->matchExtendsSingle($part, $origin)) { + if ($this->matchExtendsSingle($part, $origin)) { $after = array_slice($selector, $i + 1); $before = array_slice($selector, 0, $i); + list($before, $nonBreakableBefore) = $this->extractRelationshipFromFragment($before); foreach ($origin as $new) { @@ -413,13 +414,16 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $replacement = []; $tempReplacement = $k > 0 ? array_slice($new, $k) : $new; - for ($l = count($tempReplacement) - 1; $l >= 0 ; $l--) { + + for ($l = count($tempReplacement) - 1; $l >= 0; $l--) { $slice = $tempReplacement[$l]; array_unshift($replacement, $slice); - if (!$this->isImmediateRelationshipCombinator(end($slice))) { + + if (! $this->isImmediateRelationshipCombinator(end($slice))) { break; } } + $afterBefore = $l != 0 ? array_slice($tempReplacement, 0, $l) : []; // Merge shared direct relationships. @@ -443,9 +447,9 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) // selector sequence merging if (! empty($before) && count($new) > 1) { - $sharedParts = $k > 0 ? array_slice($before, 0, $k) : []; $postSharedParts = $k > 0 ? array_slice($before, $k) : $before; + list($injectBetweenSharedParts, $nonBreakable2) = $this->extractRelationshipFromFragment($afterBefore); $result2 = array_merge( @@ -492,6 +496,7 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) } $extendingDecoratedTag = false; + if (count($single) > 1) { $matches = null; $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i', $single[0], $matches) ? $matches[0] : false; @@ -529,8 +534,9 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) $replacement = end($new); // Extending a decorated tag with another tag is not possible. - if ($extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag - && preg_match('/^[a-z0-9]+$/i', $replacement[0])) { + if ($extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag && + preg_match('/^[a-z0-9]+$/i', $replacement[0]) + ) { unset($origin[$j]); continue; } @@ -568,15 +574,17 @@ protected function extractRelationshipFromFragment(array $fragment) $children = []; $j = $i = count($fragment); - do { + for (;;) { $children = $j != $i ? array_slice($fragment, $j, $i - $j) : []; $parents = array_slice($fragment, 0, $j); $slice = end($parents); - $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); - if ($hasImmediateRelationshipCombinator) { - $j -= 2; + + if (empty($slice) || ! $this->isImmediateRelationshipCombinator($slice[0])) { + break; } - } while ($hasImmediateRelationshipCombinator); + + $j -= 2; + } return [$parents, $children]; } @@ -1353,30 +1361,33 @@ protected function compileMediaQuery($queryList) return $out; } - protected function mergeDirectRelationships($selectors1, $selectors2) { + protected function mergeDirectRelationships($selectors1, $selectors2) + { if (empty($selectors1) || empty($selectors2)) { return array_merge($selectors1, $selectors2); } $part1 = end($selectors1); $part2 = end($selectors2); - if (!$this->isImmediateRelationshipCombinator($part1[0]) || $part1 !== $part2) { + + if (! $this->isImmediateRelationshipCombinator($part1[0]) || $part1 !== $part2) { return array_merge($selectors1, $selectors2); } $merged = []; + do { $part1 = array_pop($selectors1); $part2 = array_pop($selectors2); - if ($this->isImmediateRelationshipCombinator($part1[0]) && $part1 === $part2) { - array_unshift($merged, $part1); - array_unshift($merged, [array_pop($selectors1)[0] . array_pop($selectors2)[0]]); - } else { + if ($this->isImmediateRelationshipCombinator($part1[0]) && $part1 !== $part2) { $merged = array_merge($selectors1, [$part1], $selectors2, [$part2], $merged); break; } - } while (!empty($selectors1) && !empty($selectors2)); + + array_unshift($merged, $part1); + array_unshift($merged, [array_pop($selectors1)[0] . array_pop($selectors2)[0]]); + } while (! empty($selectors1) && ! empty($selectors2)); return $merged; } @@ -1720,7 +1731,7 @@ protected function compileChild($child, OutputBlock $out) $end = $end[1]; $d = $start < $end ? 1 : -1; - while (true) { + for (;;) { if ((! $for->until && $start - $d == $end) || ($for->until && $start == $end) ) { @@ -3063,13 +3074,14 @@ public function get($name, $shouldThrow = true, Environment $env = null) $nextIsRoot = false; $hasNamespace = $normalizedName[0] === '^' || $normalizedName[0] === '@' || $normalizedName[0] === '%'; + for (;;) { if (array_key_exists($normalizedName, $env->store)) { return $env->store[$normalizedName]; } if (! $hasNamespace && isset($env->marker)) { - if (! $nextIsRoot && !empty($env->store[$specialContentKey])) { + if (! $nextIsRoot && ! empty($env->store[$specialContentKey])) { $env = $env->store[$specialContentKey]->scope; $nextIsRoot = true; continue; From 6fdfe19d2b13a3f12ba0792227f0718809ce4e4d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 10 Sep 2016 21:34:11 -0400 Subject: [PATCH 015/282] bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 3be9cc31..80cdeee5 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.5'; + const VERSION = 'v0.6.6'; } From 8533a50c10f511ad63d03664d17dedd4cce29859 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 11 Sep 2016 00:15:21 -0400 Subject: [PATCH 016/282] v0.6.6 fixes #199 --- tests/FailingTest.php | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index 3cdf0a04..86865049 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -84,32 +84,6 @@ public function provideFailing() background: #eee; } .nav-bar > .item, header ul > .item, header ul > li { margin: 0 10px; } -END_OF_EXPECTED - ), - array( - '#199 - issue with selectors', <<<'END_OF_SCSS' -.abc { - color: #ddd; -} - -a.abc:hover { - text-decoration: underline; -} - -small { - @extend .abc; - font-weight: italic; -} -END_OF_SCSS - , << Date: Fri, 4 Nov 2016 14:37:08 -0400 Subject: [PATCH 017/282] fixes #471 - list interpolation --- src/Compiler.php | 33 ++++++++++++++++++++++++ tests/inputs/interpolation.scss | 6 +++++ tests/outputs/interpolation.css | 3 +++ tests/outputs_numbered/interpolation.css | 3 +++ 4 files changed, 45 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 592f53b9..f0eaff78 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2723,6 +2723,39 @@ public function compileValue($value) $reduced = $this->reduce($exp); switch ($reduced[0]) { + case Type::T_LIST: + $reduced = $this->extractInterpolation($reduced); + + if ($reduced[0] !== Type::T_LIST) { + break; + } + + list(, $delim, $items) = $reduced; + + if ($delim !== ' ') { + $delim .= ' '; + } + + $filtered = []; + + foreach ($items as $item) { + if ($item[0] === Type::T_NULL) { + continue; + } + + $temp = $this->compileValue([Type::T_KEYWORD, $item]); + if ($temp[0] === Type::T_STRING) { + $filtered[] = $this->compileStringContent($temp); + } elseif ($temp[0] === Type::T_KEYWORD) { + $filtered[] = $temp[1]; + } else { + $filtered[] = $this->compileValue($temp); + } + } + + $reduced = [Type::T_KEYWORD, implode("$delim", $filtered)]; + break; + case Type::T_STRING: $reduced = [Type::T_KEYWORD, $this->compileStringContent($reduced)]; break; diff --git a/tests/inputs/interpolation.scss b/tests/inputs/interpolation.scss index e4df9c84..e3736ca5 100644 --- a/tests/inputs/interpolation.scss +++ b/tests/inputs/interpolation.scss @@ -91,3 +91,9 @@ body{ foo, #{x, y} { color: #abc; } + +// interpolate lists +$foo: ('a', 'b'); +div { + prop: #{$foo}; +} diff --git a/tests/outputs/interpolation.css b/tests/outputs/interpolation.css index ebe8463d..d3084575 100644 --- a/tests/outputs/interpolation.css +++ b/tests/outputs/interpolation.css @@ -58,3 +58,6 @@ body { foo, x, y { color: #abc; } + +div { + prop: a, b; } diff --git a/tests/outputs_numbered/interpolation.css b/tests/outputs_numbered/interpolation.css index 64ef9ebc..f6d5f5fa 100644 --- a/tests/outputs_numbered/interpolation.css +++ b/tests/outputs_numbered/interpolation.css @@ -68,3 +68,6 @@ body { /* line 91, inputs/interpolation.scss */ foo, x, y { color: #abc; } +/* line 97, inputs/interpolation.scss */ +div { + prop: a, b; } From 10c650529b5c2f72094cc885eca327ead519e8c6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 8 Nov 2016 13:17:17 -0500 Subject: [PATCH 018/282] fixes #476 - pscss: enable --line-numbers and --debug-info for stdin --- bin/pscss | 4 ++-- src/Compiler.php | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bin/pscss b/bin/pscss index 3175ea0b..4ec061b8 100755 --- a/bin/pscss +++ b/bin/pscss @@ -169,11 +169,11 @@ if ($dumpTree) { $scss = new Compiler(); -if ($debugInfo && $inputFile) { +if ($debugInfo) { $scss->setLineNumberStyle(Compiler::DEBUG_INFO); } -if ($lineNumbers && $inputFile) { +if ($lineNumbers) { $scss->setLineNumberStyle(Compiler::LINE_COMMENTS); } diff --git a/src/Compiler.php b/src/Compiler.php index f0eaff78..fdb68240 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1015,12 +1015,15 @@ protected function compileBlock(Block $block) switch ($this->lineNumberStyle) { case self::LINE_COMMENTS: - $annotation->lines[] = '/* line ' . $line . ', ' . $file . ' */'; + $annotation->lines[] = '/* line ' . $line + . ($file ? ', ' . $file : '') + . ' */'; break; case self::DEBUG_INFO: - $annotation->lines[] = '@media -sass-debug-info{filename{font-family:"' . $file - . '"}line{font-family:' . $line . '}}'; + $annotation->lines[] = '@media -sass-debug-info{' + . ($file ? 'filename{font-family:"' . $file . '"}' : '') + . 'line{font-family:' . $line . '}}'; break; } From f3bb6b7de4a11c10ad6f5e0e922b9197e962e404 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 22 Nov 2016 15:21:33 -0500 Subject: [PATCH 019/282] Change self:: to static:: where possible --- src/Compiler.php | 98 ++++++++++++++++++++++----------------------- src/Node/Number.php | 10 ++--- src/Parser.php | 38 +++++++++--------- src/Server.php | 2 +- 4 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index fdb68240..df322273 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -729,7 +729,7 @@ protected function compileAtRoot(Block $block) { $env = $this->pushEnv($block); $envs = $this->compactEnv($env); - $without = isset($block->with) ? $this->compileWith($block->with) : self::WITH_RULE; + $without = isset($block->with) ? $this->compileWith($block->with) : static::WITH_RULE; // wrap inline selector if ($block->selector) { @@ -854,12 +854,12 @@ private function compileWith($with) ]; // exclude selectors by default - $without = self::WITH_RULE; + $without = static::WITH_RULE; - if ($this->libMapHasKey([$with, self::$with])) { - $without = self::WITH_ALL; + if ($this->libMapHasKey([$with, static::$with])) { + $without = static::WITH_ALL; - $list = $this->coerceList($this->libMapGet([$with, self::$with])); + $list = $this->coerceList($this->libMapGet([$with, static::$with])); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -870,10 +870,10 @@ private function compileWith($with) } } - if ($this->libMapHasKey([$with, self::$without])) { + if ($this->libMapHasKey([$with, static::$without])) { $without = 0; - $list = $this->coerceList($this->libMapGet([$with, self::$without])); + $list = $this->coerceList($this->libMapGet([$with, static::$without])); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -920,10 +920,10 @@ private function filterWithout($envs, $without) */ private function isWithout($without, Block $block) { - if ((($without & self::WITH_RULE) && isset($block->selectors)) || - (($without & self::WITH_MEDIA) && + if ((($without & static::WITH_RULE) && isset($block->selectors)) || + (($without & static::WITH_MEDIA) && isset($block->type) && $block->type === Type::T_MEDIA) || - (($without & self::WITH_SUPPORTS) && + (($without & static::WITH_SUPPORTS) && isset($block->type) && $block->type === Type::T_DIRECTIVE && isset($block->name) && $block->name === 'supports') ) { @@ -1014,13 +1014,13 @@ protected function compileBlock(Block $block) $line = $block->sourceLine; switch ($this->lineNumberStyle) { - case self::LINE_COMMENTS: + case static::LINE_COMMENTS: $annotation->lines[] = '/* line ' . $line . ($file ? ', ' . $file : '') . ' */'; break; - case self::DEBUG_INFO: + case static::DEBUG_INFO: $annotation->lines[] = '@media -sass-debug-info{' . ($file ? 'filename{font-family:"' . $file . '"}' : '') . 'line{font-family:' . $line . '}}'; @@ -1583,7 +1583,7 @@ protected function compileChild($child, OutputBlock $out) $shouldSet = $isDefault && (($result = $this->get($name[1], false)) === null - || $result === self::$null); + || $result === static::$null); if (! $isDefault || $shouldSet) { $this->set($name[1], $this->reduce($value)); @@ -1611,7 +1611,7 @@ protected function compileChild($child, OutputBlock $out) if ($value[0] !== Type::T_NULL) { $value = $this->reduce($value); - if ($value[0] === Type::T_NULL || $value === self::$nullString) { + if ($value[0] === Type::T_NULL || $value === static::$nullString) { break; } } @@ -1637,7 +1637,7 @@ protected function compileChild($child, OutputBlock $out) case Type::T_FUNCTION: list(, $block) = $child; - $this->set(self::$namespaces[$block->type] . $block->name, $block); + $this->set(static::$namespaces[$block->type] . $block->name, $block); break; case Type::T_EXTEND: @@ -1685,7 +1685,7 @@ protected function compileChild($child, OutputBlock $out) list(,, $values) = $this->coerceList($item); foreach ($each->vars as $i => $var) { - $this->set($var, isset($values[$i]) ? $values[$i] : self::$null, true); + $this->set($var, isset($values[$i]) ? $values[$i] : static::$null, true); } } @@ -1794,7 +1794,7 @@ protected function compileChild($child, OutputBlock $out) // including a mixin list(, $name, $argValues, $content) = $child; - $mixin = $this->get(self::$namespaces['mixin'] . $name, false); + $mixin = $this->get(static::$namespaces['mixin'] . $name, false); if (! $mixin) { $this->throwError("Undefined mixin $name"); @@ -1813,7 +1813,7 @@ protected function compileChild($child, OutputBlock $out) if (isset($content)) { $content->scope = $callingScope; - $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->env); + $this->setRaw(static::$namespaces['special'] . 'content', $content, $this->env); } if (isset($mixin->args)) { @@ -1830,8 +1830,8 @@ protected function compileChild($child, OutputBlock $out) break; case Type::T_MIXIN_CONTENT: - $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv()) - ?: $this->get(self::$namespaces['special'] . 'content', false, $this->env); + $content = $this->get(static::$namespaces['special'] . 'content', false, $this->getStoreEnv()) + ?: $this->get(static::$namespaces['special'] . 'content', false, $this->env); if (! $content) { $content = new \stdClass(); @@ -1918,7 +1918,7 @@ protected function expToString($exp) */ protected function isTruthy($value) { - return $value !== self::$false && $value !== self::$null; + return $value !== static::$false && $value !== static::$null; } /** @@ -1973,7 +1973,7 @@ protected function reduce($value, $inExp = false) case Type::T_EXPRESSION: list(, $op, $left, $right, $inParens) = $value; - $opName = isset(self::$operatorNames[$op]) ? self::$operatorNames[$op] : $op; + $opName = isset(static::$operatorNames[$op]) ? static::$operatorNames[$op] : $op; $inExp = $inExp || $this->shouldEval($left) || $this->shouldEval($right); $left = $this->reduce($left, true); @@ -2089,11 +2089,11 @@ protected function reduce($value, $inExp = false) if ($op === 'not') { if ($inExp || $inParens) { - if ($exp === self::$false || $exp === self::$null) { - return self::$true; + if ($exp === static::$false || $exp === static::$null) { + return static::$true; } - return self::$false; + return static::$false; } $op = $op . ' '; @@ -2347,7 +2347,7 @@ protected function opAnd($left, $right, $shouldEval) return; } - if ($left !== self::$false and $left !== self::$null) { + if ($left !== static::$false and $left !== static::$null) { return $this->reduce($right, true); } @@ -2369,7 +2369,7 @@ protected function opOr($left, $right, $shouldEval) return; } - if ($left !== self::$false and $left !== self::$null) { + if ($left !== static::$false and $left !== static::$null) { return $left; } @@ -2600,7 +2600,7 @@ protected function opCmpNumberNumber($left, $right) */ public function toBool($thing) { - return $thing ? self::$true : self::$false; + return $thing ? static::$true : static::$false; } /** @@ -2883,7 +2883,7 @@ protected function joinSelectors($parent, $child) $newPart = []; foreach ($part as $p) { - if ($p === self::$selfSelector) { + if ($p === static::$selfSelector) { $setSelf = true; foreach ($parent as $i => $parentPart) { @@ -3102,7 +3102,7 @@ protected function setRaw($name, $value, Environment $env) public function get($name, $shouldThrow = true, Environment $env = null) { $normalizedName = $this->normalizeName($name); - $specialContentKey = self::$namespaces['special'] . 'content'; + $specialContentKey = static::$namespaces['special'] . 'content'; if (! isset($env)) { $env = $this->getStoreEnv(); @@ -3510,7 +3510,7 @@ protected function fileExists($name) */ protected function callScssFunction($name, $argValues, &$returnValue) { - $func = $this->get(self::$namespaces['function'] . $name, false); + $func = $this->get(static::$namespaces['function'] . $name, false); if (! $func) { return false; @@ -3539,7 +3539,7 @@ protected function callScssFunction($name, $argValues, &$returnValue) $this->popEnv(); - $returnValue = ! isset($ret) ? self::$defaultValue : $ret; + $returnValue = ! isset($ret) ? static::$defaultValue : $ret; return true; } @@ -3563,7 +3563,7 @@ protected function callNativeFunction($name, $args, &$returnValue) list($f, $prototype) = $this->userFunctions[$name]; } elseif (($f = $this->getBuiltinFunction($name)) && is_callable($f)) { $libName = $f[1]; - $prototype = isset(self::$$libName) ? self::$$libName : null; + $prototype = isset(static::$$libName) ? static::$$libName : null; } else { return false; } @@ -3788,7 +3788,7 @@ private function coerceValue($value) } if ($value === null) { - return self::$null; + return static::$null; } if (is_numeric($value)) { @@ -3796,7 +3796,7 @@ private function coerceValue($value) } if ($value === '') { - return self::$emptyString; + return static::$emptyString; } if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i', $value, $m)) { @@ -3838,11 +3838,11 @@ protected function coerceMap($item) return $item; } - if ($item === self::$emptyList) { - return self::$emptyMap; + if ($item === static::$emptyList) { + return static::$emptyMap; } - return [Type::T_MAP, [$item], [self::$null]]; + return [Type::T_MAP, [$item], [static::$null]]; } /** @@ -4212,7 +4212,7 @@ protected function libIndex($args) list($list, $value) = $args; if ($value[0] === Type::T_MAP) { - return self::$null; + return static::$null; } if ($list[0] === Type::T_MAP || @@ -4224,7 +4224,7 @@ protected function libIndex($args) } if ($list[0] !== Type::T_LIST) { - return self::$null; + return static::$null; } $values = []; @@ -4235,7 +4235,7 @@ protected function libIndex($args) $key = array_search($this->normalizeValue($value), $values); - return false === $key ? self::$null : $key + 1; + return false === $key ? static::$null : $key + 1; } protected static $libRgb = ['red', 'green', 'blue']; @@ -4805,7 +4805,7 @@ protected function libNth($args) $n += count($list[2]); } - return isset($list[2][$n]) ? $list[2][$n] : self::$defaultValue; + return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue; } protected static $libSetNth = ['list', 'n', 'value']; @@ -4843,7 +4843,7 @@ protected function libMapGet($args) } } - return self::$null; + return static::$null; } protected static $libMapKeys = ['map']; @@ -4994,7 +4994,7 @@ protected function libTypeOf($args) switch ($value[0]) { case Type::T_KEYWORD: - if ($value === self::$true || $value === self::$false) { + if ($value === static::$true || $value === static::$false) { return 'bool'; } @@ -5067,7 +5067,7 @@ protected function libStrIndex($args) $result = strpos($stringContent, $substringContent); - return $result === false ? self::$null : new Node\Number($result + 1, ''); + return $result === false ? static::$null : new Node\Number($result + 1, ''); } protected static $libStrInsert = ['string', 'insert', 'index']; @@ -5099,7 +5099,7 @@ protected function libStrLength($args) protected function libStrSlice($args) { if (isset($args[2]) && $args[2][1] == 0) { - return self::$nullString; + return static::$nullString; } $string = $this->coerceString($args[0]); @@ -5161,7 +5161,7 @@ protected function libFunctionExists($args) $name = $this->compileStringContent($string); // user defined functions - if ($this->has(self::$namespaces['function'] . $name)) { + if ($this->has(static::$namespaces['function'] . $name)) { return true; } @@ -5192,7 +5192,7 @@ protected function libMixinExists($args) $string = $this->coerceString($args[0]); $name = $this->compileStringContent($string); - return $this->has(self::$namespaces['mixin'] . $name); + return $this->has(static::$namespaces['mixin'] . $name); } protected static $libVariableExists = ['name']; @@ -5250,7 +5250,7 @@ protected function libUniqueId() protected static $libInspect = ['value']; protected function libInspect($args) { - if ($args[0] === self::$null) { + if ($args[0] === static::$null) { return [Type::T_KEYWORD, 'null']; } diff --git a/src/Node/Number.php b/src/Node/Number.php index a803a6ca..55fec292 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -110,7 +110,7 @@ public function coerce($units) $dimension = $this->dimension; - foreach (self::$unitTable['in'] as $unit => $conv) { + foreach (static::$unitTable['in'] as $unit => $conv) { $from = isset($this->units[$unit]) ? $this->units[$unit] : 0; $to = isset($units[$unit]) ? $units[$unit] : 0; $factor = pow($conv, $from - $to); @@ -265,7 +265,7 @@ public function unitStr() */ public function output(Compiler $compiler = null) { - $dimension = round($this->dimension, self::$precision); + $dimension = round($this->dimension, static::$precision); $units = array_filter($this->units, function ($unitSize) { return $unitSize; @@ -277,7 +277,7 @@ public function output(Compiler $compiler = null) $this->normalizeUnits($dimension, $units, 'in'); - $dimension = round($dimension, self::$precision); + $dimension = round($dimension, static::$precision); $units = array_filter($units, function ($unitSize) { return $unitSize; }); @@ -316,8 +316,8 @@ private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in') $units = []; foreach ($this->units as $unit => $exp) { - if (isset(self::$unitTable[$baseUnit][$unit])) { - $factor = pow(self::$unitTable[$baseUnit][$unit], $exp); + if (isset(static::$unitTable[$baseUnit][$unit])) { + $factor = pow(static::$unitTable[$baseUnit][$unit], $exp); $unit = $baseUnit; $dimension /= $factor; diff --git a/src/Parser.php b/src/Parser.php index ed0621ea..9f28a591 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -83,17 +83,17 @@ public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8') $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais'; - if (empty(self::$operatorPattern)) { - self::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; + if (empty(static::$operatorPattern)) { + static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; $commentSingle = '\/\/'; $commentMultiLeft = '\/\*'; $commentMultiRight = '\*\/'; - self::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight; - self::$whitePattern = $this->utf8 - ? '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisuS' - : '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisS'; + static::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight; + static::$whitePattern = $this->utf8 + ? '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisuS' + : '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisS'; } } @@ -563,9 +563,9 @@ protected function parseChunk() list($line, $column) = $this->getSourcePosition($s); - $statement[self::SOURCE_LINE] = $line; - $statement[self::SOURCE_COLUMN] = $column; - $statement[self::SOURCE_INDEX] = $this->sourceIndex; + $statement[static::SOURCE_LINE] = $line; + $statement[static::SOURCE_COLUMN] = $column; + $statement[static::SOURCE_INDEX] = $this->sourceIndex; $this->charset = $statement; } @@ -922,7 +922,7 @@ protected function whitespace() { $gotWhite = false; - while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { + while (preg_match(static::$whitePattern, $this->buffer, $m, null, $this->count)) { if (isset($m[1]) && empty($this->commentsSeen[$this->count])) { $this->appendComment([Type::T_COMMENT, $m[1]]); @@ -959,9 +959,9 @@ protected function append($statement, $pos = null) if ($pos !== null) { list($line, $column) = $this->getSourcePosition($pos); - $statement[self::SOURCE_LINE] = $line; - $statement[self::SOURCE_COLUMN] = $column; - $statement[self::SOURCE_INDEX] = $this->sourceIndex; + $statement[static::SOURCE_LINE] = $line; + $statement[static::SOURCE_COLUMN] = $column; + $statement[static::SOURCE_INDEX] = $this->sourceIndex; } $this->env->children[] = $statement; @@ -1249,13 +1249,13 @@ protected function expression(&$out) */ protected function expHelper($lhs, $minP) { - $operators = self::$operatorPattern; + $operators = static::$operatorPattern; $ss = $this->seek(); $whiteBefore = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); - while ($this->match($operators, $m, false) && self::$precedence[$m[1]] >= $minP) { + while ($this->match($operators, $m, false) && static::$precedence[$m[1]] >= $minP) { $whiteAfter = isset($this->buffer[$this->count]) && ctype_space($this->buffer[$this->count]); $varAfter = isset($this->buffer[$this->count]) && @@ -1275,8 +1275,8 @@ protected function expHelper($lhs, $minP) } // peek and see if rhs belongs to next operator - if ($this->peek($operators, $next) && self::$precedence[$next[1]] > self::$precedence[$op]) { - $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); + if ($this->peek($operators, $next) && static::$precedence[$next[1]] > static::$precedence[$op]) { + $rhs = $this->expHelper($rhs, static::$precedence[$next[1]]); } $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter]; @@ -1812,7 +1812,7 @@ protected function openString($end, &$out, $nestingOpen = null) $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - $patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . self::$commentPattern . ')'; + $patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . static::$commentPattern . ')'; $nestingLevel = 0; @@ -1946,7 +1946,7 @@ protected function propertyName(&$out) // match comment hack if (preg_match( - self::$whitePattern, + static::$whitePattern, $this->buffer, $m, null, diff --git a/src/Server.php b/src/Server.php index 221655ca..33d7b3de 100644 --- a/src/Server.php +++ b/src/Server.php @@ -457,7 +457,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) */ public static function serveFrom($path) { - $server = new self($path); + $server = new static($path); $server->serve(); } } From bacbd7a0c03fa8af6bdba4fff41ffc13625608fa Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Jan 2017 12:56:38 -0500 Subject: [PATCH 020/282] fixes #484 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index df322273..10fee440 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3496,7 +3496,7 @@ protected function handleImportLoop($name) */ protected function fileExists($name) { - return is_file($name); + return file_exists($name) && is_file($name); } /** From fe7e36b4250c916bf7e94f1e0ec8384fef75d617 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Jan 2017 13:02:45 -0500 Subject: [PATCH 021/282] fixes #485 throw RangeException --- src/Exception/RangeException.php | 21 +++++++++++++++++++++ src/Util.php | 5 +++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/Exception/RangeException.php diff --git a/src/Exception/RangeException.php b/src/Exception/RangeException.php new file mode 100644 index 00000000..22f46f54 --- /dev/null +++ b/src/Exception/RangeException.php @@ -0,0 +1,21 @@ + + */ +class RangeException extends \Exception +{ +} diff --git a/src/Util.php b/src/Util.php index 9f47c1d7..9e968d8a 100644 --- a/src/Util.php +++ b/src/Util.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Base\Range; +use Leafo\ScssPhp\Exception\RangeException; /** * Utilties @@ -31,7 +32,7 @@ class Util * * @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin. * - * @throws \Exception + * @throws \Leafo\ScssPhp\Exception\RangeException */ public static function checkRange($name, Range $range, $value, $unit = '') { @@ -50,6 +51,6 @@ public static function checkRange($name, Range $range, $value, $unit = '') return $range->last; } - throw new \Exception("$name {$val} must be between {$range->first} and {$range->last}$unit"); + throw new RangeException("$name {$val} must be between {$range->first} and {$range->last}$unit"); } } From e7aa77e74f31ab51dd72d95b40b6b5fc2ef46b9f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Jan 2017 13:05:38 -0500 Subject: [PATCH 022/282] Bump copyright year --- src/Base/Range.php | 2 +- src/Block.php | 2 +- src/Colors.php | 2 +- src/Compiler.php | 2 +- src/Compiler/Environment.php | 2 +- src/Exception/CompilerException.php | 2 +- src/Exception/ParserException.php | 2 +- src/Exception/RangeException.php | 2 +- src/Exception/ServerException.php | 2 +- src/Formatter.php | 2 +- src/Formatter/Compact.php | 2 +- src/Formatter/Compressed.php | 2 +- src/Formatter/Crunched.php | 2 +- src/Formatter/Debug.php | 2 +- src/Formatter/Expanded.php | 2 +- src/Formatter/Nested.php | 2 +- src/Formatter/OutputBlock.php | 2 +- src/Node.php | 2 +- src/Node/Number.php | 2 +- src/Parser.php | 2 +- src/Server.php | 2 +- src/Type.php | 2 +- src/Util.php | 2 +- src/Version.php | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Base/Range.php b/src/Base/Range.php index a591d7b0..3f3a067d 100644 --- a/src/Base/Range.php +++ b/src/Base/Range.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2015 Leaf Corcoran + * @copyright 2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Block.php b/src/Block.php index 6b972a1b..a6b5f3f0 100644 --- a/src/Block.php +++ b/src/Block.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Colors.php b/src/Colors.php index ff48c199..61a71ab6 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Compiler.php b/src/Compiler.php index 10fee440..36a0f7cc 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php index d44bff76..b4b86e32 100644 --- a/src/Compiler/Environment.php +++ b/src/Compiler/Environment.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/CompilerException.php b/src/Exception/CompilerException.php index 777e40ac..45fc1671 100644 --- a/src/Exception/CompilerException.php +++ b/src/Exception/CompilerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ParserException.php b/src/Exception/ParserException.php index fbe6388f..c0ee002d 100644 --- a/src/Exception/ParserException.php +++ b/src/Exception/ParserException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/RangeException.php b/src/Exception/RangeException.php index 22f46f54..47192ff5 100644 --- a/src/Exception/RangeException.php +++ b/src/Exception/RangeException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php index 5a878d2f..68d3a290 100644 --- a/src/Exception/ServerException.php +++ b/src/Exception/ServerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter.php b/src/Formatter.php index 2770bb2d..0d2635f1 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index 94abe329..aaf972b7 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index df7bc75f..17aca54a 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index ccba1333..0a89bc48 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 855742e7..321c5368 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 54db742f..dff17e6b 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 9fdb4dd0..c4ff803c 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index bb8d99b4..88e86402 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node.php b/src/Node.php index e6ed178c..eb543c73 100644 --- a/src/Node.php +++ b/src/Node.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node/Number.php b/src/Node/Number.php index 55fec292..d4f406fa 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Parser.php b/src/Parser.php index 9f28a591..619b7ba8 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Server.php b/src/Server.php index 33d7b3de..e4caddb4 100644 --- a/src/Server.php +++ b/src/Server.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Type.php b/src/Type.php index 8c3886c8..2d659e4d 100644 --- a/src/Type.php +++ b/src/Type.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Util.php b/src/Util.php index 9e968d8a..b2a05db8 100644 --- a/src/Util.php +++ b/src/Util.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Version.php b/src/Version.php index 80cdeee5..dff3bd5b 100644 --- a/src/Version.php +++ b/src/Version.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * From d395f6f914e77031bbb822d55bc802e85441056e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 10 Jan 2017 11:45:37 -0500 Subject: [PATCH 023/282] @import - implicit .scss extension when not specified; otherwise limit to explicit .css and .scss extension; mitigates #487 --- src/Compiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 36a0f7cc..68003b90 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3390,6 +3390,8 @@ public function findImport($url) $urls = [$url, preg_replace('/[^\/]+$/', '_\0', $url)]; } + $hasExtension = preg_match('/[.]s?css$/', $url); + foreach ($this->importPaths as $dir) { if (is_string($dir)) { // check urls for normal import paths @@ -3399,7 +3401,7 @@ public function findImport($url) . $full; if ($this->fileExists($file = $full . '.scss') || - $this->fileExists($file = $full) + ($hasExtension && $this->fileExists($file = $full)) ) { return $file; } From 562213cd803e42ea53b0735554794c4022d8db89 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 00:07:33 -0500 Subject: [PATCH 024/282] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index dff3bd5b..2d6eabc1 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.6'; + const VERSION = 'v0.6.7'; } From ad205b8b7cdb066283a7cd2fd4b66fe55131cdef Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 00:32:54 -0500 Subject: [PATCH 025/282] Server::serveFrom() removed --- src/Server.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Server.php b/src/Server.php index e4caddb4..c3fa985d 100644 --- a/src/Server.php +++ b/src/Server.php @@ -449,15 +449,4 @@ public function __construct($dir, $cacheDir = null, $scss = null) date_default_timezone_set('UTC'); } } - - /** - * Helper method to serve compiled scss - * - * @param string $path Root path - */ - public static function serveFrom($path) - { - $server = new static($path); - $server->serve(); - } } From 3dc8ecfbf4900f2cc7b740ab98c8244d232a03d6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 00:59:07 -0500 Subject: [PATCH 026/282] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e5b70fb7..1a28f1ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: - 5.5 - 5.6 - 7.0 + - 7.1 - nightly script: From d2cfeb60986784c7c09257486b00f35c49b8c99c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 01:05:26 -0500 Subject: [PATCH 027/282] Update travis.yml Disable php nightly (7.2) - broken build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1a28f1ea..25e06b3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ php: - 5.6 - 7.0 - 7.1 - - nightly +# - nightly script: - phpunit tests From b5db19b0dd98807279cea399917b38487ed4e8ba Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 24 Feb 2017 20:55:56 -0500 Subject: [PATCH 028/282] Add test for user function that returns null --- tests/ApiTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 8d89b588..f39e7f62 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -38,6 +38,18 @@ public function testUserFunction() ); } + public function testUserFunctionNull() + { + $this->scss->registerFunction('get-null', function ($args) { + return Compiler::$null; + }); + + $this->assertEquals( + '', + $this->compile('result: get-null();') + ); + } + public function testUserFunctionKwargs() { $this->scss->registerFunction( From df07dde450669bc04081a7afb3ee68b8429c37fd Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 07:04:41 -0400 Subject: [PATCH 029/282] Fix travis ci tests? --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 25e06b3e..df8c6adc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,10 @@ php: - 5.6 - 7.0 - 7.1 -# - nightly + - nightly script: - - phpunit tests + - vendor/bin/phpunit tests branches: only: From 2b7ed3384a66ac5a69846c3d2e0eb204fb6cef34 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 07:10:22 -0400 Subject: [PATCH 030/282] Travis: run composer install --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index df8c6adc..e0e9a57f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ php: - nightly script: + - composer install - vendor/bin/phpunit tests branches: From 3814a55a4efe56ac33eb586b97845a8e88093225 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 08:28:16 -0400 Subject: [PATCH 031/282] fixes #505 - remove .phar build --- .gitignore | 2 +- Makefile | 3 --- box.json.dist | 12 ------------ composer.json | 4 +--- 4 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 box.json.dist diff --git a/.gitignore b/.gitignore index dbff826d..be6e5ecf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .sass-cache +scss_cache composer.lock -pscss.phar /*.css /*.scss /_site/ diff --git a/Makefile b/Makefile index b42e4a2b..5115941c 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,3 @@ compat: standard: vendor/bin/phpcs --standard=PSR2 bin src tests *.php - -phar: - php -dphar.readonly=0 vendor/bin/box build -v diff --git a/box.json.dist b/box.json.dist deleted file mode 100644 index 63d13baf..00000000 --- a/box.json.dist +++ /dev/null @@ -1,12 +0,0 @@ -{ - "chmod": "0755", - "files": [ - "LICENSE.md", - "scss.inc.php" - ], - "directories": ["src"], - "main": "bin/pscss", - "output": "pscss.phar", - "compactors": "Herrera\\Box\\Compactor\\Php", - "stub": true -} diff --git a/composer.json b/composer.json index 112a6bde..95118a32 100644 --- a/composer.json +++ b/composer.json @@ -25,8 +25,7 @@ }, "require-dev": { "squizlabs/php_codesniffer": "~2.5", - "phpunit/phpunit": "~3.7", - "kherge/box": "~2.5" + "phpunit/phpunit": "~4.6" }, "bin": ["bin/pscss"], "archive": { @@ -35,7 +34,6 @@ "/.gitattributes", "/.gitignore", "/.travis.yml", - "/box.json.dist", "/phpunit.xml.dist", "/tests" ] From b6f1ea6baac258aacc3e44cd48df9b4b0dd95ef2 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 09:04:34 -0400 Subject: [PATCH 032/282] Move Server class to example/ folder --- {src => example}/Server.php | 4 ++-- scss.inc.php | 1 - tests/ServerTest.php | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) rename {src => example}/Server.php (98%) diff --git a/src/Server.php b/example/Server.php similarity index 98% rename from src/Server.php rename to example/Server.php index c3fa985d..8dc3c7b2 100644 --- a/src/Server.php +++ b/example/Server.php @@ -434,7 +434,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->cacheDir = $cacheDir; if (! is_dir($this->cacheDir)) { - mkdir($this->cacheDir, 0755, true); + throw new ServerException('Cache directory doesn\'t exist: ' . $cacheDir); } if (! isset($scss)) { @@ -446,7 +446,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->showErrorsAsCSS = false; if (! ini_get('date.timezone')) { - date_default_timezone_set('UTC'); + throw new ServerException('Default date.timezone not set'); } } } diff --git a/scss.inc.php b/scss.inc.php index b6892fec..d2ec2639 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -26,5 +26,4 @@ include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; include_once __DIR__ . '/src/Version.php'; - include_once __DIR__ . '/src/Server.php'; } diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 2cecdd97..7c7ef6a9 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -8,9 +8,10 @@ * * @link http://leafo.github.io/scssphp */ - namespace Leafo\ScssPhp\Tests; +require_once __DIR__ . '/../example/Server.php'; + use Leafo\ScssPhp\Server; /** From 53e86eae35448fbcce8f682ffcb9fa224cd40cc3 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 10:58:24 -0400 Subject: [PATCH 033/282] Fix failing ServerTest --- tests/ServerTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 7c7ef6a9..fcaea917 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -23,6 +23,10 @@ class ServerTest extends \PHPUnit_Framework_TestCase { public function testCheckedCachedCompile() { + if (! file_exists(__DIR__ . '/inputs/scss_cache')) { + mkdir(__DIR__ . '/inputs/scss_cache', 0755); + } + $server = new Server(__DIR__ . '/inputs/'); $css = $server->checkedCachedCompile(__DIR__ . '/inputs/import.scss', '/tmp/scss.css'); From 942c61055d53a893f79a7d34261d609b618a1ced Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 22 Aug 2017 07:20:58 -0400 Subject: [PATCH 034/282] Update scss.inc.php --- scss.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scss.inc.php b/scss.inc.php index d2ec2639..83d23a6b 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -3,7 +3,7 @@ throw new \Exception('scssphp requires PHP 5.4 or above'); } -if (! class_exists('scssc', false)) { +if (! class_exists('Leafo\ScssPhp\Version', false)) { include_once __DIR__ . '/src/Base/Range.php'; include_once __DIR__ . '/src/Block.php'; include_once __DIR__ . '/src/Colors.php'; From 494c09f0c08261265ddca4ca51c51670e01680c4 Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Fri, 29 Sep 2017 16:00:39 +0800 Subject: [PATCH 035/282] fixes #532 Substitute deprecated php function each() --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index d4f406fa..442d57d5 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -290,7 +290,7 @@ public function output(Compiler $compiler = null) } reset($units); - list($unit, ) = each($units); + $unit = key($units); return (string) $dimension . $unit; } From 9eaf3a6db4d88ce2c265a89e3fc495fbec9bf7a6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 11:52:30 -0400 Subject: [PATCH 036/282] Bump to version 0.7.0 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 2d6eabc1..dd7cd384 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.7'; + const VERSION = 'v0.7.0'; } From f79f49b9aebf7c1885a27f1152f2d58d1ba4b05a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 12:06:15 -0400 Subject: [PATCH 037/282] Workaround PHP 7.2 code sniff --- src/Parser.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 619b7ba8..44043e65 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2468,7 +2468,13 @@ private function getSourcePosition($pos) */ private function saveEncoding() { - if (ini_get('mbstring.func_overload') & 2) { + if (version_compare(PHP_VERSION, '7.2.0') >= 0) { + return; + } + + $iniDirective = 'mbstring' . '.func_overload'; // deprecated in PHP 7.2 + + if (ini_get($iniDirective) & 2) { $this->encoding = mb_internal_encoding(); mb_internal_encoding('iso-8859-1'); From 192f9b29d41ba0e2c610c2343da61bdde28f6436 Mon Sep 17 00:00:00 2001 From: Thomas Eiling Date: Fri, 13 Oct 2017 12:48:50 -0400 Subject: [PATCH 038/282] misc - fix some phpdoc comments and code styles --- src/Compiler.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 68003b90..89119933 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1181,7 +1181,7 @@ protected function flattenSelectorSingle($single) /** * Compile selector to string; self(&) should have been replaced by now * - * @param array $selector + * @param string|array $selector * * @return string */ @@ -1203,7 +1203,7 @@ protected function compileSelector($selector) /** * Compile selector part * - * @param arary $piece + * @param array $piece * * @return string */ @@ -1963,7 +1963,7 @@ protected function shouldEval($value) * @param array $value * @param boolean $inExp * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function reduce($value, $inExp = false) { @@ -2238,7 +2238,7 @@ public function normalizeValue($value) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opAddNumberNumber($left, $right) { @@ -2251,7 +2251,7 @@ protected function opAddNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opMulNumberNumber($left, $right) { @@ -2264,7 +2264,7 @@ protected function opMulNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opSubNumberNumber($left, $right) { @@ -2277,7 +2277,7 @@ protected function opSubNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function opDivNumberNumber($left, $right) { @@ -2294,7 +2294,7 @@ protected function opDivNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opModNumberNumber($left, $right) { @@ -2580,7 +2580,7 @@ protected function opLtNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opCmpNumberNumber($left, $right) { @@ -3505,7 +3505,7 @@ protected function fileExists($name) * Call SCSS @function * * @param string $name - * @param array $args + * @param array $argValues * @param array $returnValue * * @return boolean Returns true if returnValue is set; otherwise, false @@ -3777,7 +3777,7 @@ protected function applyArguments($argDef, $argValues) * * @param mixed $value * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ private function coerceValue($value) { @@ -3850,7 +3850,8 @@ protected function coerceMap($item) /** * Coerce something to list * - * @param array $item + * @param array $item + * @param string $delim * * @return array */ @@ -5210,6 +5211,8 @@ protected function libVariableExists($args) * Workaround IE7's content counter bug. * * @param array $args + * + * @return array */ protected function libCounter($args) { From b413ff279f5f0ba5938e1315a8068a06643c9443 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 13:29:38 -0400 Subject: [PATCH 039/282] Change default precision to 10 (introduced in sass 3.5.0) --- bin/pscss | 2 +- src/Node/Number.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/pscss b/bin/pscss index 4ec061b8..70a2be3c 100755 --- a/bin/pscss +++ b/bin/pscss @@ -76,7 +76,7 @@ Options include: -i=path Set import path --iso8859-1 Use iso8859-1 encoding instead of utf-8 (default utf-8) --line-numbers Annotate selectors with comments referring to the source file and line number - -p=precision Set decimal number precision (default 5) + -p=precision Set decimal number precision (default 10) -T Dump formatted parse tree -v, --version Print the version diff --git a/src/Node/Number.php b/src/Node/Number.php index 442d57d5..5faedfdf 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -31,7 +31,7 @@ class Number extends Node implements \ArrayAccess /** * @var integer */ - static public $precision = 5; + static public $precision = 10; /** * @see http://www.w3.org/TR/2012/WD-css3-values-20120308/ From 75c9fb79ae362a84e89736c2812fc022332e0f5a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 13:29:08 -0400 Subject: [PATCH 040/282] Remove setlocale() to mitigate Windows side-effects --- src/Compiler.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 89119933..0d67c525 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -165,9 +165,6 @@ public function __construct() */ public function compile($code, $path = null) { - $locale = setlocale(LC_NUMERIC, 0); - setlocale(LC_NUMERIC, 'C'); - $this->indentLevel = -1; $this->commentsSeen = []; $this->extends = []; @@ -196,8 +193,6 @@ public function compile($code, $path = null) $out = $this->formatter->format($this->scope); - setlocale(LC_NUMERIC, $locale); - return $out; } From 9b3c1ff4eab71cdda612646fc94cfba0de9b6672 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 13:33:24 -0400 Subject: [PATCH 041/282] Use number_format to output locale-independent floats --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 5faedfdf..df1b79cc 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -292,7 +292,7 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); - return (string) $dimension . $unit; + return preg_replace('/[.]0*$/', '', number_format($dimension, static::$precision)) . $unit; } /** From 3cbdd6330958420b949fd19ef24472edfd3ee517 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:31:52 -0400 Subject: [PATCH 042/282] Trim trailing zeros --- src/Node/Number.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index df1b79cc..59eb811a 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -291,8 +291,9 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); - - return preg_replace('/[.]0*$/', '', number_format($dimension, static::$precision)) . $unit; + $dimension = number_format($dimension, static::$precision, '.', ''); + + return (static::$precision ? rtrim($dimension, '0') : $dimension) . $unit; } /** From db5273cd3a33cc0fb1b1fbba85dbb00deb61446b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:35:20 -0400 Subject: [PATCH 043/282] Trim trailing decimal point. --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 59eb811a..acfdaf29 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -293,7 +293,7 @@ public function output(Compiler $compiler = null) $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - return (static::$precision ? rtrim($dimension, '0') : $dimension) . $unit; + return (static::$precision ? rtrim($dimension, '.0') : $dimension) . $unit; } /** From c0ec63ff8eaff626ddeee1245b9119c82990c0ff Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:40:55 -0400 Subject: [PATCH 044/282] Fix previous edit from my phone. =P --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index acfdaf29..3dcdd2ca 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -293,7 +293,7 @@ public function output(Compiler $compiler = null) $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - return (static::$precision ? rtrim($dimension, '.0') : $dimension) . $unit; + return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit; } /** From f735beeb2b8926f4d361d2c51bbefbc5271fbdfc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:46:23 -0400 Subject: [PATCH 045/282] Fix unit test (1/2) --- tests/outputs/operators.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/outputs/operators.css b/tests/outputs/operators.css index fb492c82..50aaa8c0 100644 --- a/tests/outputs/operators.css +++ b/tests/outputs/operators.css @@ -6,7 +6,7 @@ body { color: 5px; top: 1.5em; left: -1cm; - top: 6.29921; } + top: 6.2992125984; } div { color: false; @@ -19,7 +19,7 @@ div { color: 1; } #units { - test: 2.5748in; + test: 2.5748031496in; test: 13mm; test: 4em; test: 11mm; @@ -169,7 +169,7 @@ div { margin: 0 -2em; } div { - *margin-left: 2.07447%; } + *margin-left: 2.0744680851%; } .foo { width: 12.5%; } From 69cb0ca734665c9a9ea41dd3e3d6f7a65dfdd93d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:48:01 -0400 Subject: [PATCH 046/282] Fix unit test (2/2) --- tests/outputs_numbered/operators.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/outputs_numbered/operators.css b/tests/outputs_numbered/operators.css index c3ea3d1d..ad4d678e 100644 --- a/tests/outputs_numbered/operators.css +++ b/tests/outputs_numbered/operators.css @@ -7,7 +7,7 @@ body { color: 5px; top: 1.5em; left: -1cm; - top: 6.29921; } + top: 6.2992125984; } /* line 15, inputs/operators.scss */ div { color: false; @@ -20,7 +20,7 @@ div { color: 1; } /* line 29, inputs/operators.scss */ #units { - test: 2.5748in; + test: 2.5748031496in; test: 13mm; test: 4em; test: 11mm; @@ -170,7 +170,7 @@ div { margin: 0 -2em; } /* line 184, inputs/operators.scss */ div { - *margin-left: 2.07447%; } + *margin-left: 2.0744680851%; } /* line 190, inputs/operators.scss */ .foo { width: 12.5%; } From 4f605a51462c85cf6df5bb45460483a138e04025 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 17:35:01 -0400 Subject: [PATCH 047/282] Bump version to 0.7.2 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index dd7cd384..0dd95fe6 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.0'; + const VERSION = 'v0.7.2'; } From c96edd1bedd3ba3e8ebeaf5ddd1f8669fb6f5625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20FRAN=C3=87OIS?= Date: Thu, 9 Nov 2017 11:16:12 +0100 Subject: [PATCH 048/282] add inline sourceMaps support --- src/Block.php | 5 + src/Compiler.php | 49 +++++- src/Formatter.php | 77 ++++++++- src/Formatter/Compressed.php | 4 +- src/Formatter/Crunched.php | 4 +- src/Formatter/Debug.php | 16 +- src/Formatter/Expanded.php | 4 +- src/Formatter/Nested.php | 20 ++- src/Formatter/OutputBlock.php | 15 ++ src/Parser.php | 1 + src/SourceMap/Base64VLQEncoder.php | 174 +++++++++++++++++++ src/SourceMap/SourceMapGenerator.php | 241 +++++++++++++++++++++++++++ 12 files changed, 579 insertions(+), 31 deletions(-) create mode 100644 src/SourceMap/Base64VLQEncoder.php create mode 100644 src/SourceMap/SourceMapGenerator.php diff --git a/src/Block.php b/src/Block.php index a6b5f3f0..87cfd6d8 100644 --- a/src/Block.php +++ b/src/Block.php @@ -28,6 +28,11 @@ class Block */ public $parent; + /** + * @var string; + */ + public $sourceName; + /** * @var integer */ diff --git a/src/Compiler.php b/src/Compiler.php index 0d67c525..a677760d 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -18,6 +18,7 @@ use Leafo\ScssPhp\Exception\CompilerException; use Leafo\ScssPhp\Formatter\OutputBlock; use Leafo\ScssPhp\Node; +use Leafo\ScssPhp\SourceMap\SourceMapGenerator; use Leafo\ScssPhp\Type; use Leafo\ScssPhp\Parser; use Leafo\ScssPhp\Util; @@ -64,6 +65,9 @@ class Compiler const WITH_SUPPORTS = 4; const WITH_ALL = 7; + const SOURCE_MAP_NONE = 0; + const SOURCE_MAP_INLINE = 1; + /** * @var array */ @@ -120,11 +124,16 @@ class Compiler protected $encoding = null; protected $lineNumberStyle = null; + protected $sourceMap = self::SOURCE_MAP_NONE; + protected $sourceMapOptions = []; + + /** @var string|Formatter */ protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; protected $rootEnv; protected $rootBlock; + /** @var Environment */ protected $env; protected $scope; protected $storeEnv; @@ -191,8 +200,21 @@ public function compile($code, $path = null) $this->compileRoot($tree); $this->popEnv(); - $out = $this->formatter->format($this->scope); + $sourceMapGenerator = null; + if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + } + $out = $this->formatter->format($this->scope, $sourceMapGenerator); + if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMap = $sourceMapGenerator->generateJson(); + + $sourceMapUrl = null; + if($this->sourceMap == self::SOURCE_MAP_INLINE) { + $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + } + $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); + } return $out; } @@ -274,6 +296,9 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; + $out->sourceColumn = $this->env->block->sourceColumn; return $out; } @@ -656,6 +681,7 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; + $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; @@ -729,6 +755,7 @@ protected function compileAtRoot(Block $block) // wrap inline selector if ($block->selector) { $wrapped = new Block; + $wrapped->sourceName = $block->sourceName; $wrapped->sourceIndex = $block->sourceIndex; $wrapped->sourceLine = $block->sourceLine; $wrapped->sourceColumn = $block->sourceColumn; @@ -785,6 +812,7 @@ private function spliceTree($envs, Block $block, $without) } $b = new Block; + $b->sourceName = $e->block->sourceName; $b->sourceIndex = $e->block->sourceIndex; $b->sourceLine = $e->block->sourceLine; $b->sourceColumn = $e->block->sourceColumn; @@ -1076,6 +1104,13 @@ protected function evalSelectors($selectors) return $selectors; } + /** + * @param array $sourceMapOptions + */ + public function setSourceMapOptions($sourceMapOptions) { + $this->sourceMapOptions = $sourceMapOptions; + } + /** * Evaluate selector * @@ -3299,6 +3334,13 @@ public function setLineNumberStyle($lineNumberStyle) $this->lineNumberStyle = $lineNumberStyle; } + /** + * @param int $sourceMap + */ + public function setSourceMap($sourceMap) { + $this->sourceMap = $sourceMap; + } + /** * Register function * @@ -5256,4 +5298,9 @@ protected function libInspect($args) return $args[0]; } + + public static function encodeURIComponent($string){ + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); + } } diff --git a/src/Formatter.php b/src/Formatter.php index 0d2635f1..487b6612 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Formatter\OutputBlock; +use Leafo\ScssPhp\SourceMap\SourceMapGenerator; /** * Base formatter @@ -56,10 +57,31 @@ abstract class Formatter public $assignSeparator; /** - * @var boolea + * @var boolean */ public $keepSemicolons; + /** + * @var OutputBlock; + */ + protected $currentBlock; + + + /** + * @var int + */ + protected $currentLine; + + /** + * @var int; + */ + protected $currentColumn; + + /** + * @var SourceMapGenerator + */ + protected $sourceMapGenerator; + /** * Initialize formatter * @@ -123,10 +145,10 @@ protected function blockLines(OutputBlock $block) $glue = $this->break . $inner; - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } @@ -139,9 +161,9 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - echo $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -167,6 +189,8 @@ protected function block(OutputBlock $block) return; } + $this->currentBlock = $block; + $pre = $this->indentStr(); if (! empty($block->selectors)) { @@ -187,10 +211,10 @@ protected function block(OutputBlock $block) $this->indentLevel--; if (empty($block->children)) { - echo $this->break; + $this->write($this->break); } - echo $pre . $this->close . $this->break; + $this->write($pre . $this->close . $this->break); } } @@ -201,10 +225,19 @@ protected function block(OutputBlock $block) * * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree * + * @param SourceMapGenerator|null $sourceMapGenerator * @return string + * @internal param bool $collectSourceMap */ - public function format(OutputBlock $block) + public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { + if($sourceMapGenerator) { + $this->currentLine = 1; + $this->currentColumn = 0; + $this->sourceMapGenerator = $sourceMapGenerator; + } else { + $this->sourceMapGenerator = null; + } ob_start(); $this->block($block); @@ -213,4 +246,32 @@ public function format(OutputBlock $block) return $out; } + + /** + * @param $str + */ + protected function write($str) { + if($this->sourceMapGenerator) { + $this->sourceMapGenerator->addMapping( + $this->currentLine, + $this->currentColumn, + $this->currentBlock->sourceLine, + $this->currentBlock->sourceColumn, + $this->currentBlock->sourceName + ); + + $lines = explode("\n", $str); + $lineCount = count($lines); + $this->currentLine += $lineCount-1; + + $lastLine = array_pop($lines); + if($lineCount == 1) { + $this->currentColumn += mb_strlen($lastLine); + } else { + $this->currentColumn = mb_strlen($lastLine); + } + } + + echo $str; + } } diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 17aca54a..69a67850 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -53,10 +53,10 @@ public function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write( $inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write( $this->break); } } } diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 0a89bc48..43d63833 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -51,10 +51,10 @@ public function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } } diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 321c5368..cb29641a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -52,13 +52,13 @@ protected function blockLines(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->lines)) { - echo "{$indent}block->lines: []\n"; + $this->write("{$indent}block->lines: []\n"); return; } foreach ($block->lines as $index => $line) { - echo "{$indent}block->lines[{$index}]: $line\n"; + $this->write( "{$indent}block->lines[{$index}]: $line\n"); } } @@ -70,13 +70,13 @@ protected function blockSelectors(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->selectors)) { - echo "{$indent}block->selectors: []\n"; + $this->write( "{$indent}block->selectors: []\n"); return; } foreach ($block->selectors as $index => $selector) { - echo "{$indent}block->selectors[{$index}]: $selector\n"; + $this->write( "{$indent}block->selectors[{$index}]: $selector\n"); } } @@ -88,7 +88,7 @@ protected function blockChildren(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->children)) { - echo "{$indent}block->children: []\n"; + $this->write( "{$indent}block->children: []\n"); return; } @@ -109,8 +109,10 @@ protected function block(OutputBlock $block) { $indent = $this->indentStr(); - echo "{$indent}block->type: {$block->type}\n" . - "{$indent}block->depth: {$block->depth}\n"; + $this->write( "{$indent}block->type: {$block->type}\n" . + "{$indent}block->depth: {$block->depth}\n"); + + $this->currentBlock = $block; $this->blockSelectors($block); $this->blockLines($block); diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index dff17e6b..0449787d 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -59,10 +59,10 @@ protected function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (empty($block->selectors) || ! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } } diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index c4ff803c..48e19ef7 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -12,7 +12,6 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; -use Leafo\ScssPhp\Formatter\OutputBlock; /** * Nested formatter @@ -66,10 +65,10 @@ protected function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write( $inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write( $this->break); } } @@ -80,9 +79,9 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - echo $inner + $this->write( $inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -94,13 +93,13 @@ protected function blockChildren(OutputBlock $block) $this->block($child); if ($i < count($block->children) - 1) { - echo $this->break; + $this->write( $this->break); if (isset($block->children[$i + 1])) { $next = $block->children[$i + 1]; if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) { - echo $this->break; + $this->write( $this->break); } } } @@ -120,6 +119,9 @@ protected function block(OutputBlock $block) return; } + $this->currentBlock = $block; + + $this->depth = $block->depth; if (! empty($block->selectors)) { @@ -139,11 +141,11 @@ protected function block(OutputBlock $block) if (! empty($block->selectors)) { $this->indentLevel--; - echo $this->close; + $this->write( $this->close); } if ($block->type === 'root') { - echo $this->break; + $this->write( $this->break); } } diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index 88e86402..7d233a8d 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -47,4 +47,19 @@ class OutputBlock * @var \Leafo\ScssPhp\Formatter\OutputBlock */ public $parent; + + /** + * @var string + */ + public $sourceName; + + /** + * @var int + */ + public $sourceLine; + + /** + * @var int + */ + public $sourceColumn; } diff --git a/src/Parser.php b/src/Parser.php index 44043e65..cc0a51df 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -708,6 +708,7 @@ protected function pushBlock($selectors, $pos = 0) list($line, $column) = $this->getSourcePosition($pos); $b = new Block; + $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php new file mode 100644 index 00000000..14652fbe --- /dev/null +++ b/src/SourceMap/Base64VLQEncoder.php @@ -0,0 +1,174 @@ + 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, + 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, + 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, + 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, + 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, + 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, + 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, + 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, + 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, + ); + /** + * Integer to char map + * + * @var array + */ + private $intToCharMap = array( + 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', + 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', + 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', + 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', + 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', + 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', + 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', + 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', + 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', + 63 => '/', + ); + /** + * Constructor + */ + public function __construct(){ + // I leave it here for future reference + // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) + // { + // $this->charToIntMap[$char] = $i; + // $this->intToCharMap[$i] = $char; + // } + } + /** + * Convert from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, + * even on a 64 bit machine. + * @param string $aValue + */ + public function toVLQSigned($aValue){ + return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); + } + /** + * Convert to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * We assume that the value was generated with a 32 bit machine in mind. + * Hence + * 1 becomes -2147483648 + * even on a 64 bit machine. + * @param integer $aValue + */ + public function fromVLQSigned($aValue){ + return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); + } + /** + * Return the base 64 VLQ encoded value. + * + * @param string $aValue The value to encode + * @return string The encoded value + */ + public function encode($aValue){ + $encoded = ''; + $vlq = $this->toVLQSigned($aValue); + do + { + $digit = $vlq & $this->mask; + $vlq = $this->zeroFill($vlq, $this->shift); + if($vlq > 0){ + $digit |= $this->continuationBit; + } + $encoded .= $this->base64Encode($digit); + } while($vlq > 0); + return $encoded; + } + /** + * Return the value decoded from base 64 VLQ. + * + * @param string $encoded The encoded value to decode + * @return integer The decoded value + */ + public function decode($encoded){ + $vlq = 0; + $i = 0; + do + { + $digit = $this->base64Decode($encoded[$i]); + $vlq |= ($digit & $this->mask) << ($i * $this->shift); + $i++; + } while($digit & $this->continuationBit); + return $this->fromVLQSigned($vlq); + } + /** + * Right shift with zero fill. + * + * @param integer $a number to shift + * @param integer $b number of bits to shift + * @return integer + */ + public function zeroFill($a, $b){ + return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); + } + /** + * Encode single 6-bit digit as base64. + * + * @param integer $number + * @return string + * @throws Exception If the number is invalid + */ + public function base64Encode($number){ + if($number < 0 || $number > 63){ + throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); + } + return $this->intToCharMap[$number]; + } + /** + * Decode single 6-bit digit from base64 + * + * @param string $char + * @return number + * @throws Exception If the number is invalid + */ + public function base64Decode($char){ + if(!array_key_exists($char, $this->charToIntMap)){ + throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); + } + return $this->charToIntMap[$char]; + } +} diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php new file mode 100644 index 00000000..3d21f4fe --- /dev/null +++ b/src/SourceMap/SourceMapGenerator.php @@ -0,0 +1,241 @@ + '', + // an optional name of the generated code that this source map is associated with. + 'sourceMapFilename' => null, + // url of the map + 'sourceMapURL' => null, + // absolute path to a file to write the map to + 'sourceMapWriteTo' => null, + // output source contents? + 'outputSourceFiles' => false, + // base path for filename normalization + 'sourceMapRootpath' => '', + // base path for filename normalization + 'sourceMapBasepath' => '' + ); + + /** + * The base64 VLQ encoder + * + * @var Base64VLQEncoder + */ + protected $encoder; + /** + * Array of mappings + * + * @var array + */ + protected $mappings = array(); + + /** + * Array of contents map + * + * @var array + */ + protected $contentsMap = array(); + /** + * File to content map + * + * @var array + */ + protected $sources = array(); + protected $source_keys = array(); + /** + * @var array + */ + private $options; + + public function __construct(array $options = []) { + $this->options = array_merge($this->defaultOptions, $options); + $this->encoder = new Base64VLQEncoder(); + } + + /** + * Adds a mapping + * + * @param integer $generatedLine The line number in generated file + * @param integer $generatedColumn The column number in generated file + * @param integer $originalLine The line number in original file + * @param integer $originalColumn The column number in original file + * @param string $sourceFile The original source file + */ + public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { + $this->mappings[] = array( + 'generated_line' => $generatedLine, + 'generated_column' => $generatedColumn, + 'original_line' => $originalLine, + 'original_column' => $originalColumn, + 'source_file' => $sourceFile + ); + + $this->sources[$sourceFile] = $sourceFile; + } + + /** + * Generates the JSON source map + * + * @return string + * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# + */ + public function generateJson() { + $sourceMap = array(); + $mappings = $this->generateMappings(); + // File version (always the first entry in the object) and must be a positive integer. + $sourceMap['version'] = self::VERSION; + // An optional name of the generated code that this source map is associated with. + $file = $this->options['sourceMapFilename']; + if($file) { + $sourceMap['file'] = $file; + } + // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. + $root = $this->options['sourceRoot']; + if($root) { + $sourceMap['sourceRoot'] = $root; + } + // A list of original sources used by the 'mappings' entry. + $sourceMap['sources'] = array(); + foreach($this->sources as $source_uri => $source_filename) { + $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + } + // A list of symbol names used by the 'mappings' entry. + $sourceMap['names'] = array(); + // A string with the encoded mapping data. + $sourceMap['mappings'] = $mappings; + if($this->options['outputSourceFiles']) { + // An optional list of source content, useful when the 'source' can't be hosted. + // The contents are listed in the same order as the sources above. + // 'null' may be used if some original sources should be retrieved by name. + $sourceMap['sourcesContent'] = $this->getSourcesContent(); + } + // less.js compat fixes + if(count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { + unset($sourceMap['sourceRoot']); + } + return json_encode($sourceMap); + } + + /** + * Returns the sources contents + * + * @return array|null + */ + protected function getSourcesContent() { + if(empty($this->sources)) { + return null; + } + $content = array(); + foreach($this->sources as $sourceFile) { + $content[] = file_get_contents($sourceFile); + } + return $content; + } + + /** + * Generates the mappings string + * + * @return string + */ + public function generateMappings() { + if(!count($this->mappings)) { + return ''; + } + $this->source_keys = array_flip(array_keys($this->sources)); + // group mappings by generated line number. + $groupedMap = $groupedMapEncoded = array(); + foreach($this->mappings as $m) { + $groupedMap[$m['generated_line']][] = $m; + } + ksort($groupedMap); + $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; + foreach($groupedMap as $lineNumber => $line_map) { + while(++$lastGeneratedLine < $lineNumber) { + $groupedMapEncoded[] = ';'; + } + $lineMapEncoded = array(); + $lastGeneratedColumn = 0; + foreach($line_map as $m) { + $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); + $lastGeneratedColumn = $m['generated_column']; + // find the index + if($m['source_file']) { + $index = $this->findFileIndex($m['source_file']); + if($index !== false) { + $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); + $lastOriginalIndex = $index; + // lines are stored 0-based in SourceMap spec version 3 + $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine); + $lastOriginalLine = $m['original_line'] - 1; + $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn); + $lastOriginalColumn = $m['original_column']; + } + } + $lineMapEncoded[] = $mapEncoded; + } + $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; + } + return rtrim(implode($groupedMapEncoded), ';'); + } + + /** + * Finds the index for the filename + * + * @param string $filename + * @return integer|false + */ + protected function findFileIndex($filename) { + return $this->source_keys[$filename]; + } + + protected function normalizeFilename($filename) { + $filename = $this->fixWindowsPath($filename); + $rootpath = $this->options['sourceMapRootpath']; + $basePath = $this->options['sourceMapBasepath']; + // "Trim" the 'sourceMapBasepath' from the output filename. + if(strpos($filename, $basePath) === 0) { + $filename = substr($filename, strlen($basePath)); + } + // Remove extra leading path separators. + if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { + $filename = substr($filename, 1); + } + return $rootpath . $filename; + } + + /** + * fix windows paths + * @param string $path + * @param bool $addEndSlash + * @return string + */ + public function fixWindowsPath($path, $addEndSlash = false) { + $slash = ($addEndSlash) ? '/' : ''; + if(!empty($path)) { + $path = str_replace('\\', '/', $path); + $path = rtrim($path, '/') . $slash; + } + return $path; + } +} From 87c8b53442f0be9c8dd33cbac2308842dd240240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20FRAN=C3=87OIS?= Date: Thu, 9 Nov 2017 13:37:18 +0100 Subject: [PATCH 049/282] inline sourceMaps: fix off-by-one column in source maps --- src/Formatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Formatter.php b/src/Formatter.php index 487b6612..7939113b 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -256,7 +256,7 @@ protected function write($str) { $this->currentLine, $this->currentColumn, $this->currentBlock->sourceLine, - $this->currentBlock->sourceColumn, + $this->currentBlock->sourceColumn - 1, //columns from parser are off by one $this->currentBlock->sourceName ); From ce6c958bc90b729467b010d35fece984e6edec44 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Dec 2017 12:34:12 -0500 Subject: [PATCH 050/282] Remove trailing whitespace --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 3dcdd2ca..efa83f5b 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -292,7 +292,7 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - + return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit; } From eb4ab3f9be1d7fe854017c3bf97a3ca8c27730fb Mon Sep 17 00:00:00 2001 From: dleffler Date: Fri, 15 Dec 2017 12:42:20 -0500 Subject: [PATCH 051/282] Adds File based source map (in addition to inline source map) - SOURCE_MAP_FILE, using code from less.php; ONLY if there is output from the scss file. --- scss.inc.php | 4 ++++ src/Compiler.php | 5 ++++- src/SourceMap/SourceMapGenerator.php | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/scss.inc.php b/scss.inc.php index 83d23a6b..77303ba3 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -11,6 +11,7 @@ include_once __DIR__ . '/src/Compiler/Environment.php'; include_once __DIR__ . '/src/Exception/CompilerException.php'; include_once __DIR__ . '/src/Exception/ParserException.php'; + include_once __DIR__ . '/src/Exception/RangeException.php'; //exp include_once __DIR__ . '/src/Exception/ServerException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; @@ -23,6 +24,9 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; +// include_once __DIR__ . '/src/example/Server.php'; + include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; + include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; include_once __DIR__ . '/src/Version.php'; diff --git a/src/Compiler.php b/src/Compiler.php index a677760d..2dc50531 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -67,6 +67,7 @@ class Compiler const SOURCE_MAP_NONE = 0; const SOURCE_MAP_INLINE = 1; + const SOURCE_MAP_FILE = 2; /** * @var array @@ -205,12 +206,14 @@ public function compile($code, $path = null) $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); } $out = $this->formatter->format($this->scope, $sourceMapGenerator); - if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + if(!empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { $sourceMap = $sourceMapGenerator->generateJson(); $sourceMapUrl = null; if($this->sourceMap == self::SOURCE_MAP_INLINE) { $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + } elseif ($this->sourceMap == self::SOURCE_MAP_FILE) { + $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); } $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 3d21f4fe..300a5a08 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -8,6 +8,8 @@ namespace Leafo\ScssPhp\SourceMap; +use Leafo\ScssPhp\Exception\CompilerException; + class SourceMapGenerator { /** * What version of source map does the generator generate? @@ -94,6 +96,28 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori $this->sources[$sourceFile] = $sourceFile; } + /** + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * @throws Exception If the file could not be saved + */ + public function saveMap($content){ + $file = $this->options['sourceMapWriteTo']; + $dir = dirname($file); + // directory does not exist + if( !is_dir($dir) ){ + // FIXME: create the dir automatically? + throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); + } + // FIXME: proper saving, with dir write check! + if(file_put_contents($file, $content) === false){ + throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); + } + return $this->options['sourceMapURL']; + } + /** * Generates the JSON source map * From 201b8978143ac913f00ab5a04a11cf2ba7151091 Mon Sep 17 00:00:00 2001 From: dleffler Date: Fri, 15 Dec 2017 12:42:58 -0500 Subject: [PATCH 052/282] Adds cachedCompile function to Server example (taken/modified from less.php) --- example/Server.php | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/example/Server.php b/example/Server.php index 8dc3c7b2..a6e8b986 100644 --- a/example/Server.php +++ b/example/Server.php @@ -416,6 +416,69 @@ public function checkedCachedCompile($in, $out, $force = false) return $css; } + /** + * Execute scssphp on a .scss file or a scssphp cache structure + * + * The scssphp cache structure contains information about a specific + * scss file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param bool $force Force rebuild? + * @return array scssphp cache structure + */ + public function cachedCompile($in, $force = false) { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime ) { + if (!file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->scss->getParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + } + /** * Constructor * From 89d9dabd18f8f068c62d7d0949da0c608ae5ed94 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 18 Dec 2017 17:32:42 -0500 Subject: [PATCH 053/282] Coding style (phpcs) cleanup --- Makefile | 2 +- example/Server.php | 124 +++++++++--------- scss.inc.php | 3 +- src/Block.php | 2 +- src/Compiler.php | 48 +++++-- src/Formatter.php | 27 ++-- src/Formatter/Compressed.php | 4 +- src/Formatter/Debug.php | 10 +- src/Formatter/Nested.php | 14 +- src/Formatter/OutputBlock.php | 4 +- src/SourceMap/Base64VLQEncoder.php | 136 ++++++++++++-------- src/SourceMap/SourceMapGenerator.php | 186 ++++++++++++++++++--------- src/Util.php | 8 +- tests/ServerTest.php | 1 + 14 files changed, 342 insertions(+), 227 deletions(-) diff --git a/Makefile b/Makefile index 5115941c..fb50ffdb 100644 --- a/Makefile +++ b/Makefile @@ -5,4 +5,4 @@ compat: TEST_SCSS_COMPAT=1 vendor/bin/phpunit --colors tests | tail -2 standard: - vendor/bin/phpcs --standard=PSR2 bin src tests *.php + vendor/bin/phpcs --standard=PSR2 bin src example tests *.php diff --git a/example/Server.php b/example/Server.php index a6e8b986..a8dc0457 100644 --- a/example/Server.php +++ b/example/Server.php @@ -417,67 +417,69 @@ public function checkedCachedCompile($in, $out, $force = false) } /** - * Execute scssphp on a .scss file or a scssphp cache structure - * - * The scssphp cache structure contains information about a specific - * scss file having been parsed. It can be used as a hint for future - * calls to determine whether or not a rebuild is required. - * - * The cache structure contains two important keys that may be used - * externally: - * - * compiled: The final compiled CSS - * updated: The time (in seconds) the CSS was last compiled - * - * The cache structure is a plain-ol' PHP associative array and can - * be serialized and unserialized without a hitch. - * - * @param mixed $in Input - * @param bool $force Force rebuild? - * @return array scssphp cache structure - */ - public function cachedCompile($in, $force = false) { - // assume no root - $root = null; - - if (is_string($in)) { - $root = $in; - } elseif (is_array($in) and isset($in['root'])) { - if ($force or ! isset($in['files'])) { - // If we are forcing a recompile or if for some reason the - // structure does not contain any file information we should - // specify the root to trigger a rebuild. - $root = $in['root']; - } elseif (isset($in['files']) and is_array($in['files'])) { - foreach ($in['files'] as $fname => $ftime ) { - if (!file_exists($fname) or filemtime($fname) > $ftime) { - // One of the files we knew about previously has changed - // so we should look at our incoming root again. - $root = $in['root']; - break; - } - } - } - } else { - // TODO: Throw an exception? We got neither a string nor something - // that looks like a compatible lessphp cache structure. - return null; - } - - if ($root !== null) { - // If we have a root value which means we should rebuild. - $out = array(); - $out['root'] = $root; - $out['compiled'] = $this->compileFile($root); - $out['files'] = $this->scss->getParsedFiles(); - $out['updated'] = time(); - return $out; - } else { - // No changes, pass back the structure - // we were given initially. - return $in; - } - } + * Execute scssphp on a .scss file or a scssphp cache structure + * + * The scssphp cache structure contains information about a specific + * scss file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param boolean $force Force rebuild? + * + * @return array scssphp cache structure + */ + public function cachedCompile($in, $force = false) + { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime) { + if (! file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->scss->getParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + } /** * Constructor diff --git a/scss.inc.php b/scss.inc.php index 77303ba3..13c84bf5 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -11,7 +11,7 @@ include_once __DIR__ . '/src/Compiler/Environment.php'; include_once __DIR__ . '/src/Exception/CompilerException.php'; include_once __DIR__ . '/src/Exception/ParserException.php'; - include_once __DIR__ . '/src/Exception/RangeException.php'; //exp + include_once __DIR__ . '/src/Exception/RangeException.php'; include_once __DIR__ . '/src/Exception/ServerException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; @@ -24,7 +24,6 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; -// include_once __DIR__ . '/src/example/Server.php'; include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; diff --git a/src/Block.php b/src/Block.php index 87cfd6d8..fc21e5a4 100644 --- a/src/Block.php +++ b/src/Block.php @@ -29,7 +29,7 @@ class Block public $parent; /** - * @var string; + * @var string */ public $sourceName; diff --git a/src/Compiler.php b/src/Compiler.php index 2dc50531..64399b2a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -128,13 +128,17 @@ class Compiler protected $sourceMap = self::SOURCE_MAP_NONE; protected $sourceMapOptions = []; - /** @var string|Formatter */ + /** + * @var string|\Leafo\ScssPhp\Formatter + */ protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; protected $rootEnv; protected $rootBlock; - /** @var Environment */ + /** + * @var \Leafo\ScssPhp\Compiler\Environment + */ protected $env; protected $scope; protected $storeEnv; @@ -202,22 +206,30 @@ public function compile($code, $path = null) $this->popEnv(); $sourceMapGenerator = null; - if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + + if ($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); } + $out = $this->formatter->format($this->scope, $sourceMapGenerator); - if(!empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMap = $sourceMapGenerator->generateJson(); + if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMap = $sourceMapGenerator->generateJson(); $sourceMapUrl = null; - if($this->sourceMap == self::SOURCE_MAP_INLINE) { - $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); - } elseif ($this->sourceMap == self::SOURCE_MAP_FILE) { - $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); + + switch ($this->sourceMap) { + case self::SOURCE_MAP_INLINE: + $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + break; + + case self::SOURCE_MAP_FILE: + $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); + break; } $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); } + return $out; } @@ -1110,7 +1122,8 @@ protected function evalSelectors($selectors) /** * @param array $sourceMapOptions */ - public function setSourceMapOptions($sourceMapOptions) { + public function setSourceMapOptions($sourceMapOptions) + { $this->sourceMapOptions = $sourceMapOptions; } @@ -1959,7 +1972,7 @@ protected function isTruthy($value) * * @param string $value * - * @return bool + * @return boolean */ protected function isImmediateRelationshipCombinator($value) { @@ -3338,9 +3351,14 @@ public function setLineNumberStyle($lineNumberStyle) } /** - * @param int $sourceMap + * Set source map option + * + * @api + * + * @param integer $sourceMap */ - public function setSourceMap($sourceMap) { + public function setSourceMap($sourceMap) + { $this->sourceMap = $sourceMap; } @@ -5302,8 +5320,10 @@ protected function libInspect($args) return $args[0]; } - public static function encodeURIComponent($string){ + public static function encodeURIComponent($string) + { $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); } } diff --git a/src/Formatter.php b/src/Formatter.php index 7939113b..68d20c88 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -62,18 +62,18 @@ abstract class Formatter public $keepSemicolons; /** - * @var OutputBlock; + * @var \Leafo\ScssPhp\Formatter\OutputBlock */ protected $currentBlock; /** - * @var int + * @var integer */ protected $currentLine; /** - * @var int; + * @var integer */ protected $currentColumn; @@ -231,13 +231,14 @@ protected function block(OutputBlock $block) */ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { - if($sourceMapGenerator) { + $this->sourceMapGenerator = null; + + if ($sourceMapGenerator) { $this->currentLine = 1; $this->currentColumn = 0; $this->sourceMapGenerator = $sourceMapGenerator; - } else { - $this->sourceMapGenerator = null; } + ob_start(); $this->block($block); @@ -248,10 +249,11 @@ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerato } /** - * @param $str + * @param string $str */ - protected function write($str) { - if($this->sourceMapGenerator) { + protected function write($str) + { + if ($this->sourceMapGenerator) { $this->sourceMapGenerator->addMapping( $this->currentLine, $this->currentColumn, @@ -265,11 +267,8 @@ protected function write($str) { $this->currentLine += $lineCount-1; $lastLine = array_pop($lines); - if($lineCount == 1) { - $this->currentColumn += mb_strlen($lastLine); - } else { - $this->currentColumn = mb_strlen($lastLine); - } + + $this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine); } echo $str; diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 69a67850..21cbf28e 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -53,10 +53,10 @@ public function blockLines(OutputBlock $block) } } - $this->write( $inner . implode($glue, $block->lines)); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - $this->write( $this->break); + $this->write($this->break); } } } diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index cb29641a..b31c8c7a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -58,7 +58,7 @@ protected function blockLines(OutputBlock $block) } foreach ($block->lines as $index => $line) { - $this->write( "{$indent}block->lines[{$index}]: $line\n"); + $this->write("{$indent}block->lines[{$index}]: $line\n"); } } @@ -70,13 +70,13 @@ protected function blockSelectors(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->selectors)) { - $this->write( "{$indent}block->selectors: []\n"); + $this->write("{$indent}block->selectors: []\n"); return; } foreach ($block->selectors as $index => $selector) { - $this->write( "{$indent}block->selectors[{$index}]: $selector\n"); + $this->write("{$indent}block->selectors[{$index}]: $selector\n"); } } @@ -88,7 +88,7 @@ protected function blockChildren(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->children)) { - $this->write( "{$indent}block->children: []\n"); + $this->write("{$indent}block->children: []\n"); return; } @@ -109,7 +109,7 @@ protected function block(OutputBlock $block) { $indent = $this->indentStr(); - $this->write( "{$indent}block->type: {$block->type}\n" . + $this->write("{$indent}block->type: {$block->type}\n" . "{$indent}block->depth: {$block->depth}\n"); $this->currentBlock = $block; diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 48e19ef7..7573a596 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -65,10 +65,10 @@ protected function blockLines(OutputBlock $block) } } - $this->write( $inner . implode($glue, $block->lines)); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - $this->write( $this->break); + $this->write($this->break); } } @@ -79,7 +79,7 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - $this->write( $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) . $this->open . $this->break); } @@ -93,13 +93,13 @@ protected function blockChildren(OutputBlock $block) $this->block($child); if ($i < count($block->children) - 1) { - $this->write( $this->break); + $this->write($this->break); if (isset($block->children[$i + 1])) { $next = $block->children[$i + 1]; if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) { - $this->write( $this->break); + $this->write($this->break); } } } @@ -141,11 +141,11 @@ protected function block(OutputBlock $block) if (! empty($block->selectors)) { $this->indentLevel--; - $this->write( $this->close); + $this->write($this->close); } if ($block->type === 'root') { - $this->write( $this->break); + $this->write($this->break); } } diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index 7d233a8d..e4092175 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -54,12 +54,12 @@ class OutputBlock public $sourceName; /** - * @var int + * @var integer */ public $sourceLine; /** - * @var int + * @var integer */ public $sourceColumn; } diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index 14652fbe..d1d1ed2a 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -1,140 +1,167 @@ + * @author Nicolas FRANÇOIS + */ +class Base64VLQEncoder +{ /** * Shift * * @var integer */ private $shift = 5; + /** * Mask * * @var integer */ private $mask = 0x1F; // == (1 << shift) == 0b00011111 + /** * Continuation bit * * @var integer */ private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000 + /** * Char to integer map * * @var array */ private $charToIntMap = array( - 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, - 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, - 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, - 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, - 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, - 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, - 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, - 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, - 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, + 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, 'H' => 7, + 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15, + 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23, + 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, + 'g' => 32, 'h' => 33, 'i' => 34, 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, + 'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, + 'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, + 4 => 56, 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, ); + /** * Integer to char map * * @var array */ private $intToCharMap = array( - 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', - 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', - 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', - 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', - 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', - 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', - 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', - 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', - 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', - 63 => '/', + 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', + 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', + 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X', + 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', + 32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', + 40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', + 48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', + 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/', ); + /** * Constructor */ - public function __construct(){ + public function __construct() + { // I leave it here for future reference - // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) + // foreach (str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) // { - // $this->charToIntMap[$char] = $i; - // $this->intToCharMap[$i] = $char; + // $this->charToIntMap[$char] = $i; + // $this->intToCharMap[$i] = $char; // } } + /** * Convert from a two-complement value to a value where the sign bit is - * is placed in the least significant bit. For example, as decimals: - * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) - * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, * even on a 64 bit machine. * @param string $aValue */ - public function toVLQSigned($aValue){ + public function toVLQSigned($aValue) + { return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); } + /** * Convert to a two-complement value from a value where the sign bit is * is placed in the least significant bit. For example, as decimals: - * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 - * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 * We assume that the value was generated with a 32 bit machine in mind. * Hence - * 1 becomes -2147483648 + * 1 becomes -2147483648 * even on a 64 bit machine. * @param integer $aValue */ - public function fromVLQSigned($aValue){ + public function fromVLQSigned($aValue) + { return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); } + /** * Return the base 64 VLQ encoded value. * * @param string $aValue The value to encode * @return string The encoded value */ - public function encode($aValue){ + public function encode($aValue) + { $encoded = ''; $vlq = $this->toVLQSigned($aValue); - do - { + + do { $digit = $vlq & $this->mask; $vlq = $this->zeroFill($vlq, $this->shift); - if($vlq > 0){ + + if ($vlq > 0) { $digit |= $this->continuationBit; } + $encoded .= $this->base64Encode($digit); - } while($vlq > 0); + } while ($vlq > 0); + return $encoded; } + /** * Return the value decoded from base 64 VLQ. * * @param string $encoded The encoded value to decode * @return integer The decoded value */ - public function decode($encoded){ + public function decode($encoded) + { $vlq = 0; $i = 0; - do - { + + do { $digit = $this->base64Decode($encoded[$i]); $vlq |= ($digit & $this->mask) << ($i * $this->shift); $i++; - } while($digit & $this->continuationBit); + } while ($digit & $this->continuationBit); + return $this->fromVLQSigned($vlq); } + /** * Right shift with zero fill. * @@ -142,9 +169,11 @@ public function decode($encoded){ * @param integer $b number of bits to shift * @return integer */ - public function zeroFill($a, $b){ + public function zeroFill($a, $b) + { return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); } + /** * Encode single 6-bit digit as base64. * @@ -152,12 +181,15 @@ public function zeroFill($a, $b){ * @return string * @throws Exception If the number is invalid */ - public function base64Encode($number){ - if($number < 0 || $number > 63){ + public function base64Encode($number) + { + if ($number < 0 || $number > 63) { throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); } + return $this->intToCharMap[$number]; } + /** * Decode single 6-bit digit from base64 * @@ -165,10 +197,12 @@ public function base64Encode($number){ * @return number * @throws Exception If the number is invalid */ - public function base64Decode($char){ - if(!array_key_exists($char, $this->charToIntMap)){ + public function base64Decode($char) + { + if (! array_key_exists($char, $this->charToIntMap)) { throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); } + return $this->charToIntMap[$char]; } } diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 300a5a08..31905b10 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -1,20 +1,33 @@ + * @author Nicolas FRANÇOIS + */ +class SourceMapGenerator +{ /** * What version of source map does the generator generate? */ const VERSION = 3; + /** * Array of default options * @@ -25,16 +38,22 @@ class SourceMapGenerator { // on a server or removing repeated values in the 'sources' entry. // This value is prepended to the individual entries in the 'source' field. 'sourceRoot' => '', + // an optional name of the generated code that this source map is associated with. 'sourceMapFilename' => null, + // url of the map 'sourceMapURL' => null, + // absolute path to a file to write the map to 'sourceMapWriteTo' => null, + // output source contents? 'outputSourceFiles' => false, + // base path for filename normalization 'sourceMapRootpath' => '', + // base path for filename normalization 'sourceMapBasepath' => '' ); @@ -42,9 +61,10 @@ class SourceMapGenerator { /** * The base64 VLQ encoder * - * @var Base64VLQEncoder + * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder */ protected $encoder; + /** * Array of mappings * @@ -58,6 +78,7 @@ class SourceMapGenerator { * @var array */ protected $contentsMap = array(); + /** * File to content map * @@ -65,12 +86,14 @@ class SourceMapGenerator { */ protected $sources = array(); protected $source_keys = array(); + /** * @var array */ private $options; - public function __construct(array $options = []) { + public function __construct(array $options = []) + { $this->options = array_merge($this->defaultOptions, $options); $this->encoder = new Base64VLQEncoder(); } @@ -84,7 +107,8 @@ public function __construct(array $options = []) { * @param integer $originalColumn The column number in original file * @param string $sourceFile The original source file */ - public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { + public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) + { $this->mappings[] = array( 'generated_line' => $generatedLine, 'generated_column' => $generatedColumn, @@ -97,26 +121,30 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori } /** - * Saves the source map to a file - * - * @param string $file The absolute path to a file - * @param string $content The content to write - * @throws Exception If the file could not be saved - */ - public function saveMap($content){ - $file = $this->options['sourceMapWriteTo']; - $dir = dirname($file); - // directory does not exist - if( !is_dir($dir) ){ - // FIXME: create the dir automatically? - throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); - } - // FIXME: proper saving, with dir write check! - if(file_put_contents($file, $content) === false){ - throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); - } - return $this->options['sourceMapURL']; - } + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * @throws Exception If the file could not be saved + */ + public function saveMap($content) + { + $file = $this->options['sourceMapWriteTo']; + $dir = dirname($file); + + // directory does not exist + if (! is_dir($dir)) { + // FIXME: create the dir automatically? + throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); + } + + // FIXME: proper saving, with dir write check! + if (file_put_contents($file, $content) === false) { + throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); + } + + return $this->options['sourceMapURL']; + } /** * Generates the JSON source map @@ -124,40 +152,44 @@ public function saveMap($content){ * @return string * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# */ - public function generateJson() { + public function generateJson() + { $sourceMap = array(); - $mappings = $this->generateMappings(); + $mappings = $this->generateMappings(); + // File version (always the first entry in the object) and must be a positive integer. $sourceMap['version'] = self::VERSION; + // An optional name of the generated code that this source map is associated with. $file = $this->options['sourceMapFilename']; - if($file) { + + if ($file) { $sourceMap['file'] = $file; } - // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. + + // An optional source root, useful for relocating source files on a server or removing repeated values in the + // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; - if($root) { - $sourceMap['sourceRoot'] = $root; - } - // A list of original sources used by the 'mappings' entry. - $sourceMap['sources'] = array(); - foreach($this->sources as $source_uri => $source_filename) { - $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + + if ($root) { + $sourceMap['names'] = array(); } - // A list of symbol names used by the 'mappings' entry. - $sourceMap['names'] = array(); + // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; - if($this->options['outputSourceFiles']) { + + if ($this->options['outputSourceFiles']) { // An optional list of source content, useful when the 'source' can't be hosted. // The contents are listed in the same order as the sources above. // 'null' may be used if some original sources should be retrieved by name. $sourceMap['sourcesContent'] = $this->getSourcesContent(); } + // less.js compat fixes - if(count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { + if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { unset($sourceMap['sourceRoot']); } + return json_encode($sourceMap); } @@ -166,14 +198,18 @@ public function generateJson() { * * @return array|null */ - protected function getSourcesContent() { - if(empty($this->sources)) { + protected function getSourcesContent() + { + if (empty($this->sources)) { return null; } + $content = array(); - foreach($this->sources as $sourceFile) { + + foreach ($this->sources as $sourceFile) { $content[] = file_get_contents($sourceFile); } + return $content; } @@ -182,31 +218,41 @@ protected function getSourcesContent() { * * @return string */ - public function generateMappings() { - if(!count($this->mappings)) { + public function generateMappings() + { + if (! count($this->mappings)) { return ''; } + $this->source_keys = array_flip(array_keys($this->sources)); + // group mappings by generated line number. $groupedMap = $groupedMapEncoded = array(); - foreach($this->mappings as $m) { + + foreach ($this->mappings as $m) { $groupedMap[$m['generated_line']][] = $m; } + ksort($groupedMap); $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; - foreach($groupedMap as $lineNumber => $line_map) { - while(++$lastGeneratedLine < $lineNumber) { + + foreach ($groupedMap as $lineNumber => $line_map) { + while (++$lastGeneratedLine < $lineNumber) { $groupedMapEncoded[] = ';'; } + $lineMapEncoded = array(); $lastGeneratedColumn = 0; - foreach($line_map as $m) { + + foreach ($line_map as $m) { $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); $lastGeneratedColumn = $m['generated_column']; + // find the index - if($m['source_file']) { + if ($m['source_file']) { $index = $this->findFileIndex($m['source_file']); - if($index !== false) { + + if ($index !== false) { $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); $lastOriginalIndex = $index; // lines are stored 0-based in SourceMap spec version 3 @@ -216,10 +262,13 @@ public function generateMappings() { $lastOriginalColumn = $m['original_column']; } } + $lineMapEncoded[] = $mapEncoded; } + $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; } + return rtrim(implode($groupedMapEncoded), ';'); } @@ -227,39 +276,50 @@ public function generateMappings() { * Finds the index for the filename * * @param string $filename + * * @return integer|false */ - protected function findFileIndex($filename) { + protected function findFileIndex($filename) + { return $this->source_keys[$filename]; } - protected function normalizeFilename($filename) { + protected function normalizeFilename($filename) + { $filename = $this->fixWindowsPath($filename); $rootpath = $this->options['sourceMapRootpath']; $basePath = $this->options['sourceMapBasepath']; + // "Trim" the 'sourceMapBasepath' from the output filename. - if(strpos($filename, $basePath) === 0) { + if (strpos($filename, $basePath) === 0) { $filename = substr($filename, strlen($basePath)); } + // Remove extra leading path separators. - if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { + if (strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { $filename = substr($filename, 1); } + return $rootpath . $filename; } /** - * fix windows paths - * @param string $path - * @param bool $addEndSlash + * Fix windows paths + * + * @param string $path + * @param boolean $addEndSlash + * * @return string */ - public function fixWindowsPath($path, $addEndSlash = false) { + public function fixWindowsPath($path, $addEndSlash = false) + { $slash = ($addEndSlash) ? '/' : ''; - if(!empty($path)) { + + if (! empty($path)) { $path = str_replace('\\', '/', $path); $path = rtrim($path, '/') . $slash; } + return $path; } } diff --git a/src/Util.php b/src/Util.php index b2a05db8..f5aca7a4 100644 --- a/src/Util.php +++ b/src/Util.php @@ -25,10 +25,10 @@ class Util * Asserts that `value` falls within `range` (inclusive), leaving * room for slight floating-point errors. * - * @param string $name The name of the value. Used in the error message. - * @param Range $range Range of values. - * @param array $value The value to check. - * @param string $unit The unit of the value. Used in error reporting. + * @param string $name The name of the value. Used in the error message. + * @param \Leafo\ScssPhp\Base\Range $range Range of values. + * @param array $value The value to check. + * @param string $unit The unit of the value. Used in error reporting. * * @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin. * diff --git a/tests/ServerTest.php b/tests/ServerTest.php index fcaea917..290d8fbf 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -8,6 +8,7 @@ * * @link http://leafo.github.io/scssphp */ + namespace Leafo\ScssPhp\Tests; require_once __DIR__ . '/../example/Server.php'; From 266913f8bf86b0e6a144e75225c82dc12652bedc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:24:54 -0500 Subject: [PATCH 054/282] More coding style tweaks --- src/Compiler.php | 57 +++++++++++++--------------- src/Formatter.php | 8 ++-- src/Formatter/Nested.php | 1 + src/Parser.php | 2 +- src/SourceMap/Base64VLQEncoder.php | 19 +++++++--- src/SourceMap/SourceMapGenerator.php | 14 ++++--- src/Util.php | 16 +++++++- 7 files changed, 69 insertions(+), 48 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 64399b2a..f12cf7ee 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -65,9 +65,9 @@ class Compiler const WITH_SUPPORTS = 4; const WITH_ALL = 7; - const SOURCE_MAP_NONE = 0; + const SOURCE_MAP_NONE = 0; const SOURCE_MAP_INLINE = 1; - const SOURCE_MAP_FILE = 2; + const SOURCE_MAP_FILE = 2; /** * @var array @@ -219,7 +219,7 @@ public function compile($code, $path = null) switch ($this->sourceMap) { case self::SOURCE_MAP_INLINE: - $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap)); break; case self::SOURCE_MAP_FILE: @@ -305,14 +305,14 @@ protected function pushExtends($target, $origin, $block) protected function makeOutputBlock($type, $selectors = null) { $out = new OutputBlock; - $out->type = $type; - $out->lines = []; - $out->children = []; - $out->parent = $this->scope; - $out->selectors = $selectors; - $out->depth = $this->env->depth; - $out->sourceName = $this->env->block->sourceName; - $out->sourceLine = $this->env->block->sourceLine; + $out->type = $type; + $out->lines = []; + $out->children = []; + $out->parent = $this->scope; + $out->selectors = $selectors; + $out->depth = $this->env->depth; + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; return $out; @@ -696,7 +696,7 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; - $wrapped->sourceName = $media->sourceName; + $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; @@ -1119,14 +1119,6 @@ protected function evalSelectors($selectors) return $selectors; } - /** - * @param array $sourceMapOptions - */ - public function setSourceMapOptions($sourceMapOptions) - { - $this->sourceMapOptions = $sourceMapOptions; - } - /** * Evaluate selector * @@ -3351,7 +3343,7 @@ public function setLineNumberStyle($lineNumberStyle) } /** - * Set source map option + * Enable/disable source maps * * @api * @@ -3362,6 +3354,18 @@ public function setSourceMap($sourceMap) $this->sourceMap = $sourceMap; } + /** + * Set source map options + * + * @api + * + * @param array $sourceMapOptions + */ + public function setSourceMapOptions($sourceMapOptions) + { + $this->sourceMapOptions = $sourceMapOptions; + } + /** * Register function * @@ -5188,7 +5192,7 @@ protected function libToLowerCase($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = [mb_strtolower($stringContent)]; + $string[2] = [function_exists('mb_strtolower') ? mb_strtolower($stringContent) : strtolower($stringContent)]; return $string; } @@ -5199,7 +5203,7 @@ protected function libToUpperCase($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = [mb_strtoupper($stringContent)]; + $string[2] = [function_exists('mb_strtoupper') ? mb_strtoupper($stringContent) : strtoupper($stringContent)]; return $string; } @@ -5319,11 +5323,4 @@ protected function libInspect($args) return $args[0]; } - - public static function encodeURIComponent($string) - { - $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); - - return strtr(rawurlencode($string), $revert); - } } diff --git a/src/Formatter.php b/src/Formatter.php index 68d20c88..4d2fdd08 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -66,7 +66,6 @@ abstract class Formatter */ protected $currentBlock; - /** * @var integer */ @@ -78,7 +77,7 @@ abstract class Formatter protected $currentColumn; /** - * @var SourceMapGenerator + * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator */ protected $sourceMapGenerator; @@ -223,11 +222,10 @@ protected function block(OutputBlock $block) * * @api * - * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree + * @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator * - * @param SourceMapGenerator|null $sourceMapGenerator * @return string - * @internal param bool $collectSourceMap */ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 7573a596..626fec13 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * Nested formatter diff --git a/src/Parser.php b/src/Parser.php index cc0a51df..ccac0d3f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -708,7 +708,7 @@ protected function pushBlock($selectors, $pos = 0) list($line, $column) = $this->getSourcePosition($pos); $b = new Block; - $b->sourceName = $this->sourceName; + $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index d1d1ed2a..435a7c60 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -94,6 +94,7 @@ public function __construct() * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, * even on a 64 bit machine. + * * @param string $aValue */ public function toVLQSigned($aValue) @@ -110,6 +111,7 @@ public function toVLQSigned($aValue) * Hence * 1 becomes -2147483648 * even on a 64 bit machine. + * * @param integer $aValue */ public function fromVLQSigned($aValue) @@ -121,6 +123,7 @@ public function fromVLQSigned($aValue) * Return the base 64 VLQ encoded value. * * @param string $aValue The value to encode + * * @return string The encoded value */ public function encode($aValue) @@ -146,6 +149,7 @@ public function encode($aValue) * Return the value decoded from base 64 VLQ. * * @param string $encoded The encoded value to decode + * * @return integer The decoded value */ public function decode($encoded) @@ -167,6 +171,7 @@ public function decode($encoded) * * @param integer $a number to shift * @param integer $b number of bits to shift + * * @return integer */ public function zeroFill($a, $b) @@ -178,13 +183,15 @@ public function zeroFill($a, $b) * Encode single 6-bit digit as base64. * * @param integer $number + * * @return string - * @throws Exception If the number is invalid + * + * @throws \Exception If the number is invalid */ public function base64Encode($number) { if ($number < 0 || $number > 63) { - throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); + throw new \Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); } return $this->intToCharMap[$number]; @@ -194,13 +201,15 @@ public function base64Encode($number) * Decode single 6-bit digit from base64 * * @param string $char - * @return number - * @throws Exception If the number is invalid + * + * @return integer + * + * @throws \Exception If the number is invalid */ public function base64Decode($char) { if (! array_key_exists($char, $this->charToIntMap)) { - throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); + throw new \Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); } return $this->charToIntMap[$char]; diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 31905b10..cbfaa7cf 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -101,11 +101,11 @@ public function __construct(array $options = []) /** * Adds a mapping * - * @param integer $generatedLine The line number in generated file + * @param integer $generatedLine The line number in generated file * @param integer $generatedColumn The column number in generated file - * @param integer $originalLine The line number in original file - * @param integer $originalColumn The column number in original file - * @param string $sourceFile The original source file + * @param integer $originalLine The line number in original file + * @param integer $originalColumn The column number in original file + * @param string $sourceFile The original source file */ public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { @@ -123,9 +123,10 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori /** * Saves the source map to a file * - * @param string $file The absolute path to a file + * @param string $file The absolute path to a file * @param string $content The content to write - * @throws Exception If the file could not be saved + * + * @throws \Leafo\ScssPhp\Exception\CompilerException If the file could not be saved */ public function saveMap($content) { @@ -150,6 +151,7 @@ public function saveMap($content) * Generates the JSON source map * * @return string + * * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# */ public function generateJson() diff --git a/src/Util.php b/src/Util.php index f5aca7a4..d49b265c 100644 --- a/src/Util.php +++ b/src/Util.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Exception\RangeException; /** - * Utilties + * Utilty functions * * @author Anthon Pang */ @@ -53,4 +53,18 @@ public static function checkRange($name, Range $range, $value, $unit = '') throw new RangeException("$name {$val} must be between {$range->first} and {$range->last}$unit"); } + + /** + * Encode URI component + * + * @param string $string + * + * @return string + */ + public static function encodeURIComponent($string) + { + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + + return strtr(rawurlencode($string), $revert); + } } From f92d3e46cf98f1ba7a6af0d8ceee1beeaff75827 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:31:11 -0500 Subject: [PATCH 055/282] Version bump to v0.7.3; fixed #135 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 0dd95fe6..e9c0218c 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.2'; + const VERSION = 'v0.7.3'; } From b373570479027fa226afec3762852e54ee560bdd Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:45:24 -0500 Subject: [PATCH 056/282] Add --sourcemap flag to bin/pscss --- bin/pscss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bin/pscss b/bin/pscss index 70a2be3c..be1cb51c 100755 --- a/bin/pscss +++ b/bin/pscss @@ -32,6 +32,7 @@ $debugInfo = false; $lineNumbers = false; $ignoreErrors = false; $encoding = false; +$sourceMap = false; /** * Parse argument @@ -77,6 +78,7 @@ Options include: --iso8859-1 Use iso8859-1 encoding instead of utf-8 (default utf-8) --line-numbers Annotate selectors with comments referring to the source file and line number -p=precision Set decimal number precision (default 10) + --sourcemap Create source map file -T Dump formatted parse tree -v, --version Print the version @@ -108,6 +110,11 @@ EOT; continue; } + if ($argv[$i] === '--sourcemap') { + $sourceMap = true; + continue; + } + if ($argv[$i] === '-T') { $dumpTree = true; continue; @@ -193,6 +200,10 @@ if ($style) { $scss->setFormatter('Leafo\\ScssPhp\\Formatter\\' . ucfirst($style)); } +if ($sourceMap) { + $scss->setSourceMap(Compiler::SOURCE_MAP_FILE); +} + if ($encoding) { $scss->setEncoding($encoding); } From 1e862d7f28bbcce159e3c75eee8069e059f95563 Mon Sep 17 00:00:00 2001 From: dleffler Date: Wed, 20 Dec 2017 09:08:18 -0500 Subject: [PATCH 057/282] Fix/restore working source map generation in 0.7.3 --- src/SourceMap/SourceMapGenerator.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index cbfaa7cf..3a55851a 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -174,9 +174,17 @@ public function generateJson() $root = $this->options['sourceRoot']; if ($root) { +// $sourceMap['sourceRoot'] = $root; + // A list of symbol names used by the 'mappings' entry. $sourceMap['names'] = array(); } + // A list of original sources used by the 'mappings' entry. + $sourceMap['sources'] = array(); + foreach($this->sources as $source_uri => $source_filename) { + $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + } + // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; From e8e4df7c5447165638ac255bd86a08b093bc9fb0 Mon Sep 17 00:00:00 2001 From: dleffler Date: Wed, 20 Dec 2017 16:03:15 -0500 Subject: [PATCH 058/282] Regression fix last push to fix 0.7.3 source maps --- src/SourceMap/SourceMapGenerator.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 3a55851a..c0f96dab 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -172,11 +172,9 @@ public function generateJson() // An optional source root, useful for relocating source files on a server or removing repeated values in the // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; - if ($root) { -// $sourceMap['sourceRoot'] = $root; + $sourceMap['sourceRoot'] = $root; // A list of symbol names used by the 'mappings' entry. - $sourceMap['names'] = array(); } // A list of original sources used by the 'mappings' entry. @@ -184,6 +182,7 @@ public function generateJson() foreach($this->sources as $source_uri => $source_filename) { $sourceMap['sources'][] = $this->normalizeFilename($source_filename); } + $sourceMap['names'] = array(); // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; From 045f3087bcb7a293ca2f6da02109fa17d20834a5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 20 Dec 2017 19:17:12 -0500 Subject: [PATCH 059/282] Update SourceMapGenerator.php --- src/SourceMap/SourceMapGenerator.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index c0f96dab..08e06168 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -172,16 +172,19 @@ public function generateJson() // An optional source root, useful for relocating source files on a server or removing repeated values in the // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; + if ($root) { $sourceMap['sourceRoot'] = $root; - // A list of symbol names used by the 'mappings' entry. } // A list of original sources used by the 'mappings' entry. $sourceMap['sources'] = array(); - foreach($this->sources as $source_uri => $source_filename) { + + foreach ($this->sources as $source_uri => $source_filename) { $sourceMap['sources'][] = $this->normalizeFilename($source_filename); } + + // A list of symbol names used by the 'mappings' entry. $sourceMap['names'] = array(); // A string with the encoded mapping data. From d7296256bd1bfacb8bd24e69030fd5e187181835 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 20 Dec 2017 19:18:53 -0500 Subject: [PATCH 060/282] Version bump --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index e9c0218c..6d07d5ab 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.3'; + const VERSION = 'v0.7.4'; } From 11f556571b2d4d531a6215398f39e313e48d1aa2 Mon Sep 17 00:00:00 2001 From: dleffler Date: Mon, 5 Feb 2018 09:23:38 -0500 Subject: [PATCH 061/282] Add option to pass a custom SourceMapGenerator object --- src/Compiler.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index f12cf7ee..35a3f866 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -207,8 +207,13 @@ public function compile($code, $path = null) $sourceMapGenerator = null; - if ($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + if ($this->sourceMap) { + if (is_object($this->sourceMap) && is_a($this->sourceMap, 'SourceMapGenerator')) { + $sourceMapGenerator = $this->sourceMap; + $this->sourceMap = self::SOURCE_MAP_FILE; + } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + } } $out = $this->formatter->format($this->scope, $sourceMapGenerator); From a73e32a5e110a8de9919939b3f5371d44c1299b5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:14:51 -0500 Subject: [PATCH 062/282] is_a() -> instanceof --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 35a3f866..03c94d9a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -208,7 +208,7 @@ public function compile($code, $path = null) $sourceMapGenerator = null; if ($this->sourceMap) { - if (is_object($this->sourceMap) && is_a($this->sourceMap, 'SourceMapGenerator')) { + if (is_object($this->sourceMap) && $this->sourceMap instanceof SourceMapGenerator) { $sourceMapGenerator = $this->sourceMap; $this->sourceMap = self::SOURCE_MAP_FILE; } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { From 593b92929c3f0e9d82c84ace7d29e8550e386413 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:19:06 -0500 Subject: [PATCH 063/282] No side-effect in abs(), ceil(), floor(), round(); fixes #551 --- src/Compiler.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 03c94d9a..8da8be77 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4740,36 +4740,32 @@ protected function libPercentage($args) protected function libRound($args) { $num = $args[0]; - $num[1] = round($num[1]); - return $num; + return new Node\Number(round($num[1]), $num[2]); } protected static $libFloor = ['value']; protected function libFloor($args) { $num = $args[0]; - $num[1] = floor($num[1]); - return $num; + return new Node\Number(floor($num[1]), $num[2]); } protected static $libCeil = ['value']; protected function libCeil($args) { $num = $args[0]; - $num[1] = ceil($num[1]); - return $num; + return new Node\Number(ceil($num[1]), $num[2]); } protected static $libAbs = ['value']; protected function libAbs($args) { $num = $args[0]; - $num[1] = abs($num[1]); - return $num; + return new Node\Number(abs($num[1]), $num[2]); } protected function libMin($args) From d3801706dbf2423a380b7815383c0cbc9203c58a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:50:15 -0500 Subject: [PATCH 064/282] `@for` with units; fixes #548 --- src/Compiler.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8da8be77..d46d5332 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1772,9 +1772,18 @@ protected function compileChild($child, OutputBlock $out) list(, $for) = $child; $start = $this->reduce($for->start, true); + $end = $this->reduce($for->end, true); + + if ( ! ($start[2] == $end[2] || $end->unitless())) { + $this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr()); + + break; + } + + $unit = $start[2]; $start = $start[1]; - $end = $this->reduce($for->end, true); - $end = $end[1]; + $end = $end[1]; + $d = $start < $end ? 1 : -1; for (;;) { @@ -1784,7 +1793,7 @@ protected function compileChild($child, OutputBlock $out) break; } - $this->set($for->var, new Node\Number($start, '')); + $this->set($for->var, new Node\Number($start, $unit)); $start += $d; $ret = $this->compileChildren($for->children, $out); From a4b566f0571f63a2cc00ee03025f6a498092383f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 04:03:48 -0500 Subject: [PATCH 065/282] Update docblocks --- src/Base/Range.php | 2 +- src/Block.php | 2 +- src/Colors.php | 2 +- src/Compiler.php | 4 ++-- src/Compiler/Environment.php | 2 +- src/Exception/CompilerException.php | 2 +- src/Exception/ParserException.php | 2 +- src/Exception/RangeException.php | 2 +- src/Exception/ServerException.php | 2 +- src/Formatter.php | 2 +- src/Formatter/Compact.php | 2 +- src/Formatter/Compressed.php | 2 +- src/Formatter/Crunched.php | 2 +- src/Formatter/Debug.php | 2 +- src/Formatter/Expanded.php | 2 +- src/Formatter/Nested.php | 2 +- src/Formatter/OutputBlock.php | 2 +- src/Node.php | 2 +- src/Node/Number.php | 2 +- src/Parser.php | 2 +- src/SourceMap/Base64VLQEncoder.php | 2 +- src/SourceMap/SourceMapGenerator.php | 2 +- src/Type.php | 2 +- src/Util.php | 2 +- src/Version.php | 2 +- 25 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Base/Range.php b/src/Base/Range.php index 3f3a067d..8bcc6ecf 100644 --- a/src/Base/Range.php +++ b/src/Base/Range.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2017 Leaf Corcoran + * @copyright 2015-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Block.php b/src/Block.php index fc21e5a4..a6ef8e03 100644 --- a/src/Block.php +++ b/src/Block.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Colors.php b/src/Colors.php index 61a71ab6..2314ea58 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Compiler.php b/src/Compiler.php index d46d5332..292e960e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -1774,7 +1774,7 @@ protected function compileChild($child, OutputBlock $out) $start = $this->reduce($for->start, true); $end = $this->reduce($for->end, true); - if ( ! ($start[2] == $end[2] || $end->unitless())) { + if (! ($start[2] == $end[2] || $end->unitless())) { $this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr()); break; diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php index b4b86e32..fe309dd3 100644 --- a/src/Compiler/Environment.php +++ b/src/Compiler/Environment.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/CompilerException.php b/src/Exception/CompilerException.php index 45fc1671..7ca2e2b3 100644 --- a/src/Exception/CompilerException.php +++ b/src/Exception/CompilerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ParserException.php b/src/Exception/ParserException.php index c0ee002d..6d64335f 100644 --- a/src/Exception/ParserException.php +++ b/src/Exception/ParserException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/RangeException.php b/src/Exception/RangeException.php index 47192ff5..3ba6bf14 100644 --- a/src/Exception/RangeException.php +++ b/src/Exception/RangeException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php index 68d3a290..d0ed0842 100644 --- a/src/Exception/ServerException.php +++ b/src/Exception/ServerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter.php b/src/Formatter.php index 4d2fdd08..b4f90aa9 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index aaf972b7..4efa1a08 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 21cbf28e..1faa7e11 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 43d63833..42d77b5f 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index b31c8c7a..bfcbf41a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 0449787d..d8c1e887 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 626fec13..8f72206f 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index e4092175..5eb589c9 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node.php b/src/Node.php index eb543c73..b9f7945a 100644 --- a/src/Node.php +++ b/src/Node.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node/Number.php b/src/Node/Number.php index efa83f5b..42c16803 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Parser.php b/src/Parser.php index ccac0d3f..6fdea3e2 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index 435a7c60..1189ce0e 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 08e06168..70b47cdc 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Type.php b/src/Type.php index 2d659e4d..e84c47e4 100644 --- a/src/Type.php +++ b/src/Type.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Util.php b/src/Util.php index d49b265c..7526e020 100644 --- a/src/Util.php +++ b/src/Util.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Version.php b/src/Version.php index 6d07d5ab..2bda80ff 100644 --- a/src/Version.php +++ b/src/Version.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * From 8b538d828bbb75276974605c4a1a435e939da74e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 8 Feb 2018 21:04:21 -0500 Subject: [PATCH 066/282] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 2bda80ff..3b8dcd00 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.4'; + const VERSION = 'v0.7.5'; } From 9410cbb398d17b096ce4bdd533f1712a651aca3d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 28 Feb 2018 23:06:40 -0500 Subject: [PATCH 067/282] Fixes #559 - mix() fix --- src/Compiler.php | 2 +- tests/inputs/functions.scss | 9 ++++++++- tests/outputs/functions.css | 3 +++ tests/outputs_numbered/functions.css | 3 +++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 292e960e..698d4d89 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4526,7 +4526,7 @@ protected function libMix($args) ]; if ($firstAlpha != 1.0 || $secondAlpha != 1.0) { - $new[] = $firstAlpha * $weight + $secondAlpha * ($weight - 1); + $new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight); } return $this->fixColor($new); diff --git a/tests/inputs/functions.scss b/tests/inputs/functions.scss index bf3edf1f..9ebd544f 100644 --- a/tests/inputs/functions.scss +++ b/tests/inputs/functions.scss @@ -165,4 +165,11 @@ $str: 'global'; div { margin: mapping(one two); @include test(); -} \ No newline at end of file +} + +$a: rgba(#000, 0.5); +$b: rgba(#fff, 0.75); + +.test { + color: mix($a, $b); +} diff --git a/tests/outputs/functions.css b/tests/outputs/functions.css index aa0bebbb..e89beb8f 100644 --- a/tests/outputs/functions.css +++ b/tests/outputs/functions.css @@ -49,3 +49,6 @@ p { div { margin: 1px 1px 2px 2px; padding: 1px 1px 2px 2px; } + +.test { + color: rgba(159, 159, 159, 0.625); } diff --git a/tests/outputs_numbered/functions.css b/tests/outputs_numbered/functions.css index 6583ff05..6841fa05 100644 --- a/tests/outputs_numbered/functions.css +++ b/tests/outputs_numbered/functions.css @@ -51,3 +51,6 @@ p { div { margin: 1px 1px 2px 2px; padding: 1px 1px 2px 2px; } +/* line 173, inputs/functions.scss */ +.test { + color: rgba(159, 159, 159, 0.625); } From 847f9f2d6cb64042ed6ca91938f8aa7d20dcadba Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 19 Apr 2018 11:42:23 -0400 Subject: [PATCH 068/282] fixes #568 example/Server: set default timezone to UTC --- example/Server.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/example/Server.php b/example/Server.php index a8dc0457..6ea97018 100644 --- a/example/Server.php +++ b/example/Server.php @@ -207,7 +207,7 @@ protected function compile($in, $out) $elapsed = round((microtime(true) - $start), 4); $v = Version::VERSION; - $t = date('r'); + $t = gmdate('r'); $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css; $etag = md5($css); @@ -329,7 +329,7 @@ public function serve($salt = '') try { list($css, $etag) = $this->compile($input, $output); - $lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT'; + $lastModified = gmdate('r', filemtime($output)); header('Last-Modified: ' . $lastModified); header('Content-type: text/css'); @@ -371,7 +371,7 @@ public function serve($salt = '') return; } - $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT'; + $lastModified = gmdate('r', $mtime); header('Last-Modified: ' . $lastModified); echo file_get_contents($output); @@ -510,8 +510,6 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->scss = $scss; $this->showErrorsAsCSS = false; - if (! ini_get('date.timezone')) { - throw new ServerException('Default date.timezone not set'); - } + date_default_timezone_set('UTC'); } } From 53bb5275a7e3fad3676f1e01f3014d39a0c6d675 Mon Sep 17 00:00:00 2001 From: AzJezz Date: Sun, 29 Apr 2018 16:32:07 +0100 Subject: [PATCH 069/282] Update Compiler.php --- src/Compiler.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 698d4d89..32e9b774 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,10 +316,15 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; - $out->sourceName = $this->env->block->sourceName; - $out->sourceLine = $this->env->block->sourceLine; - $out->sourceColumn = $this->env->block->sourceColumn; - + if($this->env->block instanceof Block) { + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; + $out->sourceColumn = $this->env->block->sourceColumn; + } else { + $out->sourceName = null; + $out->sourceLine = null; + $out->sourceColum = null; + } return $out; } From ddebe1a68a53c4b8524c03f8cb2c24174cb028dc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 18 May 2018 14:48:28 -0400 Subject: [PATCH 070/282] Fix #573 --- src/Compiler.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 32e9b774..1aad621b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,9 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $value[4] . ')'; + $a = new Node\Number($value[4]); + + return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } $h = sprintf('#%02x%02x%02x', $r, $g, $b); @@ -4336,7 +4338,7 @@ protected function libRgb($args) protected function libRgba($args) { if ($color = $this->coerceColor($args[0])) { - $num = ! isset($args[1]) ? $args[3] : $args[1]; + $num = isset($args[3]) ? $args[3] : $args[1]; $alpha = $this->assertNumber($num); $color[4] = $alpha; From 57292fba51af83b0e812bc1cb31c7816b3f61936 Mon Sep 17 00:00:00 2001 From: Tim Elsass Date: Sun, 20 May 2018 00:52:50 -0400 Subject: [PATCH 071/282] Fix #575 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 1aad621b..746c6422 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,7 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - $a = new Node\Number($value[4]); + $a = new Node\Number($value[4],''); return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } From 04482345fc0dd1b5305bcd5032ac4deb8a9ebbeb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 20 May 2018 10:11:19 -0400 Subject: [PATCH 072/282] Update Compiler.php --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 746c6422..b727f178 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,7 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - $a = new Node\Number($value[4],''); + $a = new Node\Number($value[4], ''); return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } From 1a819c3b926c251891ede7c6bd12d91e403dc384 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 18:05:54 -0400 Subject: [PATCH 073/282] Fix phpcs warnings --- src/Compiler.php | 4 ++-- src/Parser.php | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index b727f178..bfda455f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,14 +316,14 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; - if($this->env->block instanceof Block) { + if ($this->env->block instanceof Block) { $out->sourceName = $this->env->block->sourceName; $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; } else { $out->sourceName = null; $out->sourceLine = null; - $out->sourceColum = null; + $out->sourceColum = null; } return $out; } diff --git a/src/Parser.php b/src/Parser.php index 6fdea3e2..b8bfdc36 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1302,6 +1302,21 @@ protected function value(&$out) { $s = $this->seek(); + if ($this->literal('url(') && $this->match('data:image\/([a-z0-9+-]+);base64,', $m, false)) { + $len = strspn($this->buffer, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=', $this->count); + + $this->count += $len; + + if ($this->literal(')')) { + $content = substr($this->buffer, $s, $this->count - $s); + $out = [Type::T_KEYWORD, $content]; + + return true; + } + } + + $this->seek($s); + if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) { $out = [Type::T_UNARY, 'not', $inner, $this->inParens]; From e94a5c21addf8541a710d5ac6507532118ba3295 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 18:12:08 -0400 Subject: [PATCH 074/282] Refs #527 - parsing base64 --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index b8bfdc36..de86f094 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1302,7 +1302,7 @@ protected function value(&$out) { $s = $this->seek(); - if ($this->literal('url(') && $this->match('data:image\/([a-z0-9+-]+);base64,', $m, false)) { + if ($this->literal('url(') && $this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m, false)) { $len = strspn($this->buffer, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=', $this->count); $this->count += $len; From deaf1dede19347fed905931ad80b495b077e4220 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 21:33:11 -0400 Subject: [PATCH 075/282] Fix typo --- src/Compiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index bfda455f..72df991e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,6 +316,7 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; + if ($this->env->block instanceof Block) { $out->sourceName = $this->env->block->sourceName; $out->sourceLine = $this->env->block->sourceLine; @@ -323,8 +324,9 @@ protected function makeOutputBlock($type, $selectors = null) } else { $out->sourceName = null; $out->sourceLine = null; - $out->sourceColum = null; + $out->sourceColumn = null; } + return $out; } From 585f6ae84de62ffecf69c23805f25d78d7e4b794 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 22:18:53 -0400 Subject: [PATCH 076/282] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 3b8dcd00..c8a56a5b 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.5'; + const VERSION = 'v0.7.6'; } From 0b94ebc2b0e61e797bfc0cb74acab5f3e95a09fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 10 Jun 2018 01:09:50 +0200 Subject: [PATCH 077/282] Actually merge maps instead of just concatenating --- src/Compiler.php | 12 +++++++++++- tests/inputs/map.scss | 5 +++-- tests/outputs/map.css | 4 ++++ tests/outputs_numbered/map.css | 10 ++++++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 72df991e..8551f001 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4985,7 +4985,17 @@ protected function libMapMerge($args) $map1 = $this->assertMap($args[0]); $map2 = $this->assertMap($args[1]); - return [Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])]; + foreach ($map2[1] as $i2 => $key2) { + foreach ($map1[1] as $i1 => $key1) { + if ($this->compileStringContent($this->coerceString($key1)) === $this->compileStringContent($this->coerceString($key2))) { + $map1[2][$i1] = $map2[2][$i2]; + continue 2; + } + } + $map1[1][] = $map2[1][$i2]; + $map1[2][] = $map2[2][$i2]; + } + return $map1; } protected static $libKeywords = ['args']; diff --git a/tests/inputs/map.scss b/tests/inputs/map.scss index 0fb6be19..7f8adeba 100644 --- a/tests/inputs/map.scss +++ b/tests/inputs/map.scss @@ -25,9 +25,10 @@ div { bar: nth(nth($map, 1), 1); } -$color: ("black" : #000000); +$color: ("black" : #000000, "grey" : #777777); +$color2: ("grey" : #888888, "white" : #ffffff); -@each $color_name, $color_value in $color { +@each $color_name, $color_value in map_merge( $color, $color2 ) { .#{$color_name} { background-color: $color_value !important; } diff --git a/tests/outputs/map.css b/tests/outputs/map.css index 9c38094a..a8898915 100644 --- a/tests/outputs/map.css +++ b/tests/outputs/map.css @@ -12,6 +12,10 @@ div { bar: color; } .black { background-color: #000 !important; } + .grey { + background-color: #888 !important; } + .white { + background-color: #fff !important; } div { a: 1; diff --git a/tests/outputs_numbered/map.css b/tests/outputs_numbered/map.css index 16de52b9..23c144fb 100644 --- a/tests/outputs_numbered/map.css +++ b/tests/outputs_numbered/map.css @@ -11,10 +11,16 @@ div { div { foo: color black; bar: color; } -/* line 31, inputs/map.scss */ +/* line 32, inputs/map.scss */ .black { background-color: #000 !important; } -/* line 52, inputs/map.scss */ +/* line 32, inputs/map.scss */ +.grey { + background-color: #888 !important; } +/* line 32, inputs/map.scss */ +.white { + background-color: #fff !important; } +/* line 53, inputs/map.scss */ div { a: 1; b: 2; From 4963903c0a88db128026f1a78de205a7153d4b48 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Jun 2018 18:06:25 -0400 Subject: [PATCH 078/282] #569 attempt to fix call() with ellipsis --- src/Compiler.php | 38 +++++++++++++++++++++-------- tests/inputs/builtins.scss | 20 +++++++++++++++ tests/outputs/builtins.css | 12 +++++++++ tests/outputs_numbered/builtins.css | 12 +++++++++ 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 72df991e..7d8db736 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3655,7 +3655,7 @@ protected function callNativeFunction($name, $args, &$returnValue) return false; } - list($sorted, $kwargs) = $this->sortArgs($prototype, $args); + @list($sorted, $kwargs) = $this->sortArgs($prototype, $args); if ($name !== 'if' && $name !== 'call') { foreach ($sorted as &$val) { @@ -3714,7 +3714,7 @@ protected function sortArgs($prototype, $args) $key = $key[1]; if (empty($key)) { - $posArgs[] = $value; + $posArgs[] = empty($arg[2]) ? $value : $arg; } else { $keyArgs[$key] = $value; } @@ -4266,20 +4266,38 @@ protected function libCall($args, $kwargs) { $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true))); - $args = array_map( - function ($a) { - return [null, $a, false]; - }, - $args - ); + $posArgs = []; + + foreach ($args as $arg) { + if (empty($arg[0])) { + if ($arg[2] === true) { + $tmp = $this->reduce($arg[1]); + + if ($tmp[0] === Type::T_LIST) { + foreach ($tmp[2] as $item) { + $posArgs[] = [null, $item, false]; + } + } else { + $posArgs[] = [null, $tmp, true]; + } + + continue; + } + + $posArgs[] = [null, $this->reduce($arg), false]; + continue; + } + + $posArgs[] = [null, $arg, false]; + } if (count($kwargs)) { foreach ($kwargs as $key => $value) { - $args[] = [[Type::T_VARIABLE, $key], $value, false]; + $posArgs[] = [[Type::T_VARIABLE, $key], $value, false]; } } - return $this->reduce([Type::T_FUNCTION_CALL, $name, $args]); + return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]); } protected static $libIf = ['condition', 'if-true', 'if-false']; diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index a80db5b1..00cd3c63 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -216,3 +216,23 @@ $type: text; div.unquote-test { a: unquote('[type=\'#{$type}\']'); } + +.does-compile-1 { + color: call(lighten, #000, 40%); +} + +.does-compile-2 { + $color: #000; + $percent: 40%; + color: call(lighten, $color, $percent); +} + +.does-compile-3 { + $args: (#000, 40%); + color: call(lighten, nth($args, 1), nth($args, 2)); +} + +.does-compile-4 { + $args: (#000, 40%); + color: call(lighten, $args...); +} diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index 50e2a82e..db43727d 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -159,3 +159,15 @@ div.call-tests { div.unquote-test { a: [type='text']; } + +.does-compile-1 { + color: #666; } + +.does-compile-2 { + color: #666; } + +.does-compile-3 { + color: #666; } + +.does-compile-4 { + color: #666; } diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index 722c6af4..e84d727c 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -160,3 +160,15 @@ div.call-tests { /* line 216, inputs/builtins.scss */ div.unquote-test { a: [type='text']; } +/* line 220, inputs/builtins.scss */ +.does-compile-1 { + color: #666; } +/* line 224, inputs/builtins.scss */ +.does-compile-2 { + color: #666; } +/* line 230, inputs/builtins.scss */ +.does-compile-3 { + color: #666; } +/* line 235, inputs/builtins.scss */ +.does-compile-4 { + color: #666; } From 3d3fd508102242ab5744b91e51ff78e262db9467 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Jun 2018 23:10:38 -0400 Subject: [PATCH 079/282] refs #548 - treat 0 as a special unitless number --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7d8db736..62d125d8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4856,7 +4856,7 @@ protected function getNormalizedNumbers($args) if (null === $unit) { $unit = $number[2]; $originalUnit = $item->unitStr(); - } elseif ($unit !== $number[2]) { + } elseif ($number[1] && $unit !== $number[2]) { $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr()); break; } From 1d656f8c02a3a69404bba6b28ec4e06edddf0f49 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 21 Jul 2018 21:22:08 -0400 Subject: [PATCH 080/282] refs #578 peephole optimization --- src/Compiler.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 414e4401..637f1c1c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -5004,15 +5004,19 @@ protected function libMapMerge($args) $map2 = $this->assertMap($args[1]); foreach ($map2[1] as $i2 => $key2) { + $key = $this->compileStringContent($this->coerceString($key2)); + foreach ($map1[1] as $i1 => $key1) { - if ($this->compileStringContent($this->coerceString($key1)) === $this->compileStringContent($this->coerceString($key2))) { + if ($key === $this->compileStringContent($this->coerceString($key1))) { $map1[2][$i1] = $map2[2][$i2]; continue 2; } } + $map1[1][] = $map2[1][$i2]; $map1[2][] = $map2[2][$i2]; } + return $map1; } From b72d613f682b714f498412483aff353cde977c00 Mon Sep 17 00:00:00 2001 From: Jiong Ye Date: Fri, 17 Aug 2018 16:24:08 -0400 Subject: [PATCH 081/282] leafo/scssphp#581 generate inline sourcemap in commandline --- bin/pscss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pscss b/bin/pscss index be1cb51c..ce80e6a9 100755 --- a/bin/pscss +++ b/bin/pscss @@ -201,7 +201,7 @@ if ($style) { } if ($sourceMap) { - $scss->setSourceMap(Compiler::SOURCE_MAP_FILE); + $scss->setSourceMap(Compiler::SOURCE_MAP_INLINE); } if ($encoding) { From a7be38d3dabb6d3c15e28073541163428ad819ab Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Thu, 30 Aug 2018 14:43:47 +0200 Subject: [PATCH 082/282] #515 Fix backslash escape --- src/Parser.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index de86f094..748d38ae 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1737,6 +1737,8 @@ protected function string(&$out) $content[] = $m[2] . '"'; } elseif ($this->literal("'", false)) { $content[] = $m[2] . "'"; + } elseif ($this->literal("\\", false)) { + $content[] = $m[2] . "\\"; } else { $content[] = $m[2]; } @@ -1753,7 +1755,9 @@ protected function string(&$out) $delim = '"'; foreach ($content as &$string) { - if ($string === "\\'") { + if ($string === "\\\\") { + $string = "\\"; + } elseif ($string === "\\'") { $string = "'"; } elseif ($string === '\\"') { $string = '"'; From 0b589890bfbdd2e14d272f78d9d49038c051f12c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 24 Jul 2018 10:47:05 -0400 Subject: [PATCH 083/282] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index c8a56a5b..ec0fee6f 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.6'; + const VERSION = 'v0.7.8'; } From 2a88d3e9ba903e1568cda7cd33add91d093a639c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 26 Sep 2018 16:46:29 -0400 Subject: [PATCH 084/282] Update unit tests (fixes #515) --- tests/inputs/builtins.scss | 1 + tests/outputs/builtins.css | 1 + tests/outputs_numbered/builtins.css | 27 ++++++++++++++------------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 00cd3c63..c75a854e 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -78,6 +78,7 @@ color: quote("I do?"); color: str_index(abc, b); color: str_insert(string, insert, 2); + color: str_insert(string, "\\", 2); color: str_length(string); color: str_slice(string, 2, 4); color: str-slice(string, 2, 0); diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index db43727d..94f29b4e 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -53,6 +53,7 @@ color: "I do?"; color: 2; color: sinserttring; + color: s\\tring; color: 6; color: tri; color: trin; diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index e84d727c..559b6a30 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -54,6 +54,7 @@ color: "I do?"; color: 2; color: sinserttring; + color: s\\tring; color: 6; color: tri; color: trin; @@ -74,7 +75,7 @@ color: st; color: string; color: strin; } -/* line 105, inputs/builtins.scss */ +/* line 106, inputs/builtins.scss */ #number { color: 250%; color: 50%; @@ -86,7 +87,7 @@ width: 200%; bottom: 10px; padding: 3em 1in 96px 72pt; } -/* line 119, inputs/builtins.scss */ +/* line 120, inputs/builtins.scss */ #list { len: 3; len: 1; @@ -108,7 +109,7 @@ cool: great job one two three; zip: 1px solid, 2px dashed; zip: 1px solid red, 2px dashed green; } -/* line 155, inputs/builtins.scss */ +/* line 156, inputs/builtins.scss */ #introspection { t: number; t: string; @@ -128,47 +129,47 @@ c: true; c: false; c: true; } -/* line 179, inputs/builtins.scss */ +/* line 180, inputs/builtins.scss */ #if { color: yes; color: no; color: yes; color: yes; } -/* line 186, inputs/builtins.scss */ +/* line 187, inputs/builtins.scss */ .transparent { r: 0; g: 0; b: 0; a: 0; } -/* line 193, inputs/builtins.scss */ +/* line 194, inputs/builtins.scss */ .alpha { a: 1; a: 1; a: 1; a: 0.5; a: alpha(currentColor); } -/* line 202, inputs/builtins.scss */ +/* line 203, inputs/builtins.scss */ #exists { a: true; b: true; c: false; } -/* line 209, inputs/builtins.scss */ +/* line 210, inputs/builtins.scss */ div.call-tests { a: #0a64ff; b: #0058ef; c: b; } -/* line 216, inputs/builtins.scss */ +/* line 217, inputs/builtins.scss */ div.unquote-test { a: [type='text']; } -/* line 220, inputs/builtins.scss */ +/* line 221, inputs/builtins.scss */ .does-compile-1 { color: #666; } -/* line 224, inputs/builtins.scss */ +/* line 225, inputs/builtins.scss */ .does-compile-2 { color: #666; } -/* line 230, inputs/builtins.scss */ +/* line 231, inputs/builtins.scss */ .does-compile-3 { color: #666; } -/* line 235, inputs/builtins.scss */ +/* line 236, inputs/builtins.scss */ .does-compile-4 { color: #666; } From f893748cd4482051bc503064c6a4396b10280957 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 26 Sep 2018 17:15:59 -0400 Subject: [PATCH 085/282] Add Base64VLQ encoder test (refs #600) --- tests/Base64VLQTest.php | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/Base64VLQTest.php diff --git a/tests/Base64VLQTest.php b/tests/Base64VLQTest.php new file mode 100644 index 00000000..21b373b8 --- /dev/null +++ b/tests/Base64VLQTest.php @@ -0,0 +1,54 @@ + + */ +class Base64VLQTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test encode + * + * param string $expected + * param string $value + * + * @dataProvider getEncode + */ + public function testEncode($expected, $value) + { + $encoder = new Base64VLQEncoder; + + $this->assertEquals($expected, $encoder->encode($value)); + } + + /** + * Data provider for testEncode + * + * @return array + */ + public static function getEncode() + { + return [ + ['A', 0], + ['C', 1], + ['D', -1], + ['2H', 123], + ['qxmvrH', 123456789], + ['+/////D', 2147483647], // 2^31-1 + ]; + } +} From 0fcbdf599f9f8289d033f327532b6a2230b1d77f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 30 Sep 2018 20:57:13 -0400 Subject: [PATCH 086/282] Fixes #600 - change Base64 VLQ encoder/decoder implementtion --- scss.inc.php | 3 +- src/SourceMap/Base64.php | 184 +++++++++++++++++++++++++++ src/SourceMap/Base64VLQ.php | 137 ++++++++++++++++++++ src/SourceMap/SourceMapGenerator.php | 4 +- tests/Base64VLQTest.php | 4 +- 5 files changed, 327 insertions(+), 5 deletions(-) create mode 100644 src/SourceMap/Base64.php create mode 100644 src/SourceMap/Base64VLQ.php diff --git a/scss.inc.php b/scss.inc.php index 13c84bf5..d75ecb55 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -24,7 +24,8 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; - include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; + include_once __DIR__ . '/src/SourceMap/Base64.php'; + include_once __DIR__ . '/src/SourceMap/Base64VLQ.php'; include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; diff --git a/src/SourceMap/Base64.php b/src/SourceMap/Base64.php new file mode 100644 index 00000000..9d17e6f0 --- /dev/null +++ b/src/SourceMap/Base64.php @@ -0,0 +1,184 @@ + + */ +class Base64 +{ + /** + * @var array + */ + private static $encodingMap = array ( + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + 4 => 'E', + 5 => 'F', + 6 => 'G', + 7 => 'H', + 8 => 'I', + 9 => 'J', + 10 => 'K', + 11 => 'L', + 12 => 'M', + 13 => 'N', + 14 => 'O', + 15 => 'P', + 16 => 'Q', + 17 => 'R', + 18 => 'S', + 19 => 'T', + 20 => 'U', + 21 => 'V', + 22 => 'W', + 23 => 'X', + 24 => 'Y', + 25 => 'Z', + 26 => 'a', + 27 => 'b', + 28 => 'c', + 29 => 'd', + 30 => 'e', + 31 => 'f', + 32 => 'g', + 33 => 'h', + 34 => 'i', + 35 => 'j', + 36 => 'k', + 37 => 'l', + 38 => 'm', + 39 => 'n', + 40 => 'o', + 41 => 'p', + 42 => 'q', + 43 => 'r', + 44 => 's', + 45 => 't', + 46 => 'u', + 47 => 'v', + 48 => 'w', + 49 => 'x', + 50 => 'y', + 51 => 'z', + 52 => '0', + 53 => '1', + 54 => '2', + 55 => '3', + 56 => '4', + 57 => '5', + 58 => '6', + 59 => '7', + 60 => '8', + 61 => '9', + 62 => '+', + 63 => '/', + ); + + /** + * @var array + */ + private static $decodingMap = array( + 'A' => 0, + 'B' => 1, + 'C' => 2, + 'D' => 3, + 'E' => 4, + 'F' => 5, + 'G' => 6, + 'H' => 7, + 'I' => 8, + 'J' => 9, + 'K' => 10, + 'L' => 11, + 'M' => 12, + 'N' => 13, + 'O' => 14, + 'P' => 15, + 'Q' => 16, + 'R' => 17, + 'S' => 18, + 'T' => 19, + 'U' => 20, + 'V' => 21, + 'W' => 22, + 'X' => 23, + 'Y' => 24, + 'Z' => 25, + 'a' => 26, + 'b' => 27, + 'c' => 28, + 'd' => 29, + 'e' => 30, + 'f' => 31, + 'g' => 32, + 'h' => 33, + 'i' => 34, + 'j' => 35, + 'k' => 36, + 'l' => 37, + 'm' => 38, + 'n' => 39, + 'o' => 40, + 'p' => 41, + 'q' => 42, + 'r' => 43, + 's' => 44, + 't' => 45, + 'u' => 46, + 'v' => 47, + 'w' => 48, + 'x' => 49, + 'y' => 50, + 'z' => 51, + 0 => 52, + 1 => 53, + 2 => 54, + 3 => 55, + 4 => 56, + 5 => 57, + 6 => 58, + 7 => 59, + 8 => 60, + 9 => 61, + '+' => 62, + '/' => 63, + ); + + /** + * Convert to base64 + * + * @param integer $value + * + * @return string + */ + public static function encode($value) + { + return self::$encodingMap[$value]; + } + + /** + * Convert from base64 + * + * @param string $value + * + * @return integer + */ + public static function decode($value) + { + return self::$decodingMap[$value]; + } +} diff --git a/src/SourceMap/Base64VLQ.php b/src/SourceMap/Base64VLQ.php new file mode 100644 index 00000000..6de3b5ee --- /dev/null +++ b/src/SourceMap/Base64VLQ.php @@ -0,0 +1,137 @@ + + * @author Anthon Pang + */ +class Base64VLQ +{ + // A Base64 VLQ digit can represent 5 bits, so it is base-32. + const VLQ_BASE_SHIFT = 5; + + // A mask of bits for a VLQ digit (11111), 31 decimal. + const VLQ_BASE_MASK = 31; + + // The continuation bit is the 6th bit. + const VLQ_CONTINUATION_BIT = 32; + + /** + * Returns the VLQ encoded value. + * + * @param integer $value + * + * @return string + */ + public static function encode($value) + { + $encoded = ''; + $vlq = self::toVLQSigned($value); + + do { + $digit = $vlq & self::VLQ_BASE_MASK; + $vlq >>= self::VLQ_BASE_SHIFT; + + if ($vlq > 0) { + $digit |= self::VLQ_CONTINUATION_BIT; + } + + $encoded .= Base64::encode($digit); + } while ($vlq > 0); + + return $encoded; + } + + /** + * Decodes VLQValue. + * + * @param string $str + * @param integer $index + * + * @return integer + */ + public static function decode($str, &$index) + { + $result = 0; + $shift = 0; + + do { + $c = $str[$index++]; + $digit = Base64::decode($c); + $continuation = ($digit & self::VLQ_CONTINUATION_BIT) != 0; + $digit &= self::VLQ_BASE_MASK; + $result = $result + ($digit << $shift); + $shift = $shift + self::VLQ_BASE_SHIFT; + } while ($continuation); + + return self::fromVLQSigned($result); + } + + /** + * Converts from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * + * @param integer $value + * + * @return integer + */ + private static function toVLQSigned($value) + { + if ($value < 0) { + return ((-$value) << 1) + 1; + } + + return ($value << 1) + 0; + } + + /** + * Converts to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * + * @param integer $value + * + * @return integer + */ + private static function fromVLQSigned($value) + { + $negate = ($value & 1) === 1; + $value = $value >> 1; + + return $negate ? -$value : $value; + } +} diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 70b47cdc..6e969cae 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -61,7 +61,7 @@ class SourceMapGenerator /** * The base64 VLQ encoder * - * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder + * @var \Leafo\ScssPhp\SourceMap\Base64VLQ */ protected $encoder; @@ -95,7 +95,7 @@ class SourceMapGenerator public function __construct(array $options = []) { $this->options = array_merge($this->defaultOptions, $options); - $this->encoder = new Base64VLQEncoder(); + $this->encoder = new Base64VLQ(); } /** diff --git a/tests/Base64VLQTest.php b/tests/Base64VLQTest.php index 21b373b8..a3b638de 100644 --- a/tests/Base64VLQTest.php +++ b/tests/Base64VLQTest.php @@ -11,7 +11,7 @@ namespace Leafo\ScssPhp\Tests; -use Leafo\ScssPhp\SourceMap\Base64VLQEncoder; +use Leafo\ScssPhp\SourceMap\Base64VLQ; /** * Base64VLQ encoder test @@ -30,7 +30,7 @@ class Base64VLQTest extends \PHPUnit_Framework_TestCase */ public function testEncode($expected, $value) { - $encoder = new Base64VLQEncoder; + $encoder = new Base64VLQ; $this->assertEquals($expected, $encoder->encode($value)); } From c1ca4095952a431d4847862e048715bc6fa331fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Nguyen?= Date: Thu, 11 Oct 2018 13:27:55 +0200 Subject: [PATCH 087/282] fix doc for addImportPath, should also accept callable as input --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 637f1c1c..ed80d8d3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3308,7 +3308,7 @@ public function getParsedFiles() * * @api * - * @param string $path + * @param string|callable $path */ public function addImportPath($path) { From b931793663ac56d57d5f87f9f6f3f50cf6b73aa0 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Tue, 23 Oct 2018 23:12:14 +0100 Subject: [PATCH 088/282] Bug fix error thrown from strpos if needle (basePath) is empty and ignore PhpStorm .idea IDE directory --- .gitignore | 1 + src/SourceMap/SourceMapGenerator.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index be6e5ecf..999984a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea .sass-cache scss_cache composer.lock diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 6e969cae..39bfac1c 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -303,7 +303,7 @@ protected function normalizeFilename($filename) $basePath = $this->options['sourceMapBasepath']; // "Trim" the 'sourceMapBasepath' from the output filename. - if (strpos($filename, $basePath) === 0) { + if (strlen($basePath) && strpos($filename, $basePath) === 0) { $filename = substr($filename, strlen($basePath)); } From a0e5590e65e33ad6c10e4c98e4aa7c8563779ee8 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Wed, 24 Oct 2018 00:41:31 +0100 Subject: [PATCH 089/282] Fix bug where rooted SCSS URIs aren't normalised properly with baseUri provided --- src/Compiler.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..ee4efc96 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3481,9 +3481,12 @@ public function findImport($url) if (is_string($dir)) { // check urls for normal import paths foreach ($urls as $full) { - $full = $dir - . (! empty($dir) && substr($dir, -1) !== '/' ? '/' : '') - . $full; + $separator = ( + !empty($dir) && + substr($dir, -1) !== '/' && + substr($full, 0, 1) !== '/' + ) ? '/' : ''; + $full = $dir . $separator . $full; if ($this->fileExists($file = $full . '.scss') || ($hasExtension && $this->fileExists($file = $full)) From 51041822ae0e8b646e7d86de6c6f81100345f939 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Wed, 24 Oct 2018 00:42:04 +0100 Subject: [PATCH 090/282] Json preference to not escape slashes (opinionated) --- src/SourceMap/SourceMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 39bfac1c..7bb005c4 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -202,7 +202,7 @@ public function generateJson() unset($sourceMap['sourceRoot']); } - return json_encode($sourceMap); + return json_encode($sourceMap, JSON_UNESCAPED_SLASHES); } /** From 94261339c3b11fdec1d230c57a54084c34a8c824 Mon Sep 17 00:00:00 2001 From: Vince Date: Wed, 13 Mar 2019 14:54:47 -0700 Subject: [PATCH 091/282] Fix for "continue" causing a warning in PHP 7.3 "continue statements targeting switch control flow structures will now generate a warning. In PHP such continue statements are equivalent to break, while they behave as continue 2 in other languages." http://php.net/manual/en/migration73.incompatible.php "In PHP the switch statement is considered a looping structure for the purposes of continue. continue behaves like break (when no arguments are passed). If a switch is inside a loop, continue 2 will continue with the next iteration of the outer loop." http://php.net/manual/en/control-structures.continue.php Signed-off-by: Vince --- tests/ScssTest.php | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/ScssTest.php b/tests/ScssTest.php index 96f0d76e..9c412fe3 100644 --- a/tests/ScssTest.php +++ b/tests/ScssTest.php @@ -75,7 +75,7 @@ public function provideTests() if (preg_match('/^\s*def test_([a-z_]+)/', $line, $matches)) { $state = 1; // enter function $name = $matches[1]; - continue; + continue 2; } break; @@ -83,7 +83,7 @@ public function provideTests() case 1: // inside function if ($line === '' || $line[0] === '#') { - continue; + continue 2; } if (preg_match('/= <<([A-Z_]+)\s*$/', $line, $matches) @@ -95,14 +95,14 @@ public function provideTests() ; } - continue; + continue 2; } if (preg_match('/^\s*assert_equal\(< e' ) { - continue; + continue 2; } if (preg_match('/^\s*end\s*$/', $line)) { @@ -168,7 +168,7 @@ public function provideTests() $scss = array(); $css = array(); $style = null; - continue; + continue 2; } $skipped[] = $line; @@ -179,9 +179,9 @@ public function provideTests() // get css if (preg_match('/^CSS\s*$/', $line)) { $state = 3; // get scss - continue; + continue 2; } - + $css[] = $lines[$i]; break; @@ -190,9 +190,9 @@ public function provideTests() // get scss if (preg_match('/^SCSS\s*$/', $line)) { $state = 1; // end of parameter list - continue; + continue 2; } - + $scss[] = $lines[$i]; break; @@ -201,12 +201,12 @@ public function provideTests() // inside block if (preg_match('/^\s*end\s*$/', $line)) { $state = 1; // end block - continue; + continue 2; } if (preg_match('/^\s*assert_equal < Date: Sun, 17 Mar 2019 23:23:12 +0200 Subject: [PATCH 092/282] Avoid infinitely duplicating parts when extending selector --- src/Compiler.php | 7 ++++++- tests/inputs/extending_compound_selector.scss | 19 +++++++++++++++++++ tests/outputs/extending_compound_selector.css | 4 ++++ .../extending_compound_selector.css | 11 +++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/inputs/extending_compound_selector.scss create mode 100644 tests/outputs/extending_compound_selector.css create mode 100644 tests/outputs_numbered/extending_compound_selector.css diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..c73f5b02 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -463,7 +463,12 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $tempReplacement = $k > 0 ? array_slice($new, $k) : $new; for ($l = count($tempReplacement) - 1; $l >= 0; $l--) { - $slice = $tempReplacement[$l]; + $slice = []; + foreach ($tempReplacement[$l] as $chunk) { + if (!in_array($chunk, $slice)) { + $slice[] = $chunk; + } + } array_unshift($replacement, $slice); if (! $this->isImmediateRelationshipCombinator(end($slice))) { diff --git a/tests/inputs/extending_compound_selector.scss b/tests/inputs/extending_compound_selector.scss new file mode 100644 index 00000000..669591fd --- /dev/null +++ b/tests/inputs/extending_compound_selector.scss @@ -0,0 +1,19 @@ +.foo { + &:after { + color: red; + } + &:hover:after { + color: blue; + } +} + +.bar { + @extend .foo; + &:before { + @extend .bar:after; + } +} + +.baz.bar:before { + @extend .bar:before; +} diff --git a/tests/outputs/extending_compound_selector.css b/tests/outputs/extending_compound_selector.css new file mode 100644 index 00000000..7e8cb599 --- /dev/null +++ b/tests/outputs/extending_compound_selector.css @@ -0,0 +1,4 @@ +.foo:after, .bar:after, .bar:before, .baz.bar:before { + color: red; } + .foo:hover:after, .bar:hover:after, .bar:before:hover, .baz.bar:before:hover { + color: blue; } diff --git a/tests/outputs_numbered/extending_compound_selector.css b/tests/outputs_numbered/extending_compound_selector.css new file mode 100644 index 00000000..50685dcf --- /dev/null +++ b/tests/outputs_numbered/extending_compound_selector.css @@ -0,0 +1,11 @@ +/* line 1, inputs/extending_compound_selector.scss */ +/* line 2, inputs/extending_compound_selector.scss */ + .foo:after, .bar:after, .bar:before, .baz.bar:before { + color: red; } +/* line 5, inputs/extending_compound_selector.scss */ +.foo:hover:after, .bar:hover:after, .bar:before:hover, .baz.bar:before:hover { + color: blue; } +/* line 10, inputs/extending_compound_selector.scss */ +/* line 12, inputs/extending_compound_selector.scss */ + +/* line 17, inputs/extending_compound_selector.scss */ From 405ebd336c86b397841eb3d17af1aa9200ca2b41 Mon Sep 17 00:00:00 2001 From: Vjacheslav Trushkin Date: Sun, 17 Mar 2019 23:56:10 +0200 Subject: [PATCH 093/282] Change private properties to protected in compiler --- src/Compiler.php | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..f9934aa2 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -145,18 +145,18 @@ class Compiler protected $charsetSeen; protected $sourceNames; - private $indentLevel; - private $commentsSeen; - private $extends; - private $extendsMap; - private $parsedFiles; - private $parser; - private $sourceIndex; - private $sourceLine; - private $sourceColumn; - private $stderr; - private $shouldEvaluate; - private $ignoreErrors; + protected $indentLevel; + protected $commentsSeen; + protected $extends; + protected $extendsMap; + protected $parsedFiles; + protected $parser; + protected $sourceIndex; + protected $sourceLine; + protected $sourceColumn; + protected $stderr; + protected $shouldEvaluate; + protected $ignoreErrors; /** * Constructor @@ -817,7 +817,7 @@ protected function compileAtRoot(Block $block) * * @return array */ - private function spliceTree($envs, Block $block, $without) + protected function spliceTree($envs, Block $block, $without) { $newBlock = null; @@ -894,7 +894,7 @@ private function spliceTree($envs, Block $block, $without) * * @return integer */ - private function compileWith($with) + protected function compileWith($with) { static $mapping = [ 'rule' => self::WITH_RULE, @@ -945,7 +945,7 @@ private function compileWith($with) * * @return \Leafo\ScssPhp\Compiler\Environment */ - private function filterWithout($envs, $without) + protected function filterWithout($envs, $without) { $filtered = []; @@ -968,7 +968,7 @@ private function filterWithout($envs, $without) * * @return boolean */ - private function isWithout($without, Block $block) + protected function isWithout($without, Block $block) { if ((($without & static::WITH_RULE) && isset($block->selectors)) || (($without & static::WITH_MEDIA) && @@ -2215,7 +2215,7 @@ protected function reduce($value, $inExp = false) * * @return array|null */ - private function fncall($name, $argValues) + protected function fncall($name, $argValues) { // SCSS @function if ($this->callScssFunction($name, $argValues, $returnValue)) { @@ -3016,7 +3016,7 @@ protected function multiplyMedia(Environment $env = null, $childQueries = null) * * @return array */ - private function compactEnv(Environment $env) + protected function compactEnv(Environment $env) { for ($envs = []; $env; $env = $env->parent) { $envs[] = $env; @@ -3032,7 +3032,7 @@ private function compactEnv(Environment $env) * * @return \Leafo\ScssPhp\Compiler\Environment */ - private function extractEnv($envs) + protected function extractEnv($envs) { for ($env = null; $e = array_pop($envs);) { $e->parent = $env; @@ -3864,7 +3864,7 @@ protected function applyArguments($argDef, $argValues) * * @return array|\Leafo\ScssPhp\Node\Number */ - private function coerceValue($value) + protected function coerceValue($value) { if (is_array($value) || $value instanceof \ArrayAccess) { return $value; @@ -4203,7 +4203,7 @@ public function toHSL($red, $green, $blue) * * @return float */ - private function hueToRGB($m1, $m2, $h) + protected function hueToRGB($m1, $m2, $h) { if ($h < 0) { $h += 1; From 20d88db8a832429c2a4cac035c32a63b968cb6f4 Mon Sep 17 00:00:00 2001 From: josh Date: Sun, 27 Dec 2015 13:30:27 -0700 Subject: [PATCH 094/282] add matchChar() --- src/Parser.php | 168 ++++++++++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 70 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 748d38ae..5114cdab 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -286,7 +286,7 @@ protected function parseChunk() if ($this->literal('@at-root') && ($this->selectors($selector) || true) && ($this->map($with) || true) && - $this->literal('{') + $this->matchChar('{') ) { $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s); $atRoot->selector = $selector; @@ -297,7 +297,7 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) { + if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->matchChar('{')) { $media = $this->pushSpecialBlock(Type::T_MEDIA, $s); $media->queryList = $mediaQueryList[2]; @@ -309,7 +309,7 @@ protected function parseChunk() if ($this->literal('@mixin') && $this->keyword($mixinName) && ($this->argumentDef($args) || true) && - $this->literal('{') + $this->matchChar('{') ) { $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s); $mixin->name = $mixinName; @@ -322,11 +322,11 @@ protected function parseChunk() if ($this->literal('@include') && $this->keyword($mixinName) && - ($this->literal('(') && + ($this->matchChar('(') && ($this->argValues($argValues) || true) && - $this->literal(')') || true) && + $this->matchChar(')') || true) && ($this->end() || - $this->literal('{') && $hasBlock = true) + $this->matchChar('{') && $hasBlock = true) ) { $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null]; @@ -391,7 +391,7 @@ protected function parseChunk() if ($this->literal('@function') && $this->keyword($fnName) && $this->argumentDef($args) && - $this->literal('{') + $this->matchChar('{') ) { $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s); $func->name = $fnName; @@ -431,7 +431,7 @@ protected function parseChunk() $this->genericList($varNames, 'variable', ',', false) && $this->literal('in') && $this->valueList($list) && - $this->literal('{') + $this->matchChar('{') ) { $each = $this->pushSpecialBlock(Type::T_EACH, $s); @@ -448,7 +448,7 @@ protected function parseChunk() if ($this->literal('@while') && $this->expression($cond) && - $this->literal('{') + $this->matchChar('{') ) { $while = $this->pushSpecialBlock(Type::T_WHILE, $s); $while->cond = $cond; @@ -465,7 +465,7 @@ protected function parseChunk() ($this->literal('through') || ($forUntil = true && $this->literal('to'))) && $this->expression($end) && - $this->literal('{') + $this->matchChar('{') ) { $for = $this->pushSpecialBlock(Type::T_FOR, $s); $for->var = $varName[1]; @@ -478,7 +478,7 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) { + if ($this->literal('@if') && $this->valueList($cond) && $this->matchChar('{')) { $if = $this->pushSpecialBlock(Type::T_IF, $s); $if->cond = $cond; $if->cases = []; @@ -535,9 +535,9 @@ protected function parseChunk() list(, $if) = $last; if ($this->literal('@else')) { - if ($this->literal('{')) { + if ($this->matchChar('{')) { $else = $this->pushSpecialBlock(Type::T_ELSE, $s); - } elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) { + } elseif ($this->literal('if') && $this->valueList($cond) && $this->matchChar('{')) { $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s); $else->cond = $cond; } @@ -576,10 +576,10 @@ protected function parseChunk() $this->seek($s); // doesn't match built in directive, do generic one - if ($this->literal('@', false) && + if ($this->matchChar('@', false) && $this->keyword($dirName) && ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) && - $this->literal('{') + $this->matchChar('{') ) { if ($dirName === 'media') { $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s); @@ -617,7 +617,7 @@ protected function parseChunk() // variable assigns if ($this->variable($name) && - $this->literal(':') && + $this->matchChar(':') && $this->valueList($value) && $this->end() ) { @@ -636,7 +636,7 @@ protected function parseChunk() } // opening css block - if ($this->selectors($selectors) && $this->literal('{')) { + if ($this->selectors($selectors) && $this->matchChar('{')) { $this->pushBlock($selectors, $s); return true; @@ -645,7 +645,7 @@ protected function parseChunk() $this->seek($s); // property assign, or nested assign - if ($this->propertyName($name) && $this->literal(':')) { + if ($this->propertyName($name) && $this->matchChar(':')) { $foundSomething = false; if ($this->valueList($value)) { @@ -653,7 +653,7 @@ protected function parseChunk() $foundSomething = true; } - if ($this->literal('{')) { + if ($this->matchChar('{')) { $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s); $propBlock->prefix = $name; $foundSomething = true; @@ -669,7 +669,7 @@ protected function parseChunk() $this->seek($s); // closing a block - if ($this->literal('}')) { + if ($this->matchChar('}')) { $block = $this->popBlock(); if (isset($block->type) && $block->type === Type::T_INCLUDE) { @@ -686,7 +686,7 @@ protected function parseChunk() } // extra stuff - if ($this->literal(';') || + if ($this->matchChar(';') || $this->literal('')) { + if ($this->literal('-->', 3)) { return true; } @@ -687,7 +687,7 @@ protected function parseChunk() // extra stuff if ($this->matchChar(';') || - $this->literal('