From d5bf4dfc50d7a70e3d5fe0528dff85d194194ea9 Mon Sep 17 00:00:00 2001 From: Matthew Dean Date: Tue, 19 Jun 2018 20:47:29 -0700 Subject: [PATCH] Fixes #3191 --- lib/less/contexts.js | 16 ++++++++++++++++ lib/less/parser/parser.js | 8 ++++++-- lib/less/tree/call.js | 11 ++++++++--- lib/less/tree/expression.js | 5 +++-- test/css/calc.css | 12 ++++++++++++ test/less/calc.less | 18 ++++++++++++++++++ 6 files changed, 63 insertions(+), 7 deletions(-) diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 4d21f7662..8f8a22f75 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -62,6 +62,21 @@ contexts.Eval = function(options, frames) { this.importantScope = this.importantScope || []; }; +contexts.Eval.prototype.enterCalc = function () { + if (!this.calcStack) { + this.calcStack = []; + } + this.calcStack.push(true); + this.inCalc = true; +}; + +contexts.Eval.prototype.exitCalc = function () { + this.calcStack.pop(); + if (!this.calcStack) { + this.inCalc = false; + } +}; + contexts.Eval.prototype.inParenthesis = function () { if (!this.parensStack) { this.parensStack = []; @@ -73,6 +88,7 @@ contexts.Eval.prototype.outOfParenthesis = function () { this.parensStack.pop(); }; +contexts.Eval.prototype.inCalc = false; contexts.Eval.prototype.mathOn = true; contexts.Eval.prototype.isMathOn = function () { if (!this.mathOn) { diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index ef8357ce4..dc1899fe4 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -351,13 +351,17 @@ var Parser = function Parser(context, imports, fileInfo) { // // "milky way" 'he\'s the one!' // - quoted: function () { + quoted: function (forceEscaped) { var str, index = parserInput.i, isEscaped = false; parserInput.save(); if (parserInput.$char("~")) { isEscaped = true; + } else if (forceEscaped) { + parserInput.restore(); + return; } + str = parserInput.$quoted(); if (!str) { parserInput.restore(); @@ -1883,7 +1887,7 @@ var Parser = function Parser(context, imports, fileInfo) { var o = this.sub() || entities.dimension() || entities.color() || entities.variable() || entities.property() || entities.call() || - entities.colorKeyword(); + entities.quoted(true) || entities.colorKeyword(); if (negate) { o.parensInOp = true; diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index 00dc7b5df..c42b4b8b3 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -7,7 +7,7 @@ var Node = require("./node"), var Call = function (name, args, index, currentFileInfo) { this.name = name; this.args = args; - this.mathOn = name === 'calc' ? false : true; + this.calc = name === 'calc'; this._index = index; this._fileInfo = currentFileInfo; }; @@ -30,13 +30,18 @@ Call.prototype.accept = function (visitor) { // The function should receive the value, not the variable. // Call.prototype.eval = function (context) { - /** * Turn off math for calc(), and switch back on for evaluating nested functions */ var currentMathContext = context.mathOn; - context.mathOn = this.mathOn; + context.mathOn = !this.calc; + if (this.calc || context.inCalc) { + context.enterCalc(); + } var args = this.args.map(function (a) { return a.eval(context); }); + if (this.calc || context.inCalc) { + context.exitCalc(); + } context.mathOn = currentMathContext; var result, funcCaller = new FunctionCaller(this.name, context, this.getIndex(), this.fileInfo()); diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 4f939a6f4..0a89e5224 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -15,6 +15,7 @@ Expression.prototype.accept = function (visitor) { }; Expression.prototype.eval = function (context) { var returnValue, + mathOn = context.isMathOn(), inParenthesis = this.parens && !this.parensInOp, doubleParen = false; if (inParenthesis) { @@ -25,7 +26,7 @@ Expression.prototype.eval = function (context) { return e.eval(context); })); } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { + if (this.value[0].parens && !this.value[0].parensInOp && !context.inCalc) { doubleParen = true; } returnValue = this.value[0].eval(context); @@ -35,7 +36,7 @@ Expression.prototype.eval = function (context) { if (inParenthesis) { context.outOfParenthesis(); } - if (this.parens && this.parensInOp && !(context.isMathOn()) && !doubleParen) { + if (this.parens && this.parensInOp && !mathOn && !doubleParen) { returnValue = new Paren(returnValue); } return returnValue; diff --git a/test/css/calc.css b/test/css/calc.css index 48951ed0d..ec8b7c933 100644 --- a/test/css/calc.css +++ b/test/css/calc.css @@ -1,5 +1,17 @@ .no-math { width: calc(50% + (25vh - 20px)); + height: calc(50% + (25vh - 20px)); + min-height: calc((10vh) + calc(5vh)); foo: 3 calc(3 + 4) 11; bar: calc(1 + 20%); } +.b { + one: calc(100% - (20px)); + two: calc(100% - (10px + 10px)); + three: calc(100% - (3 * 1)); + four: calc(100% - (3 * 1)); + nested: calc(calc(2.25rem + 2px) - 1px * 2); +} +.c { + height: calc(100% - ((10px * 3) + (10px * 2))); +} diff --git a/test/less/calc.less b/test/less/calc.less index df1c7a56d..fb9d21d88 100644 --- a/test/less/calc.less +++ b/test/less/calc.less @@ -1,7 +1,25 @@ .no-math { @var: 50vh/2; width: calc(50% + (@var - 20px)); + height: calc(50% + ((@var - 20px))); + min-height: calc(((10vh)) + calc((5vh))); foo: 1 + 2 calc(3 + 4) 5 + 6; @floor: floor(1 + .1); bar: calc(@floor + 20%); +} + +.b { + @a: 10px; + @b: 10px; + + one: calc(100% - ((min(@a + @b)))); + two: calc(100% - (((@a + @b)))); + three: calc(e('100%') - (3 * 1)); + four: calc(~'100%' - (3 * 1)); + nested: calc(calc(2.25rem + 2px) - 1px * 2); +} + +.c { + @v: 10px; + height: calc(100% - ((@v * 3) + (@v * 2))); } \ No newline at end of file