Skip to content
This repository has been archived by the owner on Feb 23, 2022. It is now read-only.

Commit

Permalink
Allow .^ClassName in @component blocks to suppress prefixing
Browse files Browse the repository at this point in the history
Fixes issue #73.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=110368380
  • Loading branch information
SLaks authored and iflan committed Mar 9, 2016
1 parent 0b446fd commit 2e059b0
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 21 deletions.
29 changes: 18 additions & 11 deletions src/com/google/common/css/compiler/ast/CssClassSelectorNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,36 @@
* @author fbenz@google.com (Florian Benz)
*/
public class CssClassSelectorNode extends CssRefinerNode {
private final boolean componentScoped;
/** Specifies the kind or absence of a component scoping prefix. */
public static enum ComponentScoping {
/** The classname has no prefix. */
DEFAULT,
/** The classname has a % prefix, to force scoping. */
FORCE_SCOPED,
/** The classname has a ^ prefix, to prevent scoping. */
FORCE_UNSCOPED
}

private final ComponentScoping scoping;

public CssClassSelectorNode(String refinerName, boolean componentScoped,
public CssClassSelectorNode(String refinerName, ComponentScoping scoping,
SourceCodeLocation sourceCodeLocation) {
super(Refiner.CLASS, refinerName, sourceCodeLocation);
this.componentScoped = componentScoped;
this.scoping = scoping;
}

public CssClassSelectorNode(String refinerName, SourceCodeLocation sourceCodeLocation) {
this(refinerName, false, sourceCodeLocation);
this(refinerName, ComponentScoping.DEFAULT, sourceCodeLocation);
}

protected CssClassSelectorNode(CssClassSelectorNode node) {
this(node.refinerName, node.componentScoped, node.getSourceCodeLocation());
this(node.refinerName, node.scoping, node.getSourceCodeLocation());
this.setComments(node.getComments());
}

/**
* Returns {@code true} if this class selector was prefixed with a percent-sign, indicating that
* it should be prefixed with the current @component's class prefix.
*/
public boolean isComponentScoped() {
return componentScoped;
/** Returns the kind or absence of a component scoping prefix. */
public ComponentScoping getScoping() {
return scoping;
}

@Override
Expand Down
17 changes: 10 additions & 7 deletions src/com/google/common/css/compiler/ast/GssParserCC.jj
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,9 @@ public class GssParserCC {
}

public CssClassSelectorNode buildClassSelectorNode(String name,
SourceCodeLocation location, boolean isComponentScoped, List<Token> tokens) {
CssClassSelectorNode node = new CssClassSelectorNode(name, isComponentScoped, location);
SourceCodeLocation location, CssClassSelectorNode.ComponentScoping scoping,
List<Token> tokens) {
CssClassSelectorNode node = new CssClassSelectorNode(name, scoping, location);
attachComments(tokens, node);
return node;
}
Expand Down Expand Up @@ -595,7 +596,7 @@ PARSER_END(GssParserCC)
| < #HASH: "#" >
| < #UNDERSCORE: "_" >
| < #AMPERSAND: "&" >
| < #CARET: "^" >
| < CARET: "^" >
| < #DOLLAR: "$" >
| < #PIPE: "|" >
| < AND: <AMPERSAND> <AMPERSAND> >
Expand Down Expand Up @@ -863,16 +864,18 @@ CssClassSelectorNode className() :
{
Token t;
List<Token> tokens = Lists.newArrayList();
boolean isComponentScoped = false;
CssClassSelectorNode.ComponentScoping scoping = CssClassSelectorNode.ComponentScoping.DEFAULT;
}
{
t = <DOT> { tokens.add(t); }
( <PERCENT> { isComponentScoped = true; } )?
(
( <PERCENT> { scoping = CssClassSelectorNode.ComponentScoping.FORCE_SCOPED; } ) |
( <CARET> { scoping = CssClassSelectorNode.ComponentScoping.FORCE_UNSCOPED; } )
)?
t = <IDENTIFIER>
{
tokens.add(t);
return nodeBuilder.buildClassSelectorNode(t.image, this.getLocation(), isComponentScoped,
tokens);
return nodeBuilder.buildClassSelectorNode(t.image, this.getLocation(), scoping, tokens);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public boolean enterClassSelector(CssClassSelectorNode classSelector) {
visitController.replaceCurrentBlockChildWith(
ImmutableList.of(new CssClassSelectorNode(
refinerName,
classSelector.isComponentScoped(),
classSelector.getScoping(),
classSelector.getSourceCodeLocation())),
true);
}
Expand Down
16 changes: 14 additions & 2 deletions src/com/google/common/css/compiler/passes/ProcessComponents.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.google.common.css.compiler.ast.CssAtRuleNode;
import com.google.common.css.compiler.ast.CssBlockNode;
import com.google.common.css.compiler.ast.CssClassSelectorNode;
import com.google.common.css.compiler.ast.CssClassSelectorNode.ComponentScoping;
import com.google.common.css.compiler.ast.CssCombinatorNode;
import com.google.common.css.compiler.ast.CssCompilerPass;
import com.google.common.css.compiler.ast.CssComponentNode;
Expand Down Expand Up @@ -141,11 +142,16 @@ public boolean enterClassSelector(CssClassSelectorNode node) {
// Note that this works because enterComponent, above, returns false -
// this visitor never sees class selectors inside components (the other
// visitor does).
if (node.isComponentScoped()) {
if (node.getScoping() == ComponentScoping.FORCE_SCOPED) {
reportError("'%' prefix for class selectors may only be used in the scope of an @component",
node);
return false;
}
if (node.getScoping() == ComponentScoping.FORCE_UNSCOPED) {
reportError("'^' prefix for class selectors may only be used in the scope of an @component",
node);
return false;
}
return true;
}

Expand Down Expand Up @@ -362,7 +368,13 @@ public void leavePseudoClass(CssPseudoClassNode pseudoClass) {
@Override
public boolean enterClassSelector(CssClassSelectorNode node) {
Preconditions.checkState(!isAbstract);
if (firstClassSelector || node.isComponentScoped()) {
if (!firstClassSelector && node.getScoping() == ComponentScoping.FORCE_UNSCOPED) {
errorManager.report(new GssError(
"'^' prefix may only be used on the first classname in a selector.",
node.getSourceCodeLocation()));
}
if (firstClassSelector && node.getScoping() != ComponentScoping.FORCE_UNSCOPED
|| node.getScoping() == ComponentScoping.FORCE_SCOPED) {
CssClassSelectorNode newNode = new CssClassSelectorNode(
classPrefix + node.getRefinerName(),
inAncestorBlock ? sourceCodeLocation : node.getSourceCodeLocation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ public class ProcessComponentsTest extends PassesTestBase {
" TD.PREFIX_F1 TD.NO_PREFIX_F2,", // Multiple element refiners
" #X.PREFIX_G1.NO_PREFIX_G2,", // ID refiner
" #X.PREFIX_H1 .NO_PREFIX_H2,", // ID refiner with combinator
" .^NO_PREFIX_A1.NO_PREFIX_A2,", // Explicit unscoped with complex selector
" .PREFIX_I1.%PREFIX_I2,", // Explicit scoped with complex selector
" .PREFIX_J1 .%PREFIX_J2,", // Explicit scoped with descendant combinator
" .PREFIX_K1 > .%PREFIX_K2,", // Explicit scoped with child combinator
Expand Down Expand Up @@ -291,6 +292,7 @@ public class ProcessComponentsTest extends PassesTestBase {
"TD.someExamplePackagePREFIX_F1 TD.NO_PREFIX_F2," +
"#X.someExamplePackagePREFIX_G1.NO_PREFIX_G2," +
"#X.someExamplePackagePREFIX_H1 .NO_PREFIX_H2," +
".NO_PREFIX_A1.NO_PREFIX_A2," +
".someExamplePackagePREFIX_I1.someExamplePackagePREFIX_I2," +
".someExamplePackagePREFIX_J1 .someExamplePackagePREFIX_J2," +
".someExamplePackagePREFIX_K1>.someExamplePackagePREFIX_K2," +
Expand Down

0 comments on commit 2e059b0

Please sign in to comment.