diff --git a/src/main/antlr3/com/github/sommeri/less4j/core/parser/Less.g b/src/main/antlr3/com/github/sommeri/less4j/core/parser/Less.g index 2b5159ed..2b54743c 100644 --- a/src/main/antlr3/com/github/sommeri/less4j/core/parser/Less.g +++ b/src/main/antlr3/com/github/sommeri/less4j/core/parser/Less.g @@ -319,6 +319,7 @@ top_level_element : (mixinReferenceWithSemi)=>mixinReferenceWithSemi | (namespaceReferenceWithSemi)=>namespaceReferenceWithSemi | (reusableStructureName LPAREN)=>reusableStructure + | (detachedRulesetReference)=>detachedRulesetReference | (variabledeclaration)=>variabledeclaration | ruleSet | media_top_level diff --git a/src/main/java/com/github/sommeri/less4j/LessSource.java b/src/main/java/com/github/sommeri/less4j/LessSource.java index a62afd28..8823f3de 100644 --- a/src/main/java/com/github/sommeri/less4j/LessSource.java +++ b/src/main/java/com/github/sommeri/less4j/LessSource.java @@ -220,6 +220,9 @@ public FileSource(FileSource parent, String filename) { @Override public URI getURI() { + if (inputFile==null) + return null; + try { String path = inputFile.toString(); path = URIUtils.convertPlatformSeparatorToUri(path); @@ -231,7 +234,7 @@ public URI getURI() { @Override public String getName() { - return inputFile.getName(); + return inputFile==null? null: inputFile.getName(); } @Override diff --git a/src/main/java/com/github/sommeri/less4j/core/ast/ASTCssNode.java b/src/main/java/com/github/sommeri/less4j/core/ast/ASTCssNode.java index 1dc728b7..954b5afa 100644 --- a/src/main/java/com/github/sommeri/less4j/core/ast/ASTCssNode.java +++ b/src/main/java/com/github/sommeri/less4j/core/ast/ASTCssNode.java @@ -15,6 +15,9 @@ public abstract class ASTCssNode implements PubliclyCloneable { //I'm using underlying structure as identified in cycle detector. If it stops to be identifying, //cycle detector must be modified. ! private HiddenTokenAwareTree underlyingStructure; +// //A scope used +// private IScope primaryScope; +// // private List openingComments = new ArrayList(); private List orphanComments = new ArrayList(); private List trailingComments = new ArrayList(); diff --git a/src/main/java/com/github/sommeri/less4j/core/ast/BodyOwner.java b/src/main/java/com/github/sommeri/less4j/core/ast/BodyOwner.java index db098969..c1d432f1 100644 --- a/src/main/java/com/github/sommeri/less4j/core/ast/BodyOwner.java +++ b/src/main/java/com/github/sommeri/less4j/core/ast/BodyOwner.java @@ -1,6 +1,10 @@ package com.github.sommeri.less4j.core.ast; -public interface BodyOwner { +import com.github.sommeri.less4j.utils.PubliclyCloneable; + +public interface BodyOwner extends PubliclyCloneable { + + public BodyOwner clone(); void setBody(T body); diff --git a/src/main/java/com/github/sommeri/less4j/core/ast/DetachedRulesetReference.java b/src/main/java/com/github/sommeri/less4j/core/ast/DetachedRulesetReference.java index bbebde08..76f0d077 100644 --- a/src/main/java/com/github/sommeri/less4j/core/ast/DetachedRulesetReference.java +++ b/src/main/java/com/github/sommeri/less4j/core/ast/DetachedRulesetReference.java @@ -12,6 +12,7 @@ public class DetachedRulesetReference extends ASTCssNode { public DetachedRulesetReference(HiddenTokenAwareTree token, Variable variable) { super(token); + this.variable = variable; } public Variable getVariable() { diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionEvaluator.java b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionEvaluator.java index 84ed0626..16fd5674 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionEvaluator.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionEvaluator.java @@ -11,10 +11,10 @@ import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.ASTCssNodeType; import com.github.sommeri.less4j.core.ast.AbstractVariableDeclaration; +import com.github.sommeri.less4j.core.ast.BinaryExpression; import com.github.sommeri.less4j.core.ast.BinaryExpressionOperator.Operator; import com.github.sommeri.less4j.core.ast.ComparisonExpression; import com.github.sommeri.less4j.core.ast.ComparisonExpressionOperator; -import com.github.sommeri.less4j.core.ast.BinaryExpression; import com.github.sommeri.less4j.core.ast.CssString; import com.github.sommeri.less4j.core.ast.EmbeddedScript; import com.github.sommeri.less4j.core.ast.EscapedValue; @@ -40,8 +40,9 @@ import com.github.sommeri.less4j.core.compiler.expressions.strings.StringInterpolator; import com.github.sommeri.less4j.core.compiler.scopes.BasicScope; import com.github.sommeri.less4j.core.compiler.scopes.FullMixinDefinition; +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; +import com.github.sommeri.less4j.core.compiler.scopes.ILocalScope; import com.github.sommeri.less4j.core.compiler.scopes.IScope; -import com.github.sommeri.less4j.core.compiler.scopes.ScopeFactory; import com.github.sommeri.less4j.core.compiler.scopes.ScopesTree; import com.github.sommeri.less4j.core.compiler.scopes.local.LocalScope; import com.github.sommeri.less4j.core.problems.BugHappened; @@ -103,22 +104,6 @@ public List evaluateAll(List expressions) { return values; } - public IScope evaluateValues(IScope scope) { - IScope result = ScopeFactory.createDummyScope(); - result.addFilteredVariables(toEvaluationFilter(), scope); - return result; - } - - private ExpressionFilter toEvaluationFilter() { - return new ExpressionFilter() { - - @Override - public Expression apply(Expression input) { - return evaluate(input); - } - }; - } - public Expression evaluate(CssString input) { String value = stringInterpolator.replaceIn(input.getValue(), this, input.getUnderlyingStructure()); return new CssString(input.getUnderlyingStructure(), value, input.getQuoteType()); @@ -140,42 +125,64 @@ public Expression evaluate(Variable input) { return new FaultyExpression(input); } - Expression value = scope.getValue(input); - if (value == null) { + FullNodeDefinition value = scope.getValue(input); + if (value == null || value.getNode()==null) { problemsHandler.undefinedVariable(input); return new FaultyExpression(input); } + if (!(value.getNode() instanceof Expression)) { + //FIXME !!!!!!!!!!!!!!!!! what to do here???? + return input; + } + Expression expression = (Expression)value.getNode(); cycleDetector.enteringVariableValue(input); - Expression result = evaluate(value); + Expression result = evaluate(expression); cycleDetector.leftVariableValue(); return result; } public Expression evaluateIfPresent(Variable input) { - Expression value = scope.getValue(input); + FullNodeDefinition value = scope.getValue(input); if (value == null) { return null; } - return evaluate(value); + if (!(value.getNode() instanceof Expression)) { + //FIXME !!!!!!!!!!!!!!!!! what to do here???? + return input; + } + Expression expression = (Expression)value.getNode(); + return evaluate(expression); } public Expression evaluate(IndirectVariable input) { - Expression value = scope.getValue(input); - if (!(value instanceof CssString)) { + FullNodeDefinition value = scope.getValue(input); + if (!(value.getNode() instanceof Expression)) { + //FIXME !!!!!!!!!!!!!!!!! what to do here???? + return input; + } + Expression expression = (Expression)value.getNode(); + if (!(expression instanceof CssString)) { problemsHandler.nonStringIndirection(input); return new FaultyExpression(input); } - CssString realName = (CssString) value; + CssString realName = (CssString) expression; String realVariableName = "@" + realName.getValue(); + //FIXME: !!!!!!!!!!!! bypassing cycle detector!!!!!! (test also less.js) value = scope.getValue(realVariableName); if (value == null) { problemsHandler.undefinedVariable(realVariableName, realName); return new FaultyExpression(realName.getUnderlyingStructure()); } - return evaluate(value); + if (!(value.getNode() instanceof Expression)) { + //FIXME !!!!!!!!!!!!!!!!! what to do here???? + return input; + } + //FIXME !!!!!!!!!!!!!!!!! reusing variable ugly + expression = (Expression)value.getNode(); + return evaluate(expression); } public Expression evaluate(Expression input) { @@ -442,24 +449,20 @@ public boolean hasParent() { } @Override - public void registerVariable(AbstractVariableDeclaration declaration) { + public void registerVariable(AbstractVariableDeclaration node, FullNodeDefinition replacementValue) { } @Override - public void registerVariable(AbstractVariableDeclaration node, Expression replacementValue) { + public void registerVariableIfNotPresent(String name, FullNodeDefinition replacementValue) { } @Override - public void registerVariableIfNotPresent(String name, Expression replacementValue) { - } - - @Override - public Expression getValue(Variable variable) { + public FullNodeDefinition getValue(Variable variable) { return null; } @Override - public Expression getValue(String name) { + public FullNodeDefinition getValue(String name) { return null; } @@ -467,10 +470,6 @@ public Expression getValue(String name) { public void registerMixin(ReusableStructure mixin, IScope mixinsBodyScope) { } -// @Override -// public void setParent(IScope parent) { -// } - @Override public void removedFromAst() { } @@ -500,11 +499,11 @@ public String toFullName() { } @Override - public void registerVariable(String name, Expression replacementValue) { + public void registerVariable(String name, FullNodeDefinition replacementValue) { } @Override - public void addFilteredVariables(ExpressionFilter filter, IScope source) { + public void addFilteredContent(LocalScopeFilter filter, ILocalScope source) { } @Override diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionFilter.java b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionFilter.java deleted file mode 100644 index e991d826..00000000 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionFilter.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.sommeri.less4j.core.compiler.expressions; - -import com.github.sommeri.less4j.core.ast.Expression; - -public interface ExpressionFilter { - - Expression apply(Expression input); - -} diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/LocalScopeFilter.java b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/LocalScopeFilter.java new file mode 100644 index 00000000..2789c554 --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/LocalScopeFilter.java @@ -0,0 +1,12 @@ +package com.github.sommeri.less4j.core.compiler.expressions; + +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; +import com.github.sommeri.less4j.core.compiler.scopes.IScope; + +public interface LocalScopeFilter { + + IScope apply(IScope input); + + FullNodeDefinition apply(FullNodeDefinition value); + +} diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/BasicScope.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/BasicScope.java index 595c7865..85aaa251 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/BasicScope.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/BasicScope.java @@ -4,7 +4,6 @@ import java.util.List; import com.github.sommeri.less4j.core.ast.ASTCssNode; -import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.Variable; public class BasicScope extends ComposedDumbScope implements IScope { @@ -17,16 +16,16 @@ public IScope firstChild() { return getChilds().get(0); } - public Expression getValue(Variable variable) { + public FullNodeDefinition getValue(Variable variable) { return getValue(variable.getName()); } - - public Expression getValue(String name) { - Expression value = getLocalValue(name); - + + public FullNodeDefinition getValue(String name) { + FullNodeDefinition value = getLocalValue(name); + if (value == null && hasParent()) value = getParent().getValue(name); - + return value; } @@ -62,7 +61,6 @@ public boolean seesLocalDataOf(IScope otherScope) { return getParent().seesLocalDataOf(otherScope); } - public IScope getRootScope() { if (!hasParent()) return this; diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ComposedDumbScope.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ComposedDumbScope.java index 036d6479..d5cc6e19 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ComposedDumbScope.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ComposedDumbScope.java @@ -4,11 +4,10 @@ import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.AbstractVariableDeclaration; -import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.ReusableStructure; import com.github.sommeri.less4j.core.ast.ReusableStructureName; import com.github.sommeri.less4j.core.ast.Variable; -import com.github.sommeri.less4j.core.compiler.expressions.ExpressionFilter; +import com.github.sommeri.less4j.core.compiler.expressions.LocalScopeFilter; import com.github.sommeri.less4j.core.compiler.scopes.local.LocalScopeData; import com.github.sommeri.less4j.core.compiler.scopes.local.MixinsDefinitionsStorage; import com.github.sommeri.less4j.core.compiler.scopes.local.VariablesDeclarationsStorage; @@ -47,24 +46,20 @@ public boolean hasTheSameLocalData(ILocalScope otherScope) { return localScope.hasTheSameLocalData(otherScope); } - public void registerVariable(AbstractVariableDeclaration declaration) { - localScope.registerVariable(declaration); - } - - public void registerVariable(AbstractVariableDeclaration node, Expression replacementValue) { + public void registerVariable(AbstractVariableDeclaration node, FullNodeDefinition replacementValue) { localScope.registerVariable(node, replacementValue); } - public void registerVariableIfNotPresent(String name, Expression replacementValue) { + public void registerVariableIfNotPresent(String name, FullNodeDefinition replacementValue) { localScope.registerVariableIfNotPresent(name, replacementValue); } - public void registerVariable(String name, Expression replacementValue) { + public void registerVariable(String name, FullNodeDefinition replacementValue) { localScope.registerVariable(name, replacementValue); } - public void addFilteredVariables(ExpressionFilter filter, IScope source) { - localScope.addFilteredVariables(filter, source); + public void addFilteredContent(LocalScopeFilter filter, ILocalScope source) { + localScope.addFilteredContent(filter, source); } public void registerMixin(ReusableStructure mixin, IScope mixinsBodyScope) { @@ -144,11 +139,11 @@ public boolean isBodyOwnerScope() { return localScope.isBodyOwnerScope(); } - public Expression getLocalValue(Variable variable) { + public FullNodeDefinition getLocalValue(Variable variable) { return localScope.getValue(variable); } - public Expression getLocalValue(String name) { + public FullNodeDefinition getLocalValue(String name) { return localScope.getValue(name); } 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 deleted file mode 100644 index 7b76ac0b..00000000 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/FullDetachedRulesetDefinition.java +++ /dev/null @@ -1,34 +0,0 @@ -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/FullNodeDefinition.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/FullNodeDefinition.java new file mode 100644 index 00000000..42d0cac9 --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/FullNodeDefinition.java @@ -0,0 +1,35 @@ +package com.github.sommeri.less4j.core.compiler.scopes; + +import com.github.sommeri.less4j.core.ast.ASTCssNode; + +public class FullNodeDefinition { + + private final ASTCssNode node; + private final IScope primaryScope; + + public FullNodeDefinition(ASTCssNode node, IScope mixinsBodyScope) { + super(); + this.node = node; + this.primaryScope = mixinsBodyScope; + } + + public ASTCssNode getNode() { + return node; + } + + public IScope getPrimaryScope() { + return primaryScope; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("FullDetachedDefinition [detached ["); + builder.append(getNode().getSourceLine()).append(":").append(getNode().getSourceColumn()); + builder.append("], primaryScope="); + builder.append(primaryScope); + builder.append("]"); + return builder.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ILocalScope.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ILocalScope.java index 3f3f25fc..22931620 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ILocalScope.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/ILocalScope.java @@ -4,11 +4,10 @@ import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.AbstractVariableDeclaration; -import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.ReusableStructure; import com.github.sommeri.less4j.core.ast.ReusableStructureName; import com.github.sommeri.less4j.core.ast.Variable; -import com.github.sommeri.less4j.core.compiler.expressions.ExpressionFilter; +import com.github.sommeri.less4j.core.compiler.expressions.LocalScopeFilter; import com.github.sommeri.less4j.core.compiler.scopes.local.LocalScopeData; import com.github.sommeri.less4j.core.compiler.scopes.local.MixinsDefinitionsStorage; import com.github.sommeri.less4j.core.compiler.scopes.local.MixinsDefinitionsStorage.MixinsPlaceholder; @@ -54,19 +53,17 @@ public interface ILocalScope { public ILocalScope cloneCurrentDataSnapshot(); // variables - public void registerVariable(AbstractVariableDeclaration declaration); + public void registerVariable(AbstractVariableDeclaration node, FullNodeDefinition replacementValue); - public void registerVariable(AbstractVariableDeclaration node, Expression replacementValue); + public void registerVariableIfNotPresent(String name, FullNodeDefinition replacementValue); - public void registerVariableIfNotPresent(String name, Expression replacementValue); + public void registerVariable(String name, FullNodeDefinition replacementValue); + + public FullNodeDefinition getValue(Variable variable); - public void registerVariable(String name, Expression replacementValue); + public FullNodeDefinition getValue(String name); - public void addFilteredVariables(ExpressionFilter filter, IScope source); - - public Expression getValue(Variable variable); - - public Expression getValue(String name); + public void addFilteredContent(LocalScopeFilter filter, ILocalScope source); // mixins public void registerMixin(ReusableStructure mixin, IScope mixinsBodyScope); @@ -109,4 +106,5 @@ public MixinsPlaceholder getMixinsPlaceholder() { } } + } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/IScope.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/IScope.java index 93b166c9..8c221ac6 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/IScope.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/IScope.java @@ -1,9 +1,9 @@ package com.github.sommeri.less4j.core.compiler.scopes; import com.github.sommeri.less4j.core.ast.ASTCssNode; -import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.Variable; +//FIXME: !!!! TODO: add @Override to the whole hierarchy and maybe clean up again public interface IScope extends ILocalScope, IScopesTree { // debug printing @@ -28,14 +28,9 @@ public interface IScope extends ILocalScope, IScopesTree { public IScope childByOwners(ASTCssNode headNode, ASTCssNode... restNodes); - // data access methods - public Expression getValue(Variable variable); + public FullNodeDefinition getLocalValue(Variable variable); - public Expression getValue(String name); - - public Expression getLocalValue(Variable variable); - - public Expression getLocalValue(String name); + public FullNodeDefinition getLocalValue(String name); // smart setters public void setParentKeepConsistency(IScope parent); @@ -45,5 +40,4 @@ public interface IScope extends ILocalScope, IScopesTree { public ILocalScope getLocalScope(); - } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScope.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScope.java index 0e4a7e69..0ea11de2 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScope.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/LocalScope.java @@ -5,12 +5,12 @@ import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.AbstractVariableDeclaration; -import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.ReusableStructure; import com.github.sommeri.less4j.core.ast.ReusableStructureName; import com.github.sommeri.less4j.core.ast.Variable; -import com.github.sommeri.less4j.core.compiler.expressions.ExpressionFilter; +import com.github.sommeri.less4j.core.compiler.expressions.LocalScopeFilter; import com.github.sommeri.less4j.core.compiler.scopes.FullMixinDefinition; +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; import com.github.sommeri.less4j.core.compiler.scopes.ILocalScope; import com.github.sommeri.less4j.core.compiler.scopes.IScope; import com.github.sommeri.less4j.core.compiler.scopes.InScopeSnapshotRunner; @@ -74,27 +74,28 @@ public void registerVariable(AbstractVariableDeclaration declaration) { getLocalVariables().store(declaration); } - public void registerVariable(AbstractVariableDeclaration node, Expression replacementValue) { + public void registerVariable(AbstractVariableDeclaration node, FullNodeDefinition replacementValue) { getLocalVariables().store(node, replacementValue); } - public void registerVariableIfNotPresent(String name, Expression replacementValue) { + public void registerVariableIfNotPresent(String name, FullNodeDefinition replacementValue) { getLocalVariables().storeIfNotPresent(name, replacementValue); } - public void registerVariable(String name, Expression replacementValue) { + public void registerVariable(String name, FullNodeDefinition replacementValue) { getLocalVariables().store(name, replacementValue); } - public void addFilteredVariables(ExpressionFilter filter, IScope source) { + public void addFilteredContent(LocalScopeFilter filter, ILocalScope source) { getLocalVariables().addFilteredVariables(filter, source.getLocalVariables()); + getLocalMixins().addFilteredMixins(filter, source.getAllMixins()); } - public Expression getValue(Variable variable) { + public FullNodeDefinition getValue(Variable variable) { return getLocalVariables().getValue(variable.getName()); } - public Expression getValue(String name) { + public FullNodeDefinition getValue(String name) { return getLocalVariables().getValue(name); } 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 feed0429..ccb8fba7 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,17 +1,11 @@ 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() { @@ -19,7 +13,6 @@ 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) { @@ -40,7 +33,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/scopes/local/MixinsDefinitionsStorage.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/MixinsDefinitionsStorage.java index 8b0cb9b1..831574f2 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/MixinsDefinitionsStorage.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/MixinsDefinitionsStorage.java @@ -4,6 +4,7 @@ import java.util.List; import com.github.sommeri.less4j.core.ast.ReusableStructureName; +import com.github.sommeri.less4j.core.compiler.expressions.LocalScopeFilter; import com.github.sommeri.less4j.core.compiler.scopes.FullMixinDefinition; import com.github.sommeri.less4j.core.compiler.scopes.local.KeyListStorage.ListPlaceholder; @@ -85,7 +86,13 @@ public void closePlaceholder() { public int size() { return coolStorage.getAllValues().size(); } - + + public void addFilteredMixins(LocalScopeFilter filter, List allMixins) { + for (FullMixinDefinition mixin : allMixins) { + store(new FullMixinDefinition(mixin.getMixin(), filter.apply(mixin.getScope()))); + } + } + public MixinsDefinitionsStorage clone() { try { MixinsDefinitionsStorage clone = (MixinsDefinitionsStorage) super.clone(); diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/SaveableLocalScope.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/SaveableLocalScope.java index 6f8641ec..55dddbd8 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/SaveableLocalScope.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/SaveableLocalScope.java @@ -4,12 +4,12 @@ import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.AbstractVariableDeclaration; -import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.ReusableStructure; import com.github.sommeri.less4j.core.ast.ReusableStructureName; import com.github.sommeri.less4j.core.ast.Variable; -import com.github.sommeri.less4j.core.compiler.expressions.ExpressionFilter; +import com.github.sommeri.less4j.core.compiler.expressions.LocalScopeFilter; import com.github.sommeri.less4j.core.compiler.scopes.FullMixinDefinition; +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; import com.github.sommeri.less4j.core.compiler.scopes.ILocalScope; import com.github.sommeri.less4j.core.compiler.scopes.IScope; @@ -34,11 +34,11 @@ private ILocalScope getActiveLocalScope() { return originalLocalScope; } - public Expression getValue(Variable variable) { + public FullNodeDefinition getValue(Variable variable) { return getActiveLocalScope().getValue(variable); } - public Expression getValue(String name) { + public FullNodeDefinition getValue(String name) { return getActiveLocalScope().getValue(name); } @@ -70,24 +70,20 @@ public List getNames() { return getActiveLocalScope().getNames(); } - public void registerVariable(AbstractVariableDeclaration declaration) { - getActiveLocalScope().registerVariable(declaration); - } - - public void registerVariable(AbstractVariableDeclaration node, Expression replacementValue) { + public void registerVariable(AbstractVariableDeclaration node, FullNodeDefinition replacementValue) { getActiveLocalScope().registerVariable(node, replacementValue); } - public void registerVariableIfNotPresent(String name, Expression replacementValue) { + public void registerVariableIfNotPresent(String name, FullNodeDefinition replacementValue) { getActiveLocalScope().registerVariableIfNotPresent(name, replacementValue); } - public void registerVariable(String name, Expression replacementValue) { + public void registerVariable(String name, FullNodeDefinition replacementValue) { getActiveLocalScope().registerVariable(name, replacementValue); } - public void addFilteredVariables(ExpressionFilter filter, IScope source) { - getActiveLocalScope().addFilteredVariables(filter, source); + public void addFilteredContent(LocalScopeFilter filter, ILocalScope source) { + getActiveLocalScope().addFilteredContent(filter, source); } public void addAllMixins(List mixins) { diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java index 6cd3fc7f..4ce192f4 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java @@ -4,38 +4,38 @@ import java.util.Set; import com.github.sommeri.less4j.core.ast.AbstractVariableDeclaration; -import com.github.sommeri.less4j.core.ast.Expression; -import com.github.sommeri.less4j.core.compiler.expressions.ExpressionFilter; +import com.github.sommeri.less4j.core.compiler.expressions.LocalScopeFilter; +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; import com.github.sommeri.less4j.core.compiler.scopes.local.KeyValueStorage.ValuePlaceholder; public class VariablesDeclarationsStorage implements Cloneable { - private KeyValueStorage coolStorage = new KeyValueStorage(); + private KeyValueStorage coolStorage = new KeyValueStorage(); public VariablesDeclarationsStorage() { } - public Expression getValue(String name) { + public FullNodeDefinition getValue(String name) { return coolStorage.getValue(name); } public void store(AbstractVariableDeclaration node) { - store(node.getVariable().getName(), node.getValue()); + store(node.getVariable().getName(), new FullNodeDefinition(node.getValue(), null)); } public void storeAll(VariablesDeclarationsStorage otherStorage) { coolStorage.add(otherStorage.coolStorage); } - public void store(AbstractVariableDeclaration node, Expression replacementValue) { + public void store(AbstractVariableDeclaration node, FullNodeDefinition replacementValue) { store(node.getVariable().getName(), replacementValue); } - public void store(String name, Expression replacementValue) { + public void store(String name, FullNodeDefinition replacementValue) { coolStorage.add(name, replacementValue); } - public void storeIfNotPresent(String name, Expression replacementValue) { + public void storeIfNotPresent(String name, FullNodeDefinition replacementValue) { if (!contains(name)) store(name, replacementValue); } @@ -44,11 +44,14 @@ public void closePlaceholder() { coolStorage.closeFirstPlaceholder(); } - public void addFilteredVariables(ExpressionFilter filter, VariablesDeclarationsStorage variablesSource) { - for (Entry entry : variablesSource.coolStorage.getAllEntries()) { + //FIXME !!!!!!!!!!!!!!!!!! enable and fix + public void addFilteredVariables(LocalScopeFilter filter, VariablesDeclarationsStorage source) { + for (Entry entry : source.coolStorage.getAllEntries()) { String name = entry.getKey(); - Expression value = entry.getValue(); - store(name, filter.apply(value)); + FullNodeDefinition value = entry.getValue(); + FullNodeDefinition filteredValue = filter.apply(value); + + store(name, filteredValue); } } @@ -65,10 +68,11 @@ public VariablesPlaceholder createPlaceholder() { } public void addToFirstPlaceholderIfNotPresent(VariablesDeclarationsStorage otherStorage) { - Set> otherVariables = otherStorage.coolStorage.getAllEntries(); - for (Entry entry : otherVariables) { - if (!contains(entry.getKey())) + Set> otherVariables = otherStorage.coolStorage.getAllEntries(); + for (Entry entry : otherVariables) { + if (!contains(entry.getKey())) { coolStorage.addToFirstPlaceholder(entry.getKey(), entry.getValue()); + } } } @@ -95,9 +99,9 @@ public String toString() { public static class VariablesPlaceholder { - private final ValuePlaceholder coolPlaceholder; + private final ValuePlaceholder coolPlaceholder; - public VariablesPlaceholder(ValuePlaceholder coolPlaceholder) { + public VariablesPlaceholder(ValuePlaceholder coolPlaceholder) { this.coolPlaceholder = coolPlaceholder; } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ArgumentsBuilder.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ArgumentsBuilder.java index aa48935e..290cfdf9 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ArgumentsBuilder.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ArgumentsBuilder.java @@ -10,7 +10,7 @@ import com.github.sommeri.less4j.core.ast.Expression; 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.scopes.FullNodeDefinition; import com.github.sommeri.less4j.core.compiler.scopes.IScope; import com.github.sommeri.less4j.core.compiler.scopes.ScopeFactory; import com.github.sommeri.less4j.core.problems.ProblemsHandler; @@ -20,7 +20,8 @@ class ArgumentsBuilder { // utils private final ProblemsHandler problemsHandler; - private final ExpressionEvaluator referenceEvaluator; + private final ScopedValuesEvaluator referenceValuesEvaluator; + private final ScopedValuesEvaluator defaultValuesEvaluator; private final String ALL_ARGUMENTS = ReferencesSolver.ALL_ARGUMENTS; // input @@ -29,18 +30,19 @@ class ArgumentsBuilder { private MixinReference reference; // results - private List allValues = new ArrayList(); + private List allValues = new ArrayList(); private IScope argumentsScope; - public ArgumentsBuilder(MixinReference reference, ReusableStructure pureMixin, ExpressionEvaluator referenceEvaluator, ProblemsHandler problemsHandler) { + public ArgumentsBuilder(MixinReference reference, ReusableStructure mixin, ScopedValuesEvaluator referenceValuesEvaluator, ScopedValuesEvaluator defaultValuesEvaluator, ProblemsHandler problemsHandler) { super(); - this.referenceEvaluator = referenceEvaluator; + this.referenceValuesEvaluator = referenceValuesEvaluator; + this.defaultValuesEvaluator = defaultValuesEvaluator; this.problemsHandler = problemsHandler; this.positionalParameters = reference.getPositionalParameters().iterator(); this.reference = reference; argumentsScope = ScopeFactory.createDummyScope(reference, "#arguments-" + reference + "#"); - mixin = pureMixin; + this.mixin = mixin; } public IScope build() { @@ -55,7 +57,8 @@ public IScope build() { } - Expression allArgumentsValue = referenceEvaluator.joinAll(allValues, reference); + FullNodeDefinition allArgumentsValue = referenceValuesEvaluator.joinFull(allValues, reference); + //FIXME!!!!!!!!!!!! scopeee if needed argumentsScope.registerVariableIfNotPresent(ALL_ARGUMENTS, allArgumentsValue); return argumentsScope; } @@ -81,7 +84,7 @@ private void add(ArgumentDeclaration declaration) { } private void fillFromNamed(ArgumentDeclaration declaration) { - Expression value = referenceEvaluator.evaluate(reference.getNamedParameter(declaration.getVariable())); + FullNodeDefinition value = referenceValuesEvaluator.toFullNodeDefinition(reference.getNamedParameter(declaration.getVariable())); allValues.add(value); argumentsScope.registerVariable(declaration, value); } @@ -91,8 +94,9 @@ private boolean canFillFromNamed(ArgumentDeclaration declaration) { } private void fillFromDefault(ArgumentDeclaration declaration) { - allValues.add(declaration.getValue()); - argumentsScope.registerVariable(declaration); + FullNodeDefinition value = defaultValuesEvaluator.toFullNodeDefinition(declaration.getValue()); + allValues.add(value); + argumentsScope.registerVariable(declaration, value); } private boolean hasDefault(ArgumentDeclaration declaration) { @@ -100,7 +104,7 @@ private boolean hasDefault(ArgumentDeclaration declaration) { } private void fillFromPositional(ArgumentDeclaration declaration) { - Expression value = referenceEvaluator.evaluate(positionalParameters.next()); + FullNodeDefinition value = referenceValuesEvaluator.toFullNodeDefinition(positionalParameters.next()); allValues.add(value); argumentsScope.registerVariable(declaration, value); } @@ -110,10 +114,11 @@ private boolean canFillFromPositional() { } private void addAsCollector(ArgumentDeclaration declaration) { - List allArgumentsFrom = referenceEvaluator.evaluateAll(ArraysUtils.remaining(positionalParameters)); - allValues.addAll(allArgumentsFrom); - Expression value = referenceEvaluator.joinAll(allArgumentsFrom, reference); - argumentsScope.registerVariable(declaration, value); + List allArgumentsFrom = referenceValuesEvaluator.evaluateAll(ArraysUtils.remaining(positionalParameters)); + Expression value = referenceValuesEvaluator.joinAll(allArgumentsFrom, reference); + allValues.addAll(referenceValuesEvaluator.toFullNodeDefinitions(allArgumentsFrom)); + //FIXME!!!!!!!!!!!! scopeee if needed + argumentsScope.registerVariable(declaration, new FullNodeDefinition(value, null)); } 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 index 2b0ebf38..32d1b9b2 100644 --- 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 @@ -6,6 +6,7 @@ import java.util.List; import java.util.Set; +import com.github.sommeri.less4j.core.ast.ASTCssNode; 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; @@ -68,8 +69,8 @@ private List keepOnly(List compi return compiledMixins; } - private List extractOriginalMixins(List compiledMixins) { - List result = new ArrayList(); + private List extractOriginalMixins(List compiledMixins) { + List result = new ArrayList(); for (MixinCompilationResult compiled : compiledMixins) { result.add(compiled.getMixin()); } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/InitialScopeExtractor.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/InitialScopeExtractor.java index 52c16368..daa1c7e8 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/InitialScopeExtractor.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/InitialScopeExtractor.java @@ -1,14 +1,19 @@ package com.github.sommeri.less4j.core.compiler.stages; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.ASTCssNodeType; +import com.github.sommeri.less4j.core.ast.DetachedRuleset; +import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.ReusableStructure; import com.github.sommeri.less4j.core.ast.RuleSet; import com.github.sommeri.less4j.core.ast.VariableDeclaration; +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; import com.github.sommeri.less4j.core.compiler.scopes.IScope; import com.github.sommeri.less4j.core.compiler.scopes.PlaceholderScope; import com.github.sommeri.less4j.core.compiler.scopes.ScopeFactory; @@ -25,6 +30,7 @@ public class InitialScopeExtractor { private ASTManipulator manipulator = new ASTManipulator(); private IScope currentScope; + private Map detachedRulesetsScopes = new HashMap(); private List importsPlaceholders; public InitialScopeExtractor() { @@ -52,8 +58,17 @@ private IScope buildScope(ASTCssNode node) { if (kid.getType() == ASTCssNodeType.IMPORT) { importsPlaceholders.add(createPlaceholderScope(kid)); } else if (kid.getType() == ASTCssNodeType.VARIABLE_DECLARATION) { - currentScope.registerVariable((VariableDeclaration) kid); + VariableDeclaration variableDeclaration = (VariableDeclaration)kid; + Expression value = variableDeclaration.getValue(); + currentScope.registerVariable(variableDeclaration, new FullNodeDefinition(value, detachedRulesetsScopes.get(value))); manipulator.removeFromBody(kid); + } else if (kid.getType() == ASTCssNodeType.DETACHED_RULESET) { + DetachedRuleset detached = (DetachedRuleset)kid; + IScope bodyScope = currentScope.childByOwners(detached, detached.getBody()); + bodyScope.removedFromAst(); + if (bodyScope.hasParent()) + bodyScope.getParent().removedFromAst(); // remove also arguments scope from tree + detachedRulesetsScopes.put(detached, bodyScope); } else if (kid.getType() == ASTCssNodeType.REUSABLE_STRUCTURE) { ReusableStructure mixin = (ReusableStructure) kid; IScope bodyScope = currentScope.childByOwners(mixin, mixin.getBody()); @@ -70,6 +85,8 @@ private IScope buildScope(ASTCssNode node) { } } else if (kid.getType() == ASTCssNodeType.MIXIN_REFERENCE) { currentScope.createDataPlaceholder(); + } else if (kid.getType() == ASTCssNodeType.DETACHED_RULESET_REFERENCE) { + currentScope.createDataPlaceholder(); } } 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 index e068cbcc..d13b1634 100644 --- 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 @@ -3,19 +3,20 @@ 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.ast.BodyOwner; 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; + //FIXME!!!!!!!!!!!!!! rename class and following property + private ASTCssNode mixin; private List replacement; private IScope returnValues; private GuardValue guardValue; - public MixinCompilationResult(ReusableStructure mixin, List replacement, IScope returnValues) { + public MixinCompilationResult(ASTCssNode mixin, List replacement, IScope returnValues) { this.mixin = mixin; this.replacement = replacement; this.returnValues = returnValues; @@ -45,11 +46,11 @@ public void setReturnValues(IScope returnValues) { this.returnValues = returnValues; } - public ReusableStructure getMixin() { + public ASTCssNode getMixin() { return mixin; } - public void setMixin(ReusableStructure mixin) { + public void setMixin(ASTCssNode mixin) { this.mixin = mixin; } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinReferenceFinder.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinReferenceFinder.java index 8acfac79..8008d288 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinReferenceFinder.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinReferenceFinder.java @@ -78,7 +78,7 @@ private boolean participatesInCuttableCycle(FullMixinDefinition mixin) { * Note: the detection feels too hackish, but works and hotfix is needed * (bootstrap). */ - return mixin.getMixin().isAlsoRuleset() && semiCompiledNodes.contains(mixin.getMixin()); + return mixin.getMixin().isAlsoRuleset() && semiCompiledNodes.contains(mixin.getMixin().getBody()); } private List findInMatchingNamespace(IScope scope, List nameChain, MixinReference reference) { @@ -96,13 +96,6 @@ private List findInMatchingNamespace(IScope scope, List theRest = nameChain.subList(1, nameChain.size()); -// -// for (FullMixinDefinition fullMixin : scope.getMixinsByName(firstName)) { -// List foundInNamespaces = buildAndFind(fullMixin, theRest, reference); -// result.addAll(foundInNamespaces); -// } } result.addAll(getNearestLocalMixins(scope, nameChain, reference.getFinalName())); 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 03fc2873..64928dd2 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 @@ -8,13 +8,18 @@ import com.github.sommeri.less4j.core.ast.Body; import com.github.sommeri.less4j.core.ast.BodyOwner; import com.github.sommeri.less4j.core.ast.Declaration; +import com.github.sommeri.less4j.core.ast.DetachedRuleset; +import com.github.sommeri.less4j.core.ast.DetachedRulesetReference; +import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.GeneralBody; 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.LocalScopeFilter; 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.FullNodeDefinition; import com.github.sommeri.less4j.core.compiler.scopes.IScope; import com.github.sommeri.less4j.core.compiler.scopes.InScopeSnapshotRunner; import com.github.sommeri.less4j.core.compiler.scopes.InScopeSnapshotRunner.IFunction; @@ -39,9 +44,8 @@ public MixinsSolver(ReferencesSolver parentSolver, AstNodesStack semiCompiledNod this.defaultGuardHelper = new DefaultGuardHelper(problemsHandler); } - private MixinCompilationResult resolveMixinReference(final IScope callerScope, final FullMixinDefinition referencedMixin, final IScope mixinWorkingScope) { + private MixinCompilationResult resolveReferencedBody(final IScope callerScope, final BodyOwner mixin, final IScope mixinWorkingScope) { final ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator(mixinWorkingScope, problemsHandler, configuration); - final ReusableStructure mixin = referencedMixin.getMixin(); final IScope referencedMixinScope = mixinWorkingScope; // ... and I'm starting to see the point of closures ... @@ -50,56 +54,31 @@ private MixinCompilationResult resolveMixinReference(final IScope callerScope, f @Override public MixinCompilationResult run() { // compile referenced mixin - keep the original copy unchanged - List replacement = compileReferencedMixin(mixin, referencedMixinScope); + List replacement = compileBody(mixin.getBody(), referencedMixinScope); // collect variables and mixins to be imported - IScope returnValues = expressionEvaluator.evaluateValues(referencedMixinScope); - List unmodifiedMixinsToImport = referencedMixinScope.getAllMixins(); + IScope returnValues = ScopeFactory.createDummyScope(); + returnValues.addFilteredContent(new ImportedScopeFilter(expressionEvaluator, callerScope), referencedMixinScope); - List allMixinsToImport = mixinsToImport(callerScope, referencedMixinScope, unmodifiedMixinsToImport); - returnValues.addAllMixins(allMixinsToImport); - - return new MixinCompilationResult(mixin, replacement, returnValues); + //FIXME: !!!!!!!!!! clean up + return new MixinCompilationResult((ASTCssNode) mixin, replacement, returnValues); } }); } - //TODO: all these methods names are too similar to each other - better and clearer naming is needed - private List compileReferencedMixin(ReusableStructure referencedMixin, IScope referencedMixinScopeSnapshot) { - semiCompiledNodes.push(referencedMixin); + private List compileBody(Body body, IScope scopeSnapshot) { + semiCompiledNodes.push(body); try { - GeneralBody bodyClone = referencedMixin.getBody().clone(); - parentSolver.unsafeDoSolveReferences(bodyClone, referencedMixinScopeSnapshot); + Body bodyClone = body.clone(); + parentSolver.unsafeDoSolveReferences(bodyClone, scopeSnapshot); return bodyClone.getMembers(); } finally { semiCompiledNodes.pop(); } } - private List mixinsToImport(IScope referenceScope, IScope referencedMixinScope, List unmodifiedMixinsToImport) { - List result = new ArrayList(); - for (FullMixinDefinition mixinToImport : unmodifiedMixinsToImport) { - boolean isLocalImport = mixinToImport.getScope().seesLocalDataOf(referenceScope); - if (isLocalImport) { - // we need to copy the whole tree, because this runs inside referenced mixin scope - // snapshot and imported mixin needs to remember the scope as it is now - ScopeView newWay = ScopeFactory.createJoinedScopesView(null, mixinToImport.getScope()); - newWay.saveLocalDataForTheWholeWayUp(); - result.add(new FullMixinDefinition(mixinToImport.getMixin(), newWay)); - } else { - // since this is non-local import, we need to join reference scope and imported mixins scope - // imported mixin needs to have access to variables defined in caller - ScopeView newWay = ScopeFactory.createJoinedScopesView(referencedMixinScope, mixinToImport.getScope()); - newWay.saveLocalDataForTheWholeWayUp(); - result.add(new FullMixinDefinition(mixinToImport.getMixin(), newWay)); - } - - } - return result; - } - - private void shiftComments(MixinReference reference, GeneralBody result) { + private void shiftComments(ASTCssNode reference, GeneralBody result) { List childs = result.getMembers(); if (!childs.isEmpty()) { childs.get(0).addOpeningComments(reference.getOpeningComments()); @@ -108,7 +87,9 @@ private void shiftComments(MixinReference reference, GeneralBody result) { } private IScope buildMixinsArguments(MixinReference reference, IScope referenceScope, FullMixinDefinition mixin) { - ArgumentsBuilder builder = new ArgumentsBuilder(reference, mixin.getMixin(), new ExpressionEvaluator(referenceScope, problemsHandler, configuration), problemsHandler); + ScopedValuesEvaluator referenceValuesEvaluator = new ScopedValuesEvaluator(referenceScope, problemsHandler, configuration); + ScopedValuesEvaluator defaultValuesEvaluator = new ScopedValuesEvaluator(ScopeFactory.createJoinedScopesView(referenceScope, mixin.getScope()), problemsHandler, configuration); + ArgumentsBuilder builder = new ArgumentsBuilder(reference, mixin.getMixin(), referenceValuesEvaluator, defaultValuesEvaluator, problemsHandler); return builder.build(); } @@ -130,15 +111,15 @@ public GeneralBody buildMixinReferenceReplacement(final MixinReference reference @Override public void run() { IScope mixinArguments = buildMixinsArguments(reference, callerScope, fullMixin); - IScope mixinWorkingScope = calculateMixinsWorkingScope(callerScope, mixinArguments, mixinScope); + IScope mixinWorkingScope = calculateBodyWorkingScope(callerScope, mixinArguments, mixinScope); MixinsGuardsValidator guardsValidator = new MixinsGuardsValidator(mixinWorkingScope, problemsHandler, configuration); GuardValue guardValue = guardsValidator.evaluateGuards(mixin); - if (guardValue!=GuardValue.DO_NOT_USE) { + 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. - MixinCompilationResult compiled = resolveMixinReference(callerScope, fullMixin, mixinWorkingScope); + MixinCompilationResult compiled = resolveReferencedBody(callerScope, fullMixin.getMixin(), mixinWorkingScope); //mark the mixin according to its default() function use compiled.setGuardValue(guardValue); //store the mixin as candidate @@ -150,15 +131,31 @@ public void run() { // filter out mixins we do not want to use List mixinsToBeUsed = defaultGuardHelper.chooseMixinsToBeUsed(compiledMixins, reference); - + // update mixin replacements and update scope with imported variables and mixins for (MixinCompilationResult compiled : mixinsToBeUsed) { result.addMembers(compiled.getReplacement()); callerScope.addToDataPlaceholder(compiled.getReturnValues()); } - callerScope.closeDataPlaceholder(); resolveImportance(reference, result); + + callerScope.closeDataPlaceholder(); + shiftComments(reference, result); + + return result; + } + + public GeneralBody buildDetachedRulesetReplacement(DetachedRulesetReference reference, IScope callerScope, DetachedRuleset detachedRuleset, IScope detachedRulesetScope) { + IScope mixinWorkingScope = calculateBodyWorkingScope(callerScope, null, detachedRulesetScope); + MixinCompilationResult compiled = resolveReferencedBody(callerScope, detachedRuleset, mixinWorkingScope); + GeneralBody result = new GeneralBody(reference.getUnderlyingStructure()); + + result.addMembers(compiled.getReplacement()); + callerScope.addToDataPlaceholder(compiled.getReturnValues()); + callerScope.closeDataPlaceholder(); + + //resolveImportance(reference, result); shiftComments(reference, result); return result; @@ -183,21 +180,76 @@ private void declarationsAreImportant(Body result) { } } - private static IScope calculateMixinsWorkingScope(IScope callerScope, IScope arguments, IScope mixinScope) { + private static IScope calculateBodyWorkingScope(IScope callerScope, IScope arguments, IScope bodyScope) { // add arguments - IScope mixinDeclarationScope = mixinScope.getParent(); - mixinDeclarationScope.add(arguments); + if (arguments != null) { + IScope mixinDeclarationScope = bodyScope.getParent(); + mixinDeclarationScope.add(arguments); + } // locally defined mixin does not require any other action - boolean isLocallyDefined = mixinDeclarationScope.seesLocalDataOf(callerScope); + boolean isLocallyDefined = bodyScope.seesLocalDataOf(callerScope); if (isLocallyDefined) { - return mixinScope; + return bodyScope; } //join scopes - IScope result = ScopeFactory.createJoinedScopesView(callerScope, mixinScope); + IScope result = ScopeFactory.createJoinedScopesView(callerScope, bodyScope); return result; } -} + class ImportedScopeFilter implements LocalScopeFilter { + private final ExpressionEvaluator expressionEvaluator; + private final IScope importTargetScope; + + public ImportedScopeFilter(ExpressionEvaluator expressionEvaluator, IScope importTargetScope) { + super(); + this.expressionEvaluator = expressionEvaluator; + this.importTargetScope = importTargetScope; + } + + public Expression apply(Expression input) { + return expressionEvaluator.evaluate(input); + } + @Override + public FullNodeDefinition apply(FullNodeDefinition input) { + if (input == null) { + return null; + } + + if (!(input.getNode() instanceof Expression)) { + //FIXME !!!!!!!!!!!!!!!!! what to do here???? + return input; + } + Expression expression = (Expression)input.getNode(); + return new FullNodeDefinition(apply(expression), apply(input.getPrimaryScope())) ; + } + + //FIXME: !!!!!!!!!!!!!!!!!! clean up + @Override + public IScope apply(IScope input) { + if (input==null) + return null; + return constructImportedBodyScope(importTargetScope, input); + } + + private ScopeView constructImportedBodyScope(IScope importTargetScope, IScope bodyToBeImportedScope) { + ScopeView newScope = null; + boolean isLocalImport = bodyToBeImportedScope.seesLocalDataOf(importTargetScope); + if (isLocalImport) { + // we need to copy the whole tree, because this runs inside referenced mixin scope + // snapshot and imported mixin needs to remember the scope as it is now + newScope = ScopeFactory.createJoinedScopesView(null, bodyToBeImportedScope); + newScope.saveLocalDataForTheWholeWayUp(); + } else { + // since this is non-local import, we need to join reference scope and imported mixins scope + // imported mixin needs to have access to variables defined in caller + newScope = ScopeFactory.createJoinedScopesView(importTargetScope, bodyToBeImportedScope); + newScope.saveLocalDataForTheWholeWayUp(); + } + return newScope; + } + + } +} 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 5d7c6d05..b21c00ec 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 @@ -10,6 +10,8 @@ import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.ASTCssNodeType; import com.github.sommeri.less4j.core.ast.CssString; +import com.github.sommeri.less4j.core.ast.DetachedRuleset; +import com.github.sommeri.less4j.core.ast.DetachedRulesetReference; import com.github.sommeri.less4j.core.ast.EmbeddedScript; import com.github.sommeri.less4j.core.ast.EscapedSelector; import com.github.sommeri.less4j.core.ast.EscapedValue; @@ -25,6 +27,7 @@ import com.github.sommeri.less4j.core.ast.VariableNamePart; import com.github.sommeri.less4j.core.compiler.expressions.ExpressionEvaluator; import com.github.sommeri.less4j.core.compiler.expressions.strings.StringInterpolator; +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; import com.github.sommeri.less4j.core.compiler.scopes.FullMixinDefinition; import com.github.sommeri.less4j.core.compiler.scopes.IScope; import com.github.sommeri.less4j.core.compiler.scopes.InScopeSnapshotRunner; @@ -85,7 +88,7 @@ private void unsafeDoSolveReferences(ASTCssNode node, IteratedScope iteratedScop IScope scope = iteratedScope.getScope(); // solve all detached ruleset/mixin references and store solutions - Map solvedMixinReferences = solveCalls(childs, scope); + Map solvedMixinReferences = solveCalls(childs, scope); // solve whatever is not a mixin/detached ruleset reference solveNonCalligReferences(childs, iteratedScope); @@ -102,7 +105,7 @@ private void unsafeDoSolveReferences(ASTCssNode node, IteratedScope iteratedScop private void solveNonCalligReferences(List childs, IteratedScope iteratedScope) { ExpressionEvaluator cssGuardsValidator = new ExpressionEvaluator(iteratedScope.getScope(), problemsHandler, configuration); for (ASTCssNode kid : childs) { - if (isMixinReference(kid)) + if (isMixinReference(kid) || isDetachedRulesetReference(kid)) continue; if (isRuleset(kid)) { @@ -113,12 +116,12 @@ private void solveNonCalligReferences(List childs, IteratedScope ite manipulator.removeFromClosestBody(ruleSet); //skip child scope iteratedScope.getNextChild(); - continue ; + continue; } } - + if (AstLogic.isQuotelessUrlFunction(kid)) { - continue ; + continue; } if (AstLogic.hasOwnScope(kid)) { @@ -144,27 +147,51 @@ private boolean isRuleset(ASTCssNode kid) { return kid.getType() == ASTCssNodeType.RULE_SET; } - private void replaceMixinReferences(Map solvedMixinReferences) { - for (Entry entry : solvedMixinReferences.entrySet()) { - MixinReference mixinReference = entry.getKey(); + private void replaceMixinReferences(Map solvedMixinReferences) { + for (Entry entry : solvedMixinReferences.entrySet()) { + ASTCssNode mixinReference = entry.getKey(); GeneralBody replacement = entry.getValue(); - + manipulator.setTreeSilentness(replacement, mixinReference.isSilent()); manipulator.replaceInBody(mixinReference, replacement.getMembers()); } } - private Map solveCalls(List childs, IScope mixinReferenceScope) { - Map solvedMixinReferences = new HashMap(); + private Map solveCalls(List childs, IScope referenceScope) { + Map solvedMixinReferences = new HashMap(); for (ASTCssNode kid : childs) { if (isMixinReference(kid)) { MixinReference mixinReference = (MixinReference) kid; - List foundMixins = findReferencedMixins(mixinReference, mixinReferenceScope); - GeneralBody replacement = mixinsSolver.buildMixinReferenceReplacement(mixinReference, mixinReferenceScope, foundMixins); + List foundMixins = findReferencedMixins(mixinReference, referenceScope); + GeneralBody replacement = mixinsSolver.buildMixinReferenceReplacement(mixinReference, referenceScope, foundMixins); + + AstLogic.validateLessBodyCompatibility(kid, replacement.getMembers(), problemsHandler); + solvedMixinReferences.put(kid, replacement); + } else if (isDetachedRulesetReference(kid)) { + DetachedRulesetReference detachedRulesetReference = (DetachedRulesetReference) kid; + + FullNodeDefinition fullNodeDefinition = referenceScope.getValue(detachedRulesetReference.getVariable()); + if (fullNodeDefinition == null) { + problemsHandler.detachedRulesetNotfound(detachedRulesetReference); + solvedMixinReferences.put(kid, new GeneralBody(detachedRulesetReference.getUnderlyingStructure())); + } else { + + //FIXME !!!!!!!!!!!!!!!!! solve wrong type case + ASTCssNode node = fullNodeDefinition.getNode(); + if (node.getType() == ASTCssNodeType.DETACHED_RULESET) { + //FIXME !!!!!!!!!!!!!!!!! should I attach caller scope to primary scope? Probably yes + GeneralBody replacement = mixinsSolver.buildDetachedRulesetReplacement(detachedRulesetReference, referenceScope, (DetachedRuleset) node, fullNodeDefinition.getPrimaryScope()); + + AstLogic.validateLessBodyCompatibility(kid, replacement.getMembers(), problemsHandler); + solvedMixinReferences.put(kid, replacement); + } else { + //FIXME!!!!!!!!!!!!!!!!!!!!!!!!! + System.out.println("WROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOG: "); + (new ASTManipulator()).removeFromBody(kid); + } + } - AstLogic.validateLessBodyCompatibility(mixinReference, replacement.getMembers(), problemsHandler); - solvedMixinReferences.put(mixinReference, replacement); } } return solvedMixinReferences; diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ScopedValuesEvaluator.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ScopedValuesEvaluator.java new file mode 100644 index 00000000..f0fbb681 --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ScopedValuesEvaluator.java @@ -0,0 +1,74 @@ +package com.github.sommeri.less4j.core.compiler.stages; + +import java.util.ArrayList; +import java.util.List; + +import com.github.sommeri.less4j.LessCompiler.Configuration; +import com.github.sommeri.less4j.core.ast.BodyOwner; +import com.github.sommeri.less4j.core.ast.Expression; +import com.github.sommeri.less4j.core.ast.MixinReference; +import com.github.sommeri.less4j.core.compiler.expressions.ExpressionEvaluator; +import com.github.sommeri.less4j.core.compiler.scopes.FullNodeDefinition; +import com.github.sommeri.less4j.core.compiler.scopes.IScope; +import com.github.sommeri.less4j.core.problems.ProblemsHandler; + +public class ScopedValuesEvaluator { + + private final ExpressionEvaluator expressionEvaluator; + private IScope scope; + + public ScopedValuesEvaluator(ExpressionEvaluator expressionEvaluator) { + this.expressionEvaluator = expressionEvaluator; + } + + public ScopedValuesEvaluator(IScope scope, ProblemsHandler problemsHandler, Configuration configuration) { + this(new ExpressionEvaluator(scope, problemsHandler, configuration)); + this.scope = scope; + } + + public FullNodeDefinition toFullNodeDefinition(Expression value) { + Expression evaluated = evaluate(value); + IScope scope = findBodyScope(evaluated); + return new FullNodeDefinition(evaluated, scope); + } + + private IScope findBodyScope(Expression value) { + IScope scope = null; + if (AstLogic.isBodyOwner(value)) { + @SuppressWarnings("rawtypes") + BodyOwner asBodyOwner = (BodyOwner) value; + scope = this.scope.childByOwners(value, asBodyOwner.getBody()); + } + return scope; + } + + private Expression evaluate(Expression value) { + return expressionEvaluator.evaluate(value); + } + + public List evaluateAll(List remaining) { + return expressionEvaluator.evaluateAll(remaining); + } + + public Expression joinAll(List allArgumentsFrom, MixinReference reference) { + return expressionEvaluator.joinAll(allArgumentsFrom, reference); + } + + public List toFullNodeDefinitions(List allArgumentsFrom) { + List result = new ArrayList(); + for (Expression expression : allArgumentsFrom) { + result.add(toFullNodeDefinition(expression)); + } + return result; + } + + //FIXME:!!!!!!!!!!!!!!! scopeee what should this do? + public FullNodeDefinition joinFull(List allValues, MixinReference reference) { + List result = new ArrayList(); + for (FullNodeDefinition expression : allValues) { + result.add((Expression)expression.getNode()); + } + return new FullNodeDefinition(joinAll(result, reference), null); + } + +} diff --git a/src/main/java/com/github/sommeri/less4j/core/problems/ProblemsHandler.java b/src/main/java/com/github/sommeri/less4j/core/problems/ProblemsHandler.java index cdf7c8e4..ce07b852 100644 --- a/src/main/java/com/github/sommeri/less4j/core/problems/ProblemsHandler.java +++ b/src/main/java/com/github/sommeri/less4j/core/problems/ProblemsHandler.java @@ -9,6 +9,7 @@ import com.github.sommeri.less4j.core.ast.ArgumentDeclaration; import com.github.sommeri.less4j.core.ast.Body; import com.github.sommeri.less4j.core.ast.ComparisonExpressionOperator; +import com.github.sommeri.less4j.core.ast.DetachedRulesetReference; import com.github.sommeri.less4j.core.ast.EscapedSelector; import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.FunctionExpression; @@ -198,6 +199,10 @@ private CompilationError createUndefinedMixin(ReusableStructureName name, MixinR return new CompilationError(reference.getFinalName(), "Could not find mixin named \"" + name.asString() + "\"."); } + public void detachedRulesetNotfound(DetachedRulesetReference reference) { + collector.addError(new CompilationError(reference, "Could not find detached ruleset for \"" + reference.getVariable().getName() + "\".")); + } + public void noMixinHasRightParametersCountError(MixinReference reference) { collector.addError(createNoMixinHasRightParametersCountError(reference)); } @@ -270,7 +275,7 @@ public void warnIE8UnsafeDataUri(FunctionExpression errorNode, String filename, collector.addWarning(new CompilationWarning(errorNode, "Skipped data-uri embedding of " + filename + " because its size ("+fileSizeInKB+"dKB) exceeds IE8-safe "+dataUriMaxKb+"dKB!")); } - public void ambiguousDefaultSet(MixinReference reference, List possibleMixins) { + public void ambiguousDefaultSet(MixinReference reference, List possibleMixins) { collector.addError(new CompilationError(reference, "Ambiguous use of `default()` found when matching reference " + reference.getFinalName() +". Matched mixins using default are located at " + printer.toNodesPositions(possibleMixins))); } diff --git a/src/test/java/com/github/sommeri/less4j/AbstractFileBasedTest.java b/src/test/java/com/github/sommeri/less4j/AbstractFileBasedTest.java index 5db33910..33d57730 100644 --- a/src/test/java/com/github/sommeri/less4j/AbstractFileBasedTest.java +++ b/src/test/java/com/github/sommeri/less4j/AbstractFileBasedTest.java @@ -6,6 +6,8 @@ import java.io.File; import java.io.FileReader; import java.io.PrintStream; +import java.net.URI; +import java.net.URISyntaxException; import org.apache.commons.io.IOUtils; import org.junit.ComparisonFailure; @@ -86,7 +88,7 @@ protected Configuration createConfiguration(File cssOutput) { Configuration configuration = new Configuration(); configuration.setCssResultLocation(new LessSource.FileSource(cssOutput)); configuration.setLinkSourceMap(false); - return configuration; + return configuration;//http://myhost:8080/lessSrc } private void assertCorrectErrors(Less4jException error) { diff --git a/src/test/java/com/github/sommeri/less4j/SimpleCssTest.java b/src/test/java/com/github/sommeri/less4j/SimpleCssTest.java index 30cba7c1..e5a27935 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 91b84b2d..4b96a396 100644 --- a/src/test/java/com/github/sommeri/less4j/compiler/DetachedRulesetsTest.java +++ b/src/test/java/com/github/sommeri/less4j/compiler/DetachedRulesetsTest.java @@ -38,10 +38,51 @@ interpolation: "@{detached}"; } +//*** should this work? **** +.mixin() { + @detached: { extreme: simplicity; }; +} +.selector { + @detached(); + .mixin(); +} +//********************************************88 +.mixin() { + @detached: { scope: @see-here; }; +} +.selector { + @see-here: yes; + .nested { + .mixin(); + @detached(); + } +} +//************************************************************ + .mixin() { + @detached: { scope-detached: @see-here; }; + .nested() { + scope-mixin: @see-here; + } +} +.definer-wrapper() { + @see-here: yes; + .mixin(); +} +.selector { + .definer-wrapper(); + @detached(); + .nested(); +} + +//********************************************88 + * test if it works from imported !!!!!!!!!!!!!!!!!!!! + * test if it works when variables opy detached forever !!!!!!!!!!!!!!!!!!!! + * test if it works from list correctly -- e.g. including various callers scopes + * test if @defaults works correctly -- e.g. including various callers scopes */ -@Ignore +//@Ignore public class DetachedRulesetsTest extends BasicFeaturesTest { private static final String standardCases = "src/test/resources/compile-basic-features/detached-rulesets/"; diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-own-scope.css b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-own-scope.css new file mode 100644 index 00000000..91c58ee0 --- /dev/null +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-own-scope.css @@ -0,0 +1,4 @@ +tbd +selector { + property: in-unlocker; +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-own-scope.less b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-own-scope.less new file mode 100644 index 00000000..f96e1fa0 --- /dev/null +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-own-scope.less @@ -0,0 +1,28 @@ +#namespace { + @variable: in-unlocker; + .unlocker { + @detached-ruleset2: { property-2: @variable; }; + } +} +#namespace > .unlocker(); + +@detached-ruleset1: { property-1: @variable; }; +@list: @detached-ruleset1 @detached-ruleset2; + +.mixin(@list) { + @detached2: extract(@list, 2); + @detached2(); + @detached1: extract(@list, 1); + @detached1(); +} + +.indirect-mixin(@list) { + @variable: passing-through; + .mixin(@list); +} + +selector { + @variable: in-caller; + .indirect-mixin(@list) +} + diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-passed-around.css b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-passed-around.css new file mode 100644 index 00000000..93fd5e8c --- /dev/null +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-passed-around.css @@ -0,0 +1,3 @@ +selector { + property: passing-through; +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-passed-around.less b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-passed-around.less new file mode 100644 index 00000000..397156f3 --- /dev/null +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-ruleset-in-list-passed-around.less @@ -0,0 +1,18 @@ +@detached-ruleset1: { property: 1; }; +@detached-ruleset2: { property: @variable; }; +@list: @detached-ruleset1 @detached-ruleset2; + +.mixin(@list) { + @detached: extract(@list, 2); + @detached(); +} + +.indirect-mixin(@list) { + @variable: passing-through; + .mixin(@list); +} + +selector { + @variable: in-caller; + .indirect-mixin(@list) +} \ No newline at end of file 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 d5a2b834..4a7ccdcf 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 @@ -18,8 +18,6 @@ } } @media screen { - background: red; - .selector { background: green; } @@ -29,6 +27,19 @@ padding: 0; } } +@media screen { + .outer { + background: red; + } + .outer .selector { + background: green; + } +} +@media screen and (min-width: 1200) { + .outer .selector { + padding: 0; + } +} .mixin-with-arguments-enclosure .custom-mixin-argument { custom: custom; } @@ -37,7 +48,7 @@ property: default; property: global default; } -.see-caller use-place { +.see-caller-local-win use-place { caller-variable: declaration; variable: declaration; } @@ -57,18 +68,18 @@ .custom { property: custom; } -.caller { - property: caller; -} .caller .default { - property: caller; + property-in-caller: caller; } .caller .custom { - property: custom; + property-in-caller: custom; +} +.caller .default { + property-overwritten: overwritten; } .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 cd63e737..967da71a 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 @@ -1,29 +1,37 @@ @one-rule: { background: red; }; @ruleset: { .selector { background: green; } }; @ruleset-second: { .selector { size: 2; } }; -@media: { @media (min-width: 1200) { .selector { padding: 0; }} }; +@media-var: { @media (min-width: 1200) { .selector { padding: 0; }} }; @empty: {}; //call on top @ruleset(); @empty(); -@media(); +@media-var(); //call from ruleset .call-with-parentheses { @empty(); @one-rule(); @ruleset(); - @media(); + @media-var(); } //call from media @media screen { @empty(); - @one-rule(); @ruleset(); - @media(); + @media-var(); +} + +.outer { + @media screen { + @empty(); + @one-rule(); + @ruleset(); + @media-var(); + } } //mixin arguments @@ -56,7 +64,7 @@ } //scope - local win -.see-caller { +.see-caller-local-win { @detached-ruleset: { caller-variable: @variable; .mixin(); @@ -128,7 +136,7 @@ @with-mixin-caller-default: { .mixin(@parameter: @caller-default) { - property: @parameter; + property-in-caller: @parameter; } .default { .mixin(); @@ -137,9 +145,19 @@ .mixin(custom); } }; +@with-mixin-caller-default-overwritten: { + @caller-default:overwritten; + .mixin(@parameter: @caller-default) { + property-overwritten: @parameter; + } + .default { + .mixin(); + } +}; .caller { @caller-default: caller; @with-mixin-caller-default(); + @with-mixin-caller-default-overwritten(); } //unlock mixin see things defined in his scope first @@ -158,7 +176,7 @@ //detached returned from mixin .return-detached() { @color: #aabbcc; - @detached: {color: @color;} + @detached: {color: @color;}; } .selector-indirect { .return-detached(); diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-errors-additional.less b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-errors-additional.less new file mode 100644 index 00000000..4a0dbe91 --- /dev/null +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-errors-additional.less @@ -0,0 +1,4 @@ +//does not exists +.does-not-exists { + @does-not-exists(); +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-errors.less b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-errors.less index c5cbb566..d5c2d144 100644 --- a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-errors.less +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-errors.less @@ -33,4 +33,4 @@ b: { color: red; }; -} +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.less b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.less index 3c004c47..24f0fcfb 100644 --- a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.less +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.less @@ -14,7 +14,7 @@ visible-one: @b; visible-two: @c; } -}; +} .wrap-mixin({ color: black; @@ -45,7 +45,7 @@ header { .wrap-mixin-calls-wrap(@ruleset) { .wrap-mixin(@ruleset); -}; +} .wrap-mixin({ test: extra-wrap; 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 index ccef8d19..2cf80e72 100644 --- 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 @@ -3,4 +3,7 @@ } .selector-indirect { color: #ff0000; +} +.multiple-imports .selector { + scope: yes; } \ 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 index 1628b966..524c5fd6 100644 --- 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 @@ -1,3 +1,4 @@ +//can mixin find detached ruleset in callers scope? .whoReallyCalls(@detached) { @detached(); } @@ -25,3 +26,17 @@ @detached: { color: @color; }; } +//does it sees through multiple imports? +.multiple-imports { + .mixin() { + @detached: { scope: @see-here; }; + } + .definer-wrapper() { + @see-here: yes; + .mixin(); + } + .selector { + .definer-wrapper(); + @detached(); + } +} \ No newline at end of file diff --git a/src/test/resources/compile-basic-features/mixins/mixins-imported-with-default.css b/src/test/resources/compile-basic-features/mixins/mixins-imported-with-default.css new file mode 100644 index 00000000..ca0980fd --- /dev/null +++ b/src/test/resources/compile-basic-features/mixins/mixins-imported-with-default.css @@ -0,0 +1,6 @@ +.caller .default { + property-mixin: caller; +} +.caller .custom { + property-mixin: custom; +} diff --git a/src/test/resources/compile-basic-features/mixins/mixins-imported-with-default.less b/src/test/resources/compile-basic-features/mixins/mixins-imported-with-default.less new file mode 100644 index 00000000..22d898d3 --- /dev/null +++ b/src/test/resources/compile-basic-features/mixins/mixins-imported-with-default.less @@ -0,0 +1,26 @@ +.with-mixin-caller-default() { + .mixin(@parameter: @caller-default) { + property-mixin: @parameter; + } + .default { + .mixin(); + } + .custom { + .mixin(custom); + } +} +.with-mixin-caller-default-overwritten() { + @caller-default: overwritten; + .mixin(@parameter: @caller-default) { + property-own: @parameter; + } + .default { + @caller-default: not this one; + .mixin(); + } +} +.caller { + @caller-default: caller; + .with-mixin-caller-default(); + .with-mixin-caller-default-overwritten(); +} \ No newline at end of file diff --git a/src/test/resources/minitests/debug1.css b/src/test/resources/minitests/debug1.css index ba59e2a7..d5d32ba5 100644 --- a/src/test/resources/minitests/debug1.css +++ b/src/test/resources/minitests/debug1.css @@ -1,5 +1,82 @@ -@keyframes /* 1 */ sunrise { - h1 { - color: #ff00ff; - } +.selector { + background: green; +} +@media (min-width: 1200) { + .selector { + padding: 0; + } +} +.call-with-parentheses { + background: red; +} +.call-with-parentheses .selector { + background: green; +} +@media (min-width: 1200) { + .call-with-parentheses .selector { + padding: 0; + } +} +@media screen { + .selector { + background: green; + } +} +@media screen and (min-width: 1200) { + .selector { + padding: 0; + } +} +@media screen { + .outer { + background: red; + } + .outer .selector { + background: green; + } +} +@media screen and (min-width: 1200) { + .outer .selector { + padding: 0; + } +} +.mixin-with-arguments-enclosure .custom-mixin-argument { + custom: custom; +} +.see-caller use-place { + caller-variable: value; + property: default; + property: global default; +} +.see-caller-local-win use-place { + caller-variable: declaration; + variable: declaration; +} +.see-caller-caller use-place { + property: default; + property: global default; +} +.default { + property: default; +} +.custom { + property: custom; +} +.default { + property: global default; +} +.custom { + property: custom; +} +.caller .default { + property: caller; +} +.caller .custom { + property: custom; +} +.caller { + value: in detached; +} +.selector-indirect { + color: #aabbcc; } diff --git a/src/test/resources/minitests/debug1.less b/src/test/resources/minitests/debug1.less index 2a54cf34..60303f15 100644 --- a/src/test/resources/minitests/debug1.less +++ b/src/test/resources/minitests/debug1.less @@ -1,5 +1,15 @@ -.selector { - @detached: { extreme: simplicity; }; +//can mixin find detached ruleset in callers scope? +.whoReallyCalls(@detached) { @detached(); } +.modifyScope(@detached) { + .whoReallyCalls(@detached); + @color: green; +} + +.selector-direct { + .modifyScope(@detached); + @detached: { color: @color; }; +} +