module.exports = grammar({
name: 'css',
extras: $ => [
/\s/,
$.comment,
$.js_comment,
],
externals: $ => [
$._descendant_operator,
$._pseudo_class_selector_colon,
$.__error_recovery,
],
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,
$.at_rule,
),
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,
),
postcss_statement: $ => prec(-1, seq(
$.at_keyword,
repeat($._value),
';',
)),
at_rule: $ => seq(
$.at_keyword,
sep(',', $._query),
choice(';', $.block),
),
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,
$.postcss_statement,
$.at_rule,
),
_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,
$.namespace_selector,
),
nesting_selector: _ => '&',
universal_selector: _ => '*',
class_selector: $ => prec(1, seq(
optional($._selector),
'.',
$.class_name,
)),
pseudo_class_selector: $ => seq(
optional($._selector),
alias($._pseudo_class_selector_colon, ':'),
choice(
seq(
alias(
choice('has', 'not', 'is', 'where', 'host', 'host-context'),
$.class_name,
),
alias($.pseudo_class_with_selector_arguments, $.arguments),
),
$._nth_child_pseudo_class_selector,
seq(
$.class_name,
optional(alias($.pseudo_class_arguments, $.arguments)),
),
),
),
_nth_child_pseudo_class_selector: $ => seq(
alias(
choice('nth-child', 'nth-last-child'),
$.class_name,
),
alias($.pseudo_class_nth_child_arguments, $.arguments),
),
pseudo_element_selector: $ => seq(
optional($._selector),
'::',
alias($.identifier, $.tag_name),
optional(alias($.pseudo_element_arguments, $.arguments)),
),
id_selector: $ => seq(
optional($._selector),
'#',
alias($.identifier, $.id_name),
),
attribute_selector: $ => seq(
optional($._selector),
token(prec(1, '[')),
alias(choice($.identifier, $.namespace_selector), $.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)),
namespace_selector: $ => prec.left(seq($._selector, '|', $._selector)),
pseudo_class_arguments: $ => seq(
token.immediate('('),
sep(',', choice($._selector, repeat1($._value))),
')',
),
pseudo_class_with_selector_arguments: $ => seq(
token.immediate('('),
sep(',', $._selector),
')',
),
pseudo_class_nth_child_arguments: $ => prec(-1, seq(
token.immediate('('),
choice(
alias('even', $.plain_value),
alias('odd', $.plain_value),
$.integer_value,
alias($._nth_functional_notation, $.plain_value),
),
optional(
seq(
'of',
$._selector,
),
),
')',
)),
_nth_functional_notation: _ => /-?(\d)*n\s*(\+\s*\d+)?/,
pseudo_element_arguments: $ => seq(
token.immediate('('),
sep(',', choice($._selector, repeat1($._value))),
')',
),
declaration: $ => seq(
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',
_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,
')',
),
_value: $ => prec(-1, choice(
alias($.identifier, $.plain_value),
$.plain_value,
$.color_value,
$.integer_value,
$.float_value,
$.string_value,
$.grid_value,
$.binary_expression,
$.parenthesized_value,
$.call_expression,
$.important,
)),
parenthesized_value: $ => seq(
'(',
$._value,
')',
),
color_value: _ => seq('#', token.immediate(/[0-9a-fA-F]{3,8}/)),
string_value: $ => choice(
seq(
'\'',
repeat(choice(
alias(/[^\\'\n]+/, $.string_content),
$.escape_sequence,
)),
'\'',
),
seq(
'"',
repeat(choice(
alias(/[^\\"\n]+/, $.string_content),
$.escape_sequence,
)),
'"',
),
),
escape_sequence: _ => token(seq(
'\\',
choice(
/[0-9a-fA-F]{1,6}\s?/,
/[^0-9a-fA-F\n\r]/,
),
)),
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%]+/),
grid_value: $ => seq(
'[',
sep1(',', $._value),
']',
),
call_expression: $ => seq(
alias($.identifier, $.function_name),
$.arguments,
),
binary_expression: $ => prec.left(seq(
$._value,
choice('+', '-', '*', '/'),
$._value,
)),
arguments: $ => seq(
token.immediate('('),
sep(choice(',', ';'), repeat1($._value)),
')',
),
class_name: $ => repeat1(choice(
$.identifier,
$.escape_sequence,
)),
identifier: _ => /(--|-?[a-zA-Z_\xA0-\xFF])[a-zA-Z0-9-_\xA0-\xFF]*/,
at_keyword: _ => /@[a-zA-Z-_]+/,
js_comment: _ => token(prec(-1, seq('//', /.*/))),
comment: _ => token(seq(
'/*',
/[^*]*\*+([^/*][^*]*\*+)*/,
'/',
)),
plain_value: _ => token(seq(
repeat(choice(
/[-_]/,
/\/[^\*\s,;!{}()\[\]]/, )),
/[a-zA-Z]/,
repeat(choice(
/[^/\s,;!{}()\[\]]/, /\/[^\*\s,;!{}()\[\]]/, )),
)),
},
});
function sep(separator, rule) {
return optional(sep1(separator, rule));
}
function sep1(separator, rule) {
return seq(rule, repeat(seq(separator, rule)));
}