Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom property syntax #2534

Merged
merged 2 commits into from
Jan 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new property is not copied when cloning here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch

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
3 changes: 2 additions & 1 deletion src/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ namespace Sass {
"global-variable-shadowing",
"extend-selector-pseudoclass",
"at-error",
"units-level-3"
"units-level-3",
"custom-property"
};

////////////////
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