mirror of https://github.com/Wilfred/difftastic/
399 lines
11 KiB
JavaScript
399 lines
11 KiB
JavaScript
module.exports = grammar({
|
|
name: "scss",
|
|
|
|
extras: ($) => [/\s/, $.comment, $.single_line_comment],
|
|
|
|
externals: ($) => [$._descendant_operator],
|
|
|
|
conflicts: ($) => [[$._selector, $.declaration]],
|
|
|
|
inline: ($) => [$._top_level_item, $._block_item],
|
|
|
|
rules: {
|
|
stylesheet: ($) => repeat($._top_level_item),
|
|
|
|
_top_level_item: ($) =>
|
|
choice(
|
|
$.declaration,
|
|
$.rule_set,
|
|
$.import_statement,
|
|
$.media_statement,
|
|
$.charset_statement,
|
|
$.namespace_statement,
|
|
$.keyframes_statement,
|
|
$.supports_statement,
|
|
$.use_statement,
|
|
$.forward_statement,
|
|
$.apply_statement,
|
|
$.mixin_statement,
|
|
$.include_statement,
|
|
$.if_statement,
|
|
$.each_statement,
|
|
$.for_statement,
|
|
$.while_statement,
|
|
$.function_statement,
|
|
$.error_statement,
|
|
$.warn_statement,
|
|
$.debug_statement,
|
|
$.at_rule,
|
|
$.placeholder
|
|
),
|
|
|
|
// Statements
|
|
|
|
import_statement: ($) => seq("@import", $._value, sep(",", $._query), ";"),
|
|
|
|
media_statement: ($) => seq("@media", sep1(",", $._query), $.block),
|
|
|
|
charset_statement: ($) => seq("@charset", $._value, ";"),
|
|
|
|
namespace_statement: ($) =>
|
|
seq(
|
|
"@namespace",
|
|
optional(alias($.identifier, $.namespace_name)),
|
|
choice($.string_value, $.call_expression),
|
|
";"
|
|
),
|
|
|
|
keyframes_statement: ($) =>
|
|
seq(
|
|
choice("@keyframes", alias(/@[-a-z]+keyframes/, $.at_keyword)),
|
|
alias($.identifier, $.keyframes_name),
|
|
$.keyframe_block_list
|
|
),
|
|
|
|
keyframe_block_list: ($) => seq("{", repeat($.keyframe_block), "}"),
|
|
|
|
keyframe_block: ($) => seq(choice($.from, $.to, $.integer_value), $.block),
|
|
|
|
from: ($) => "from",
|
|
to: ($) => "to",
|
|
|
|
supports_statement: ($) => seq("@supports", $._query, $.block),
|
|
|
|
at_rule: ($) => seq($.at_keyword, sep(",", $._query), choice(";", $.block)),
|
|
|
|
use_statement: ($) => seq("@use", $._value, ";"),
|
|
|
|
forward_statement: ($) => seq("@forward", $._value, ";"),
|
|
|
|
apply_statement: ($) => seq("@apply", repeat($._value), ";"),
|
|
|
|
parameters: ($) => seq("(", sep1(",", $.parameter), ")"),
|
|
|
|
parameter: ($) =>
|
|
seq(
|
|
alias($.variable_identifier, $.variable_name),
|
|
optional(seq(":", alias($._value, $.default_value)))
|
|
),
|
|
|
|
mixin_statement: ($) =>
|
|
seq("@mixin", alias($.identifier, $.name), optional($.parameters), $.block),
|
|
|
|
include_statement: ($) =>
|
|
seq(
|
|
"@include",
|
|
$.identifier,
|
|
optional(alias($.include_arguments, $.arguments)),
|
|
choice($.block, ";")
|
|
),
|
|
|
|
include_arguments: ($) =>
|
|
seq(
|
|
token.immediate("("),
|
|
sep1(",", alias($.include_argument, $.argument)),
|
|
token.immediate(")")
|
|
),
|
|
|
|
include_argument: ($) =>
|
|
seq(
|
|
optional(seq(alias($.variable_identifier, $.argument_name), ":")),
|
|
alias($._value, $.argument_value)
|
|
),
|
|
|
|
placeholder: ($) => seq("%", alias($.identifier, $.name), $.block),
|
|
|
|
extend_statement: ($) => seq("@extend", choice($._value, $.class_selector), ";"),
|
|
|
|
if_statement: ($) => seq($.if_clause, repeat($.else_if_clause), optional($.else_clause)),
|
|
|
|
if_clause: ($) => seq("@if", alias($._value, $.condition), $.block),
|
|
|
|
else_if_clause: ($) => seq("@else", "if", alias($._value, $.condition), $.block),
|
|
|
|
else_clause: ($) => seq("@else", $.block),
|
|
|
|
each_statement: ($) =>
|
|
seq(
|
|
"@each",
|
|
optional(seq(alias($.variable_identifier, $.key), ",")),
|
|
alias($.variable_identifier, $.value),
|
|
"in",
|
|
$._value,
|
|
$.block
|
|
),
|
|
|
|
for_statement: ($) =>
|
|
seq(
|
|
"@for",
|
|
alias($.variable_identifier, $.variable),
|
|
"from",
|
|
alias($._value, $.from),
|
|
"through",
|
|
alias($._value, $.through),
|
|
$.block
|
|
),
|
|
|
|
while_statement: ($) => seq("@while", $._value, $.block),
|
|
|
|
function_statement: ($) =>
|
|
seq("@function", alias($.identifier, $.name), optional($.parameters), $.block),
|
|
|
|
return_statement: ($) => seq("@return", $._value, ";"),
|
|
|
|
at_root_statement: ($) => seq("@at-root", $._value, $.block),
|
|
|
|
error_statement: ($) => seq("@error", $._value, ";"),
|
|
|
|
warn_statement: ($) => seq("@warn", $._value, ";"),
|
|
|
|
debug_statement: ($) => seq("@debug", $._value, ";"),
|
|
|
|
// Rule sets
|
|
|
|
rule_set: ($) => seq($.selectors, $.block),
|
|
|
|
selectors: ($) => sep1(",", $._selector),
|
|
|
|
block: ($) =>
|
|
seq("{", repeat($._block_item), optional(alias($.last_declaration, $.declaration)), "}"),
|
|
|
|
_block_item: ($) =>
|
|
choice(
|
|
$.declaration,
|
|
$.rule_set,
|
|
$.import_statement,
|
|
$.media_statement,
|
|
$.charset_statement,
|
|
$.namespace_statement,
|
|
$.keyframes_statement,
|
|
$.supports_statement,
|
|
$.mixin_statement,
|
|
$.include_statement,
|
|
$.extend_statement,
|
|
$.if_statement,
|
|
$.each_statement,
|
|
$.for_statement,
|
|
$.while_statement,
|
|
$.function_statement,
|
|
$.return_statement,
|
|
$.at_root_statement,
|
|
$.error_statement,
|
|
$.warn_statement,
|
|
$.debug_statement,
|
|
$.at_rule
|
|
),
|
|
|
|
// Selectors
|
|
|
|
_selector: ($) =>
|
|
choice(
|
|
$.universal_selector,
|
|
alias($.identifier, $.tag_name),
|
|
$.class_selector,
|
|
$.nesting_selector,
|
|
$.pseudo_class_selector,
|
|
$.pseudo_element_selector,
|
|
$.id_selector,
|
|
$.attribute_selector,
|
|
$.string_value,
|
|
$.child_selector,
|
|
$.descendant_selector,
|
|
$.sibling_selector,
|
|
$.adjacent_sibling_selector
|
|
),
|
|
|
|
nesting_selector: ($) => "&",
|
|
|
|
universal_selector: ($) => "*",
|
|
|
|
class_selector: ($) =>
|
|
prec(1, seq(optional($._selector), choice(".", $.nesting_selector), alias($.identifier, $.class_name))),
|
|
|
|
pseudo_class_selector: ($) =>
|
|
seq(
|
|
optional($._selector),
|
|
":",
|
|
alias($.identifier, $.class_name),
|
|
optional(alias($.pseudo_class_arguments, $.arguments))
|
|
),
|
|
|
|
pseudo_element_selector: ($) =>
|
|
seq(optional($._selector), "::", alias($.identifier, $.tag_name)),
|
|
|
|
id_selector: ($) => seq(optional($._selector), "#", alias($.identifier, $.id_name)),
|
|
|
|
attribute_selector: ($) =>
|
|
seq(
|
|
optional($._selector),
|
|
"[",
|
|
alias($.identifier, $.attribute_name),
|
|
optional(seq(choice("=", "~=", "^=", "|=", "*=", "$="), $._value)),
|
|
"]"
|
|
),
|
|
|
|
child_selector: ($) => prec.left(seq($._selector, ">", $._selector)),
|
|
|
|
descendant_selector: ($) => prec.left(seq($._selector, $._descendant_operator, $._selector)),
|
|
|
|
sibling_selector: ($) => prec.left(seq($._selector, "~", $._selector)),
|
|
|
|
adjacent_sibling_selector: ($) => prec.left(seq($._selector, "+", $._selector)),
|
|
|
|
pseudo_class_arguments: ($) =>
|
|
seq(token.immediate("("), sep(",", choice($._selector, repeat1($._value))), ")"),
|
|
|
|
// Declarations
|
|
|
|
declaration: ($) =>
|
|
seq(
|
|
choice(alias($.variable_identifier, $.variable_name), alias($.identifier, $.property_name)),
|
|
":",
|
|
$._value,
|
|
repeat(seq(optional(","), $._value)),
|
|
optional($.important),
|
|
";"
|
|
),
|
|
|
|
last_declaration: ($) =>
|
|
prec(
|
|
1,
|
|
seq(
|
|
alias($.identifier, $.property_name),
|
|
":",
|
|
$._value,
|
|
repeat(seq(optional(","), $._value)),
|
|
optional($.important)
|
|
)
|
|
),
|
|
|
|
important: ($) => "!important",
|
|
|
|
// Media queries
|
|
|
|
_query: ($) =>
|
|
choice(
|
|
alias($.identifier, $.keyword_query),
|
|
$.feature_query,
|
|
$.binary_query,
|
|
$.unary_query,
|
|
$.selector_query,
|
|
$.parenthesized_query
|
|
),
|
|
|
|
feature_query: ($) =>
|
|
seq("(", alias($.identifier, $.feature_name), ":", repeat1($._value), ")"),
|
|
|
|
parenthesized_query: ($) => seq("(", $._query, ")"),
|
|
|
|
binary_query: ($) => prec.left(seq($._query, choice("and", "or"), $._query)),
|
|
|
|
unary_query: ($) => prec(1, seq(choice("not", "only"), $._query)),
|
|
|
|
selector_query: ($) => seq("selector", "(", $._selector, ")"),
|
|
|
|
// Property Values
|
|
|
|
_value: ($) =>
|
|
prec(
|
|
-1,
|
|
choice(
|
|
alias($.identifier, $.plain_value),
|
|
alias($.variable_identifier, $.variable_value),
|
|
$.plain_value,
|
|
$.color_value,
|
|
$.integer_value,
|
|
$.float_value,
|
|
$.string_value,
|
|
$.binary_expression,
|
|
$.parenthesized_value,
|
|
$.call_expression
|
|
)
|
|
),
|
|
|
|
parenthesized_value: ($) => seq("(", $._value, ")"),
|
|
|
|
color_value: ($) => seq("#", token.immediate(/[0-9a-fA-F]{3,8}/)),
|
|
|
|
string_value: ($) =>
|
|
token(choice(seq("'", /([^'\n]|\\(.|\n))*/, "'"), seq('"', /([^"\n]|\\(.|\n))*/, '"'))),
|
|
|
|
integer_value: ($) => seq(token(seq(optional(choice("+", "-")), /\d+/)), optional($.unit)),
|
|
|
|
float_value: ($) =>
|
|
seq(
|
|
token(
|
|
seq(
|
|
optional(choice("+", "-")),
|
|
/\d*/,
|
|
choice(
|
|
seq(".", /\d+/),
|
|
seq(/[eE]/, optional("-"), /\d+/),
|
|
seq(".", /\d+/, /[eE]/, optional("-"), /\d+/)
|
|
)
|
|
)
|
|
),
|
|
optional($.unit)
|
|
),
|
|
|
|
unit: ($) => token.immediate(/[a-zA-Z%]+/),
|
|
|
|
call_expression: ($) => seq(alias($.identifier, $.function_name), $.arguments),
|
|
|
|
binary_expression: ($) =>
|
|
prec.left(
|
|
seq($._value, choice("+", "-", "*", "/", "==", "<", ">", "!=", "<=", ">="), $._value)
|
|
),
|
|
|
|
arguments: ($) => seq(token.immediate("("), sep(choice(",", ";"), repeat1($._value)), ")"),
|
|
|
|
identifier: ($) =>
|
|
/((#\{[a-zA-Z0-9-_,&\$\.\(\) ]*\})|(--|-?[a-zA-Z_]))([a-zA-Z0-9-_]|(#\{[a-zA-Z0-9-_,&\$\.\(\) ]*\}))*/,
|
|
|
|
variable_identifier: ($) => /([a-zA-Z_]+\.)?\$[a-zA-Z-_][a-zA-Z0-9-_]*/,
|
|
|
|
at_keyword: ($) => /@[a-zA-Z-_]+/,
|
|
|
|
comment: ($) => token(seq("/*", /[^*]*\*+([^/*][^*]*\*+)*/, "/")),
|
|
|
|
single_line_comment: ($) => token(seq("//", /.*/)),
|
|
|
|
plain_value: ($) =>
|
|
token(
|
|
seq(
|
|
repeat(
|
|
choice(
|
|
/[-_]/,
|
|
/\/[^\*\s,;!{}()\[\]]/ // Slash not followed by a '*' (which would be a comment)
|
|
)
|
|
),
|
|
/[a-zA-Z]/,
|
|
repeat(
|
|
choice(
|
|
/[^/\s,;!{}()\[\]]/, // Not a slash, not a delimiter character
|
|
/\/[^\*\s,;!{}()\[\]]/ // Slash not followed by a '*' (which would be a comment)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
},
|
|
});
|
|
|
|
function sep(separator, rule) {
|
|
return optional(sep1(separator, rule));
|
|
}
|
|
|
|
function sep1(separator, rule) {
|
|
return seq(rule, repeat(seq(separator, rule)));
|
|
}
|