diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/GuardValue.java b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/GuardValue.java new file mode 100644 index 00000000..11b42fba --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/GuardValue.java @@ -0,0 +1,28 @@ +package com.github.sommeri.less4j.core.compiler.expressions; + +import com.github.sommeri.less4j.core.compiler.stages.MixinCompilationResult; +import com.github.sommeri.less4j.utils.ArraysUtils.Filter; + +public enum GuardValue { + USE, DO_NOT_USE, USE_IF_DEFAULT, USE_IF_NOT_DEFAULT; + + public Filter filter() { + return new DefaultFunctionUseFilter(this); + } +} + +class DefaultFunctionUseFilter implements Filter { + + private final GuardValue value; + + public DefaultFunctionUseFilter(GuardValue value) { + super(); + this.value = value; + } + + @Override + public boolean accept(MixinCompilationResult t) { + return t.getGuardValue().equals(value); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/MixinsGuardsValidator.java b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/MixinsGuardsValidator.java index 3cbab6b4..70f732af 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/MixinsGuardsValidator.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/MixinsGuardsValidator.java @@ -31,4 +31,28 @@ public DefaultPoweredExpressionEvaluator(IScope scope, ProblemsHandler problemsH } + public GuardValue evaluateGuards(ReusableStructure mixin) { + boolean ifDefaultGuardValue = guardsSatisfied(mixin, true); + boolean ifNotDefaultGuardValue = guardsSatisfied(mixin, false); + return toDefaultFunctionUse(ifDefaultGuardValue, ifNotDefaultGuardValue); + } + + /** + * Re-implementing less.js heuristic. If guards value does not depend on default value, then less.js + * assumes the default was not used. It does not check whether the default function was really used, so + * this: not(default()), (default()) can be used multiple times. + */ + private GuardValue toDefaultFunctionUse(boolean ifDefaultGuardValue, boolean ifNotDefaultGuardValue) { + if (ifDefaultGuardValue && ifNotDefaultGuardValue) {//default was NOT used + return GuardValue.USE; + } else if (!ifDefaultGuardValue && !ifNotDefaultGuardValue) {//default was NOT used + return GuardValue.DO_NOT_USE; + } else if (ifDefaultGuardValue) {//default is required + return GuardValue.USE_IF_DEFAULT; + } else {//if (must not be default) + return GuardValue.USE_IF_NOT_DEFAULT; + }// + } + + } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/FullDetachedRulesetDefinition.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/FullDetachedRulesetDefinition.java new file mode 100644 index 00000000..7b76ac0b --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/FullDetachedRulesetDefinition.java @@ -0,0 +1,34 @@ +package com.github.sommeri.less4j.core.compiler.scopes; + +import com.github.sommeri.less4j.core.ast.DetachedRuleset; + +public class FullDetachedRulesetDefinition { + private final DetachedRuleset detached; + private final IScope bodyScope; + + public FullDetachedRulesetDefinition(DetachedRuleset detached, IScope mixinsBodyScope) { + super(); + this.detached = detached; + this.bodyScope = mixinsBodyScope; + } + + public DetachedRuleset getDetached() { + return detached; + } + + public IScope getScope() { + return bodyScope; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("FullDetachedDefinition [detached ["); + builder.append(detached.getSourceLine()).append(":").append(detached.getSourceColumn()); + builder.append("], bodyScope="); + builder.append(bodyScope); + builder.append("]"); + return builder.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScopeData.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScopeData.java index 0a193074..feed0429 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScopeData.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScopeData.java @@ -1,10 +1,17 @@ package com.github.sommeri.less4j.core.compiler.scopes.local; +import java.util.HashMap; +import java.util.Map; + +import com.github.sommeri.less4j.core.ast.DetachedRuleset; +import com.github.sommeri.less4j.core.compiler.scopes.FullDetachedRulesetDefinition; + public class LocalScopeData implements Cloneable { private VariablesDeclarationsStorage variables = new VariablesDeclarationsStorage(); private MixinsDefinitionsStorage mixins = new MixinsDefinitionsStorage(); + private Map detachedRulesetsScopes = new HashMap(); @Override public LocalScopeData clone() { @@ -12,6 +19,7 @@ public LocalScopeData clone() { LocalScopeData clone = (LocalScopeData) super.clone(); clone.variables = variables.clone(); clone.mixins = mixins.clone(); + clone.detachedRulesetsScopes = new HashMap(detachedRulesetsScopes); return clone; } catch (CloneNotSupportedException e) { @@ -32,6 +40,7 @@ public String toString() { StringBuilder result = new StringBuilder(getClass().getSimpleName()).append("\n"); result.append("**Variables storage: ").append(variables).append("\n\n"); result.append("**Mixins storage: ").append(mixins).append("\n\n"); + result.append("**DetachedRulesets size: ").append(detachedRulesetsScopes.size()).append("\n\n"); return result.toString(); } } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/DefaultGuardHelper.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/DefaultGuardHelper.java new file mode 100644 index 00000000..2b0ebf38 --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/DefaultGuardHelper.java @@ -0,0 +1,80 @@ +package com.github.sommeri.less4j.core.compiler.stages; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import com.github.sommeri.less4j.core.ast.MixinReference; +import com.github.sommeri.less4j.core.ast.ReusableStructure; +import com.github.sommeri.less4j.core.compiler.expressions.GuardValue; +import com.github.sommeri.less4j.core.problems.BugHappened; +import com.github.sommeri.less4j.core.problems.ProblemsHandler; +import com.github.sommeri.less4j.utils.ArraysUtils; + +public class DefaultGuardHelper { + + private final ProblemsHandler problemsHandler; + + public DefaultGuardHelper(ProblemsHandler problemsHandler) { + this.problemsHandler = problemsHandler; + } + + public List chooseMixinsToBeUsed(List compiledMixins, final MixinReference reference) { + // count how many mixins of each kind we encountered + int normalMixinsCnt = ArraysUtils.count(compiledMixins, GuardValue.USE.filter()); + int ifNotCnt = ArraysUtils.count(compiledMixins, GuardValue.USE_IF_NOT_DEFAULT.filter()); + int ifDefaultCnt = ArraysUtils.count(compiledMixins, GuardValue.USE_IF_DEFAULT.filter()); + + //sanity check - could be removed - keeping only for debugging purposes + if (normalMixinsCnt+ifNotCnt+ifDefaultCnt!=compiledMixins.size()) + throw new BugHappened("Unexpected mixin type in compiled mixins list.", reference); + + // We know now that default() value is false. We do not care whether there was some potentional ambiguity or not and return anything that is not default. + if (normalMixinsCnt > 0) { + return keepOnly(compiledMixins, GuardValue.USE, GuardValue.USE_IF_NOT_DEFAULT); + } + + //there are multiple mixins using default() function and nothing else - that is ambiguous (period). + if (ifDefaultCnt+ifNotCnt > 1) { + List errorSet = keepOnly(compiledMixins, GuardValue.USE_IF_DEFAULT,GuardValue.USE_IF_NOT_DEFAULT); + problemsHandler.ambiguousDefaultSet(reference, extractOriginalMixins(errorSet)); + //no mixins are going to be used + return Collections.emptyList(); + } + + //now we know that default function returns true + return keepOnly(compiledMixins, GuardValue.USE_IF_DEFAULT); + } + + /** + * Removes all comiled mixins from compiledMixins list with wrong use of default function. + * Warning: Modifies the compiledMixins list. + + * @param compiledMixins - list of compiled mixins - will be modified. + * @param kind - types of mixins that are going to stay. + * @return compiledMixins - for convenience + */ + private List keepOnly(List compiledMixins, GuardValue... kind) { + Set expectedUses = ArraysUtils.asSet(kind); + Iterator iterator = compiledMixins.iterator(); + while (iterator.hasNext()) { + MixinCompilationResult compiled = iterator.next(); + if (!expectedUses.contains(compiled.getGuardValue())) { + iterator.remove(); + } + } + return compiledMixins; + } + + private List extractOriginalMixins(List compiledMixins) { + List result = new ArrayList(); + for (MixinCompilationResult compiled : compiledMixins) { + result.add(compiled.getMixin()); + } + return result; + } + + +} diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinCompilationResult.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinCompilationResult.java new file mode 100644 index 00000000..e068cbcc --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinCompilationResult.java @@ -0,0 +1,61 @@ +package com.github.sommeri.less4j.core.compiler.stages; + +import java.util.List; + +import com.github.sommeri.less4j.core.ast.ASTCssNode; +import com.github.sommeri.less4j.core.ast.ReusableStructure; +import com.github.sommeri.less4j.core.compiler.expressions.GuardValue; +import com.github.sommeri.less4j.core.compiler.scopes.IScope; +import com.github.sommeri.less4j.utils.ArraysUtils; + +public class MixinCompilationResult { + + private ReusableStructure mixin; + private List replacement; + private IScope returnValues; + private GuardValue guardValue; + + public MixinCompilationResult(ReusableStructure mixin, List replacement, IScope returnValues) { + this.mixin = mixin; + this.replacement = replacement; + this.returnValues = returnValues; + } + + public void setGuardValue(GuardValue guardValue) { + this.guardValue = guardValue; + } + + public GuardValue getGuardValue() { + return guardValue; + } + + public List getReplacement() { + return replacement; + } + + public void setReplacement(List replacement) { + this.replacement = replacement; + } + + public IScope getReturnValues() { + return returnValues; + } + + public void setReturnValues(IScope returnValues) { + this.returnValues = returnValues; + } + + public ReusableStructure getMixin() { + return mixin; + } + + public void setMixin(ReusableStructure mixin) { + this.mixin = mixin; + } + + @Override + protected MixinCompilationResult clone() { + return new MixinCompilationResult(mixin.clone(), ArraysUtils.deeplyClonedList(replacement), returnValues); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsSolver.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsSolver.java index b487b62e..dbfc4c58 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsSolver.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsSolver.java @@ -1,10 +1,7 @@ package com.github.sommeri.less4j.core.compiler.stages; import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; import java.util.List; -import java.util.Set; import com.github.sommeri.less4j.LessCompiler.Configuration; import com.github.sommeri.less4j.core.ast.ASTCssNode; @@ -15,6 +12,7 @@ import com.github.sommeri.less4j.core.ast.MixinReference; import com.github.sommeri.less4j.core.ast.ReusableStructure; import com.github.sommeri.less4j.core.compiler.expressions.ExpressionEvaluator; +import com.github.sommeri.less4j.core.compiler.expressions.GuardValue; import com.github.sommeri.less4j.core.compiler.expressions.MixinsGuardsValidator; import com.github.sommeri.less4j.core.compiler.scopes.FullMixinDefinition; import com.github.sommeri.less4j.core.compiler.scopes.IScope; @@ -23,10 +21,7 @@ import com.github.sommeri.less4j.core.compiler.scopes.InScopeSnapshotRunner.ITask; import com.github.sommeri.less4j.core.compiler.scopes.ScopeFactory; import com.github.sommeri.less4j.core.compiler.scopes.view.ScopeView; -import com.github.sommeri.less4j.core.problems.BugHappened; import com.github.sommeri.less4j.core.problems.ProblemsHandler; -import com.github.sommeri.less4j.utils.ArraysUtils; -import com.github.sommeri.less4j.utils.ArraysUtils.Filter; class MixinsSolver { @@ -34,12 +29,14 @@ class MixinsSolver { private final ReferencesSolver parentSolver; private final AstNodesStack semiCompiledNodes; private final Configuration configuration; + private final DefaultGuardHelper defaultGuardHelper; public MixinsSolver(ReferencesSolver parentSolver, AstNodesStack semiCompiledNodes, ProblemsHandler problemsHandler, Configuration configuration) { this.parentSolver = parentSolver; this.semiCompiledNodes = semiCompiledNodes; this.problemsHandler = problemsHandler; this.configuration = configuration; + this.defaultGuardHelper = new DefaultGuardHelper(problemsHandler); } private MixinCompilationResult resolveMixinReference(final IScope callerScope, final FullMixinDefinition referencedMixin, final IScope mixinWorkingScope, final ExpressionEvaluator expressionEvaluator) { @@ -135,17 +132,16 @@ public void run() { IScope mixinWorkingScope = calculateMixinsWorkingScope(callerScope, mixinArguments, mixinScope); MixinsGuardsValidator guardsValidator = new MixinsGuardsValidator(mixinWorkingScope, problemsHandler, configuration); - boolean ifDefaultGuardValue = guardsValidator.guardsSatisfied(mixin, true); - boolean ifNotDefaultGuardValue = guardsValidator.guardsSatisfied(mixin, false); + GuardValue guardValue = guardsValidator.evaluateGuards(mixin); // if none of them is true, then we do not need mixin no matter what - if (ifDefaultGuardValue || ifNotDefaultGuardValue) { + if (guardValue!=GuardValue.DO_NOT_USE) { //OPTIMIZATION POSSIBLE: there is no need to compile mixins at this point, some of them are not going to be //used and create snapshot operation is cheap now. It should be done later on. ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator(mixinWorkingScope, problemsHandler, configuration); MixinCompilationResult compiled = resolveMixinReference(callerScope, fullMixin, mixinWorkingScope, expressionEvaluator); //mark the mixin according to its default() function use - compiled.setDefaultFunctionUse(toDefaultFunctionUse(ifDefaultGuardValue, ifNotDefaultGuardValue)); + compiled.setGuardValue(guardValue); //store the mixin as candidate compiledMixins.add(compiled); } @@ -154,7 +150,7 @@ public void run() { } // filter out mixins we do not want to use - List mixinsToBeUsed = chooseMixinsToBeUsed(compiledMixins, reference); + List mixinsToBeUsed = defaultGuardHelper.chooseMixinsToBeUsed(compiledMixins, reference); // update mixin replacements and update scope with imported variables and mixins for (MixinCompilationResult compiled : mixinsToBeUsed) { @@ -169,76 +165,6 @@ public void run() { return result; } - private List chooseMixinsToBeUsed(List compiledMixins, final MixinReference reference) { - // count how many mixins of each kind we encountered - int normalMixinsCnt = ArraysUtils.count(compiledMixins, DefaultFunctionUse.DEFAULT_OBLIVIOUS.filter()); - int ifNotCnt = ArraysUtils.count(compiledMixins, DefaultFunctionUse.ONLY_IF_NOT_DEFAULT.filter()); - int ifDefaultCnt = ArraysUtils.count(compiledMixins, DefaultFunctionUse.ONLY_IF_DEFAULT.filter()); - - //sanity check - could be removed - keeping only for debugging purposes - if (normalMixinsCnt+ifNotCnt+ifDefaultCnt!=compiledMixins.size()) - throw new BugHappened("Unexpected mixin type in compiled mixins list.", reference); - - // We know now that default() value is false. We do not care whether there was some potentional ambiguity or not and return anything that is not default. - if (normalMixinsCnt > 0) { - return keepOnly(compiledMixins, DefaultFunctionUse.DEFAULT_OBLIVIOUS, DefaultFunctionUse.ONLY_IF_NOT_DEFAULT); - } - - //there are multiple mixins using default() function and nothing else - that is ambiguous (period). - if (ifDefaultCnt+ifNotCnt > 1) { - List errorSet = keepOnly(compiledMixins, DefaultFunctionUse.ONLY_IF_DEFAULT,DefaultFunctionUse.ONLY_IF_NOT_DEFAULT); - problemsHandler.ambiguousDefaultSet(reference, extractOriginalMixins(errorSet)); - //no mixins are going to be used - return Collections.emptyList(); - } - - //now we know that default function returns true - return keepOnly(compiledMixins, DefaultFunctionUse.ONLY_IF_DEFAULT); - } - - /** - * Removes all comiled mixins from compiledMixins list with wrong use of default function. - * Warning: Modifies the compiledMixins list. - - * @param compiledMixins - list of compiled mixins - will be modified. - * @param kind - types of mixins that are going to stay. - * @return compiledMixins - for convenience - */ - private List keepOnly(List compiledMixins, DefaultFunctionUse... kind) { - Set expectedUses = ArraysUtils.asSet(kind); - Iterator iterator = compiledMixins.iterator(); - while (iterator.hasNext()) { - MixinCompilationResult compiled = iterator.next(); - if (!expectedUses.contains(compiled.getDefaultFunctionUse())) { - iterator.remove(); - } - } - return compiledMixins; - } - - private List extractOriginalMixins(List compiledMixins) { - List result = new ArrayList(); - for (MixinCompilationResult compiled : compiledMixins) { - result.add(compiled.getMixin()); - } - return result; - } - - /** - * Re-implementing less.js heuristic. If guards value does not depend on default value, then less.js - * assumes the default was not used. It does not check whether the default function was really used, so - * this: not(default()), (default()) can be used multiple times. - */ - protected DefaultFunctionUse toDefaultFunctionUse(boolean ifDefaultGuardValue, boolean ifNotDefaultGuardValue) { - if (ifDefaultGuardValue == ifNotDefaultGuardValue) {//default was NOT used - return DefaultFunctionUse.DEFAULT_OBLIVIOUS; - } else if (ifDefaultGuardValue) {//default is required - return DefaultFunctionUse.ONLY_IF_DEFAULT; - } else {//if (must not be default) - return DefaultFunctionUse.ONLY_IF_NOT_DEFAULT; - }// - } - private void resolveImportance(MixinReference reference, GeneralBody result) { if (reference.isImportant()) { declarationsAreImportant(result); @@ -276,78 +202,3 @@ private static IScope calculateMixinsWorkingScope(IScope callerScope, IScope arg } -class MixinCompilationResult { - - private ReusableStructure mixin; - private List replacement; - private IScope returnValues; - private DefaultFunctionUse defaultFunctionUse; - - public MixinCompilationResult(ReusableStructure mixin, List replacement, IScope returnValues) { - this.mixin = mixin; - this.replacement = replacement; - this.returnValues = returnValues; - } - - public void setDefaultFunctionUse(DefaultFunctionUse defaultFunctionUse) { - this.defaultFunctionUse = defaultFunctionUse; - } - - public DefaultFunctionUse getDefaultFunctionUse() { - return defaultFunctionUse; - } - - public List getReplacement() { - return replacement; - } - - public void setReplacement(List replacement) { - this.replacement = replacement; - } - - public IScope getReturnValues() { - return returnValues; - } - - public void setReturnValues(IScope returnValues) { - this.returnValues = returnValues; - } - - public ReusableStructure getMixin() { - return mixin; - } - - public void setMixin(ReusableStructure mixin) { - this.mixin = mixin; - } - - @Override - protected MixinCompilationResult clone() { - return new MixinCompilationResult(mixin.clone(), ArraysUtils.deeplyClonedList(replacement), returnValues); - } - -} - -enum DefaultFunctionUse { - DEFAULT_OBLIVIOUS, ONLY_IF_DEFAULT, ONLY_IF_NOT_DEFAULT; - - public Filter filter() { - return new DefaultFunctionUseFilter(this); - } -} - -class DefaultFunctionUseFilter implements Filter { - - private final DefaultFunctionUse value; - - public DefaultFunctionUseFilter(DefaultFunctionUse value) { - super(); - this.value = value; - } - - @Override - public boolean accept(MixinCompilationResult t) { - return t.getDefaultFunctionUse().equals(value); - } - -} \ No newline at end of file diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java index 4816718c..5d7c6d05 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java @@ -84,11 +84,11 @@ private void unsafeDoSolveReferences(ASTCssNode node, IteratedScope iteratedScop if (!childs.isEmpty()) { IScope scope = iteratedScope.getScope(); - // solve all mixin references and store solutions - Map solvedMixinReferences = solveMixinReferences(childs, scope); + // solve all detached ruleset/mixin references and store solutions + Map solvedMixinReferences = solveCalls(childs, scope); - // solve whatever is not a mixin reference - solveNonMixinReferences(childs, iteratedScope); + // solve whatever is not a mixin/detached ruleset reference + solveNonCalligReferences(childs, iteratedScope); // replace mixin references by their solutions - we need to do it in the end // the scope and ast would get out of sync otherwise @@ -99,7 +99,7 @@ private void unsafeDoSolveReferences(ASTCssNode node, IteratedScope iteratedScop } } - private void solveNonMixinReferences(List childs, IteratedScope iteratedScope) { + private void solveNonCalligReferences(List childs, IteratedScope iteratedScope) { ExpressionEvaluator cssGuardsValidator = new ExpressionEvaluator(iteratedScope.getScope(), problemsHandler, configuration); for (ASTCssNode kid : childs) { if (isMixinReference(kid)) @@ -136,6 +136,10 @@ private boolean isMixinReference(ASTCssNode kid) { return kid.getType() == ASTCssNodeType.MIXIN_REFERENCE; } + private boolean isDetachedRulesetReference(ASTCssNode kid) { + return kid.getType() == ASTCssNodeType.DETACHED_RULESET_REFERENCE; + } + private boolean isRuleset(ASTCssNode kid) { return kid.getType() == ASTCssNodeType.RULE_SET; } @@ -150,7 +154,7 @@ private void replaceMixinReferences(Map solvedMixin } } - private Map solveMixinReferences(List childs, IScope mixinReferenceScope) { + private Map solveCalls(List childs, IScope mixinReferenceScope) { Map solvedMixinReferences = new HashMap(); for (ASTCssNode kid : childs) { if (isMixinReference(kid)) { diff --git a/src/main/java/com/github/sommeri/less4j/core/parser/SuperLessParser.java b/src/main/java/com/github/sommeri/less4j/core/parser/SuperLessParser.java index 61336e91..86ed3be1 100644 --- a/src/main/java/com/github/sommeri/less4j/core/parser/SuperLessParser.java +++ b/src/main/java/com/github/sommeri/less4j/core/parser/SuperLessParser.java @@ -121,7 +121,8 @@ public abstract class SuperLessParser extends Parser { ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("INTERPOLABLE_NAME", "interpolable name"); ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("EMBEDDED_SCRIPT", "embedded script `...`"); ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("ESCAPED_SCRIPT", "escaped script ~`...`"); - ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("DETACHED_RULESET_REFERENCE", "detached ruleset"); + ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("DETACHED_RULESET", "detached ruleset"); + ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("DETACHED_RULESET_REFERENCE", "detached ruleset call"); } diff --git a/src/test/java/com/github/sommeri/less4j/SimpleCssTest.java b/src/test/java/com/github/sommeri/less4j/SimpleCssTest.java index e5a27935..30cba7c1 100644 --- a/src/test/java/com/github/sommeri/less4j/SimpleCssTest.java +++ b/src/test/java/com/github/sommeri/less4j/SimpleCssTest.java @@ -19,7 +19,7 @@ * from the master branch. * */ -//@Ignore +@Ignore @RunWith(Parameterized.class) public class SimpleCssTest extends AbstractFileBasedTest { diff --git a/src/test/java/com/github/sommeri/less4j/compiler/DetachedRulesetsTest.java b/src/test/java/com/github/sommeri/less4j/compiler/DetachedRulesetsTest.java index 61313d64..91b84b2d 100644 --- a/src/test/java/com/github/sommeri/less4j/compiler/DetachedRulesetsTest.java +++ b/src/test/java/com/github/sommeri/less4j/compiler/DetachedRulesetsTest.java @@ -6,6 +6,41 @@ import org.junit.Ignore; import org.junit.runners.Parameterized.Parameters; +//FIXME: call on variable with wrong datatype in +//FIXME: test order detached mixin imports who sees who and who overwrites who + +/* + * .wrap-mixin(@ruleset) { + .wrap-selector { + @d: invisible; + @ruleset(); + } +}; + +.mixin() { + abusing: less; +} + +.wrap-mixin({ + two: @d; + .mixin(); +}); + +.selector { + @nosemi: {}; + @indirect: "nosemi"; + @indirect(); +} + +// ******************* another ************************** +@detached: { color: blue; }; +.selector { + interpolation: "@{detached}"; +} + + + + */ @Ignore public class DetachedRulesetsTest extends BasicFeaturesTest { diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.css b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.css index decc2c38..d5a2b834 100644 --- a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.css +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.css @@ -69,3 +69,6 @@ .caller { value: in detached; } +.selector-indirect { + color: #aabbcc; +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.less b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.less index 122d7aa4..cd63e737 100644 --- a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.less +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-basics.less @@ -141,6 +141,7 @@ @caller-default: caller; @with-mixin-caller-default(); } + //unlock mixin see things defined in his scope first @with-mixin-unlocking: { .mixin() { @@ -153,3 +154,13 @@ @with-mixin-unlocking(); .mixin(); } + +//detached returned from mixin +.return-detached() { + @color: #aabbcc; + @detached: {color: @color;} +} +.selector-indirect { + .return-detached(); + @detached(); +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-scoping.css b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-scoping.css new file mode 100644 index 00000000..ccef8d19 --- /dev/null +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-scoping.css @@ -0,0 +1,6 @@ +.selector-direct { + color: #008000; +} +.selector-indirect { + color: #ff0000; +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-scoping.less b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-scoping.less new file mode 100644 index 00000000..1628b966 --- /dev/null +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-scoping.less @@ -0,0 +1,27 @@ +.whoReallyCalls(@detached) { + @detached(); +} + +.modifyScope(@detached) { + .whoReallyCalls(@detached); + @color: green; +} + +.selector-direct { + .modifyScope(@detached); + @detached: { color: @color; }; +} + +.modifyScope-indirect(@detached) { + .mixin(); + .whoReallyCalls(@detached); + .mixin() { + @color: red; + } +} + +.selector-indirect { + .modifyScope-indirect(@detached); + @detached: { color: @color; }; +} + diff --git a/src/test/resources/minitests/debug1.less b/src/test/resources/minitests/debug1.less index 6563969b..2a54cf34 100644 --- a/src/test/resources/minitests/debug1.less +++ b/src/test/resources/minitests/debug1.less @@ -1,8 +1,5 @@ -.widget(@detached) { - @detached(); +.selector { + @detached: { extreme: simplicity; }; + @detached(); } -ruleset { - @detached: { property: value; }; - .widget(@detached); -}