Skip to content

Commit

Permalink
Add support for custom property syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
xzyfer committed Jan 14, 2018
1 parent 57c5da5 commit efd97da
Show file tree
Hide file tree
Showing 15 changed files with 168 additions and 10 deletions.
7 changes: 7 additions & 0 deletions src/ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2394,6 +2394,13 @@ namespace Sass {
return quote(value_, '*');
}

bool Declaration::is_invisible() const
{
if (is_custom_property()) return false;

return !(value_ && value_->concrete_type() != Expression::NULL_VAL);
}

//////////////////////////////////////////////////////////////////////////////////////////
// Additional method on Lists to retrieve values directly or from an encompassed Argument.
//////////////////////////////////////////////////////////////////////////////////////////
Expand Down
7 changes: 5 additions & 2 deletions src/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,19 +660,22 @@ namespace Sass {
ADD_PROPERTY(String_Obj, property)
ADD_PROPERTY(Expression_Obj, value)
ADD_PROPERTY(bool, is_important)
ADD_PROPERTY(bool, is_custom_property)
ADD_PROPERTY(bool, is_indented)
public:
Declaration(ParserState pstate,
String_Obj prop, Expression_Obj val, bool i = false, Block_Obj b = 0)
: Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_indented_(false)
String_Obj prop, Expression_Obj val, bool i = false, bool c = false, Block_Obj b = 0)
: Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false)
{ statement_type(DECLARATION); }
Declaration(const Declaration* ptr)
: Has_Block(ptr),
property_(ptr->property_),
value_(ptr->value_),
is_important_(ptr->is_important_),
is_custom_property_(ptr->is_custom_property_),
is_indented_(ptr->is_indented_)
{ statement_type(DECLARATION); }
virtual bool is_invisible() const;
ATTACH_AST_OPERATIONS(Declaration)
ATTACH_OPERATIONS()
};
Expand Down
1 change: 1 addition & 0 deletions src/constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ namespace Sass {
extern const char keyframes_kwd[] = "keyframes";
extern const char only_kwd[] = "only";
extern const char rgb_fn_kwd[] = "rgb(";
extern const char url_fn_kwd[] = "url(";
extern const char url_kwd[] = "url";
// extern const char url_prefix_fn_kwd[] = "url-prefix(";
extern const char important_kwd[] = "important";
Expand Down
1 change: 1 addition & 0 deletions src/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ namespace Sass {
extern const char keyframes_kwd[];
extern const char only_kwd[];
extern const char rgb_fn_kwd[];
extern const char url_fn_kwd[];
extern const char url_kwd[];
// extern const char url_prefix_fn_kwd[];
extern const char important_kwd[];
Expand Down
3 changes: 2 additions & 1 deletion src/cssize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ namespace Sass {
d->pstate(),
property,
d->value(),
d->is_important());
d->is_important(),
d->is_custom_property());
dd->is_indented(d->is_indented());
dd->tabs(d->tabs());

Expand Down
1 change: 1 addition & 0 deletions src/debugger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)
Declaration_Ptr block = Cast<Declaration>(node);
std::cerr << ind << "Declaration " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [is_custom_property: " << block->is_custom_property() << "] ";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->property(), ind + " prop: ", env);
debug_ast(block->value(), ind + " value: ", env);
Expand Down
3 changes: 2 additions & 1 deletion src/emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace Sass {
scheduled_linefeed(0),
scheduled_delimiter(false),
scheduled_mapping(0),
in_custom_property(false),
in_comment(false),
in_wrapped(false),
in_media_block(false),
Expand Down Expand Up @@ -209,7 +210,7 @@ namespace Sass {
{
scheduled_space = 0;
append_string(":");
append_optional_space();
if (!in_custom_property) append_optional_space();
}

void Emitter::append_mandatory_space()
Expand Down
2 changes: 2 additions & 0 deletions src/emitter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ namespace Sass {
AST_Node_Ptr scheduled_mapping;

public:
// output strings different in custom css properties
bool in_custom_property;
// output strings different in comments
bool in_comment;
// selector list does not get linefeeds
Expand Down
1 change: 1 addition & 0 deletions src/expand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ namespace Sass {
new_p,
value,
d->is_important(),
d->is_custom_property(),
bb);
decl->tabs(d->tabs());
return decl;
Expand Down
2 changes: 2 additions & 0 deletions src/inspect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ namespace Sass {
if (dec->value()->concrete_type() == Expression::NULL_VAL) return;
bool was_decl = in_declaration;
in_declaration = true;
LOCAL_FLAG(in_custom_property, dec->is_custom_property());

if (output_style() == NESTED)
indentation += dec->tabs();
append_indentation();
Expand Down
2 changes: 1 addition & 1 deletion src/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ namespace Sass {
if (s->can_compress_whitespace() && output_style() == COMPRESSED) {
value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
}
if (!in_comment) {
if (!in_comment && !in_custom_property) {
append_token(string_to_output(value), s);
} else {
append_token(value, s);
Expand Down
96 changes: 91 additions & 5 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,13 @@ namespace Sass {
}

// selector may contain interpolations which need delayed evaluation
else if (!(lookahead_result = lookahead_for_selector(position)).error)
{ block->append(parse_ruleset(lookahead_result)); }
else if (
!(lookahead_result = lookahead_for_selector(position)).error &&
!lookahead_result.is_custom_property
)
{
block->append(parse_ruleset(lookahead_result));
}

// parse multiple specific keyword directives
else if (lex < kwd_media >(true)) { block->append(parse_media_block()); }
Expand Down Expand Up @@ -1019,10 +1024,15 @@ namespace Sass {

Declaration_Obj Parser::parse_declaration() {
String_Obj prop;
bool is_custom_property = false;
if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
const std::string property(lexed);
is_custom_property = property.compare(0, 2, "--") == 0;
prop = parse_identifier_schema();
}
else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {
const std::string property(lexed);
is_custom_property = property.compare(0, 2, "--") == 0;
prop = SASS_MEMORY_NEW(String_Constant, pstate, lexed);
}
else {
Expand All @@ -1031,9 +1041,12 @@ namespace Sass {
bool is_indented = true;
const std::string property(lexed);
if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);
if (!is_custom_property && match< sequence< optional_css_comments, exactly<';'> > >()) error("style declaration must contain a value", pstate);
if (match< sequence< optional_css_comments, exactly<'{'> > >()) is_indented = false; // don't indent if value is empty
if (is_custom_property) {
return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_css_variable_value(), false, true);
}
lex < css_comments >(false);
if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);
if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty
if (peek_css< static_value >()) {
return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_static_value()/*, lex<kwd_important>()*/);
}
Expand Down Expand Up @@ -1751,13 +1764,80 @@ namespace Sass {
return schema.detach();
}

String_Schema_Obj Parser::parse_css_variable_value(bool top_level)
{
String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
String_Schema_Obj tok;
if (!(tok = parse_css_variable_value_token(top_level))) {
return NULL;
}

schema->concat(tok);
while ((tok = parse_css_variable_value_token(top_level))) {
schema->concat(tok);
}

return schema.detach();
}

String_Schema_Obj Parser::parse_css_variable_value_token(bool top_level)
{
String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
if (
(top_level && lex< css_variable_top_level_value >(false)) ||
(!top_level && lex< css_variable_value >(false))
) {
Token str(lexed);
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));
}
else if (Expression_Obj tok = lex_interpolation()) {
if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {
schema->concat(s);
} else {
schema->append(tok);
}
}
else if (lex< quoted_string >()) {
Expression_Obj tok = parse_string();
if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {
schema->concat(s);
} else {
schema->append(tok);
}
}
else {
if (peek< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {
if (lex< exactly<'('> >()) {
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("(")));
if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);
if (!lex< exactly<')'> >()) css_error("Invalid CSS", " after ", ": expected \")\", was ");
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(")")));
}
else if (lex< exactly<'['> >()) {
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("[")));
if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);
if (!lex< exactly<']'> >()) css_error("Invalid CSS", " after ", ": expected \"]\", was ");
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("]")));
}
else if (lex< exactly<'{'> >()) {
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("{")));
if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);
if (!lex< exactly<'}'> >()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("}")));
}
}
}

