difftastic/vendored_parsers/tree-sitter-scss/grammar.js

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)));
}