return schema->length() > 0 ? schema.detach() : NULL;
}

String_Constant_Obj Parser::parse_static_value()
{
lex< static_value >();
Token str(lexed);
// static values always have trailing white-
// space and end delimiter (\s*[;]$) included
-- pstate.offset.column;
--pstate.offset.column;
--str.end;
--position;

Expand Down Expand Up @@ -2679,12 +2759,18 @@ namespace Sass {
re_selector_list
>(p)
) {
bool could_be_property = peek< sequence< exactly<'-'>, exactly<'-'> > >(p);
while (p < q) {
// did we have interpolations?
if (*p == '#' && *(p+1) == '{') {
rv.has_interpolants = true;
p = q; break;
}
// A property that's ambiguous with a nested selector is interpreted as a
// custom property.
if (*p == ':') {
rv.is_custom_property = could_be_property || p+1 == q || peek< space >(p+1);
}
++ p;
}
// store anyway }
Expand Down
3 changes: 3 additions & 0 deletions src/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct Lookahead {
const char* position;
bool parsable;
bool has_interpolants;
bool is_custom_property;
};

namespace Sass {
Expand Down Expand Up @@ -285,6 +286,8 @@ namespace Sass {
String_Obj parse_interpolated_chunk(Token, bool constant = false);
String_Obj parse_string();
String_Constant_Obj parse_static_value();
String_Schema_Obj parse_css_variable_value(bool top_level = true);
String_Schema_Obj parse_css_variable_value_token(bool top_level = true);
String_Obj parse_ie_property();
String_Obj parse_ie_keyword_arg();
String_Schema_Obj parse_value_schema(const char* stop);
Expand Down
45 changes: 45 additions & 0 deletions src/prelexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1461,6 +1461,16 @@ namespace Sass {
// >(src);
// }

const char* real_uri(const char* src) {
return sequence<
exactly< url_kwd >,
exactly< '(' >,
W,
real_uri_value,
exactly< ')' >
>(src);
}

const char* real_uri_suffix(const char* src) {
return sequence< W, exactly< ')' > >(src);
}
Expand Down Expand Up @@ -1511,6 +1521,7 @@ namespace Sass {
static_string,
percentage,
hex,
hexa,
exactly<'|'>,
// exactly<'+'>,
sequence < number, unit_identifier >,
Expand Down Expand Up @@ -1578,6 +1589,40 @@ namespace Sass {
>(src);
}

extern const char css_variable_url_negates[] = "()[]{}\"'#/";
const char* css_variable_value(const char* src) {
return sequence<
alternatives<
sequence<
negate< exactly< url_fn_kwd > >,
one_plus< neg_class_char< css_variable_url_negates > >
>,
sequence< exactly<'#'>, negate< exactly<'{'> > >,
sequence< exactly<'/'>, negate< exactly<'*'> > >,
static_string,
real_uri,
block_comment
>
>(src);
}

extern const char css_variable_url_top_level_negates[] = "()[]{}\"'#/;!";
const char* css_variable_top_level_value(const char* src) {
return sequence<
alternatives<
sequence<
negate< exactly< url_fn_kwd > >,
one_plus< neg_class_char< css_variable_url_top_level_negates > >
>,
sequence< exactly<'#'>, negate< exactly<'{'> > >,
sequence< exactly<'/'>, negate< exactly<'*'> > >,
static_string,
real_uri,
block_comment
>
>(src);
}

const char* parenthese_scope(const char* src) {
return sequence <
exactly < '(' >,
Expand Down
4 changes: 4 additions & 0 deletions src/prelexer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ namespace Sass {
const char* UUNICODE(const char* src);
const char* NONASCII(const char* src);
const char* ESCAPE(const char* src);
const char* real_uri(const char* src);
const char* real_uri_suffix(const char* src);
// const char* real_uri_prefix(const char* src);
const char* real_uri_value(const char* src);
Expand All @@ -379,6 +380,9 @@ namespace Sass {
const char* static_property(const char* src);
const char* static_value(const char* src);

const char* css_variable_value(const char* src);
const char* css_variable_top_level_value(const char* src);

// Utility functions for finding and counting characters in a string.
template<char c>
const char* find_first(const char* src) {
Expand Down

0 comments on commit efd97da

Please sign in to comment.