Merge commit '02b4ee757654b7d54fe35352fd8e53a8a4385d42'

pull/708/head
Wilfred Hughes 2024-04-28 17:32:23 +07:00
commit 688aaa83f8
29 changed files with 10970 additions and 5920 deletions

@ -0,0 +1,20 @@
module.exports = {
'env': {
'commonjs': true,
'es2021': true,
},
'extends': 'google',
'overrides': [
],
'parserOptions': {
'ecmaVersion': 'latest',
'sourceType': 'module',
},
'rules': {
'indent': ['error', 2, {'SwitchCase': 1}],
'max-len': [
'error',
{'code': 120, 'ignoreComments': true, 'ignoreUrls': true, 'ignoreStrings': true},
],
},
};

@ -1,2 +1,10 @@
/src/** linguist-vendored /src/** linguist-vendored
/examples/* linguist-vendored /examples/* linguist-vendored
src/grammar.json linguist-generated
src/node-types.json linguist-generated
src/parser.c linguist-generated
src/grammar.json -diff
src/node-types.json -diff
src/parser.c -diff

@ -0,0 +1,60 @@
name: Bug Report
description: File a bug or issue
title: "bug: "
labels: [bug]
body:
- type: markdown
attributes:
value: |
**Before** reporting an issue, make sure to search [existing issues](https://github.com/tree-sitter/tree-sitter-css/issues). Usage questions such as ***"How do I...?"*** either belong in [Discussions](https://github.com/tree-sitter/tree-sitter/discussions) upstream or in our [Discord server](https://discord.gg/w7nTvsVJhm) and will be closed.
If your issue is related to a bug in your editor-experience because your editor *leverages* tree-sitter and this parser, then it is likely your issue does *NOT* belong here and belongs in the relevant editor's repository.
- type: checkboxes
attributes:
label: Did you check existing issues?
description: Make sure you've checked all of the below before submitting an issue
options:
- label: I have read all the [tree-sitter docs](https://tree-sitter.github.io/tree-sitter/using-parsers) if it relates to using the parser
required: false
- label: I have searched the existing issues of tree-sitter-css
required: true
- type: input
attributes:
label: "Tree-Sitter CLI Version, if relevant (output of `tree-sitter --version`)"
placeholder: "tree-sitter 0.20.8 (6bbb50bef8249e6460e7d69e42cc8146622fa4fd)"
validations:
required: false
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. Please include any related errors you see such as parsing errors or tree-sitter cli errors.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce/Bad Parse Tree
description: Steps to reproduce the behavior. If you have a bad parse tree, please include it here. You can get this by running `tree-sitter parse <path-to-file>` and copying the output.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior/Parse Tree
description: A concise description of what you expected to happen, or in the case of a bad parse tree, the expected parse tree.
validations:
required: true
- type: textarea
attributes:
label: Repro
description: Minimal code to reproduce this issue. Ideally this should be reproducible with the C library or the tree-sitter cli, do not suggest an editor or external tool.
value: |
/* Example code that fails to parse */
.foo {
color: red;
/* Code that fails to parse, or causes an error */
}
render: CSS
validations:
required: false

@ -0,0 +1,36 @@
name: Feature Request
description: Suggest a new feature
title: "feature: "
labels: [enhancement]
body:
- type: checkboxes
attributes:
label: Did you check the tree-sitter docs?
description: Make sure you read all the docs before submitting a feature request
options:
- label: I have read all the [tree-sitter docs](https://tree-sitter.github.io/tree-sitter/using-parsers) if it relates to using the parser
required: false
- type: textarea
validations:
required: true
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I think the grammar models this rule incorrectly and can be improved, or the scanner can be improved by doing [...], or CSS has officially added a new feature that should be added to the grammar.
- type: textarea
validations:
required: true
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
- type: textarea
validations:
required: true
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
validations:
required: false
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here. If your feature request is related to a new CSS feature, please include a link to the relevant **official** CSS documentation.

@ -0,0 +1,32 @@
name: CI
on:
pull_request:
branches:
- "**"
push:
branches:
- "master"
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm install
- run: npm test
test_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm install
- run: npm run-script test-windows

@ -0,0 +1,22 @@
name: Fuzz Parser
on:
push:
paths:
- src/scanner.c
pull_request:
paths:
- src/scanner.c
workflow_dispatch:
jobs:
test:
name: Parser fuzzing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: vigoux/tree-sitter-fuzz-action@v1
with:
language: css
external-scanner: src/scanner.c
time: 60

@ -0,0 +1,19 @@
name: Lint
on:
push:
branches:
- master
pull_request:
branches:
- "**"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install modules
run: npm install
- name: Run ESLint
run: npm run lint

@ -0,0 +1,103 @@
name: Release
on:
workflow_run:
workflows: ["CI"]
branches:
- master
types:
- completed
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get previous commit SHA
id: get_previous_commit
run: |
LATEST_TAG=$(git describe --tags --abbrev=0)
if [[ -z "$LATEST_TAG" ]]; then
echo "No tag found. Failing..."
exit 1
fi
echo "latest_tag=${LATEST_TAG#v}" >> "$GITHUB_ENV" # Remove 'v' prefix from the tag
- name: Check if version changed and is greater than the previous
id: version_check
run: |
# Compare the current version with the version from the previous commit
PREVIOUS_NPM_VERSION=${{ env.latest_tag }}
CURRENT_NPM_VERSION=$(jq -r '.version' package.json)
CURRENT_CARGO_VERSION=$(awk -F '"' '/^version/ {print $2}' Cargo.toml)
if [[ "$CURRENT_NPM_VERSION" != "$CURRENT_CARGO_VERSION" ]]; then # Cargo.toml and package.json versions must match
echo "Mismatch: NPM version ($CURRENT_NPM_VERSION) and Cargo.toml version ($CURRENT_CARGO_VERSION)"
echo "version_changed=false" >> "$GITHUB_ENV"
else
if [[ "$PREVIOUS_NPM_VERSION" == "$CURRENT_NPM_VERSION" ]]; then
echo "version_changed=" >> "$GITHUB_ENV"
else
IFS='.' read -ra PREVIOUS_VERSION_PARTS <<< "$PREVIOUS_NPM_VERSION"
IFS='.' read -ra CURRENT_VERSION_PARTS <<< "$CURRENT_NPM_VERSION"
VERSION_CHANGED=false
for i in "${!PREVIOUS_VERSION_PARTS[@]}"; do
if [[ ${CURRENT_VERSION_PARTS[i]} -gt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
VERSION_CHANGED=true
break
elif [[ ${CURRENT_VERSION_PARTS[i]} -lt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
break
fi
done
echo "version_changed=$VERSION_CHANGED" >> "$GITHUB_ENV"
echo "current_version=${CURRENT_NPM_VERSION}" >> "$GITHUB_ENV"
fi
fi
- name: Display result
run: |
echo "Version bump detected: ${{ env.version_changed }}"
- name: Fail if version is lower
if: env.version_changed == 'false'
run: exit 1
- name: Setup Node
if: env.version_changed == 'true'
uses: actions/setup-node@v4
with:
node-version: 18
registry-url: "https://registry.npmjs.org"
- name: Publish to NPM
if: env.version_changed == 'true'
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
run: npm publish
- name: Setup Rust
if: env.version_changed == 'true'
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: Publish to Crates.io
if: env.version_changed == 'true'
uses: katyo/publish-crates@v2
with:
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
- name: Tag versions
if: env.version_changed == 'true'
run: |
git checkout master
git config user.name github-actions[bot]
git config user.email github-actions[bot]@users.noreply.github.com
git tag -d "v${{ env.current_version }}" || true
git push origin --delete "v${{ env.current_version }}" || true
git tag -a "v${{ env.current_version }}" -m "Version ${{ env.current_version }}"
git push origin "v${{ env.current_version }}"

@ -1,6 +1,6 @@
Cargo.lock
node_modules node_modules
build build
target
*.log
package-lock.json package-lock.json
Cargo.lock /target/
.build/

@ -1,3 +1,5 @@
build /test
target /examples
Cargo.lock /build
/script
/target

@ -1,27 +1,27 @@
[package] [package]
name = "tree-sitter-css" name = "tree-sitter-css"
description = "css grammar for the tree-sitter parsing library" description = "CSS grammar for tree-sitter"
version = "0.19.0" version = "0.20.0"
authors = [
"Max Brunsfeld <maxbrunsfeld@gmail.com>",
"Amaan Qureshi <amaanq12@gmail.com>",
]
license = "MIT"
readme = "bindings/rust/README.md"
keywords = ["incremental", "parsing", "css"] keywords = ["incremental", "parsing", "css"]
categories = ["parsing", "text-editors"] categories = ["parsing", "text-editors"]
repository = "https://github.com/tree-sitter/tree-sitter-javascript" repository = "https://github.com/tree-sitter/tree-sitter-css"
edition = "2018" edition = "2021"
license = "MIT" autoexamples = false
authors = ["Max Brunsfeld <maxbrunsfeld@gmail.com>"]
build = "bindings/rust/build.rs" build = "bindings/rust/build.rs"
include = [ include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"]
"bindings/rust/*",
"grammar.js",
"queries/*",
"src/*",
]
[lib] [lib]
path = "bindings/rust/lib.rs" path = "bindings/rust/lib.rs"
[dependencies] [dependencies]
tree-sitter = ">= 0.19" tree-sitter = "~0.20.10"
[build-dependencies] [build-dependencies]
cc = "1.0" cc = "~1.0"

@ -0,0 +1,33 @@
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "TreeSitterCSS",
products: [
.library(name: "TreeSitterCSS", targets: ["TreeSitterCSS"]),
],
dependencies: [],
targets: [
.target(name: "TreeSitterCSS",
path: ".",
exclude: [
"binding.gyp",
"bindings",
"Cargo.toml",
"corpus",
"grammar.js",
"LICENSE",
"package.json",
"README.md",
],
sources: [
"src/parser.c",
"src/scanner.c",
],
resources: [
.copy("queries")
],
publicHeadersPath: "bindings/swift",
cSettings: [.headerSearchPath("src")])
]
)

@ -1,11 +1,19 @@
tree-sitter-css # tree-sitter-css
===============
[![Build Status](https://travis-ci.org/tree-sitter/tree-sitter-css.svg?branch=master)](https://travis-ci.org/tree-sitter/tree-sitter-css) [![CI][ci]](https://github.com/tree-sitter/tree-sitter-css/actions/workflows/ci.yml)
[![Build status](https://ci.appveyor.com/api/projects/status/smdphgf4ns9jglw5/branch/master?svg=true)](https://ci.appveyor.com/project/maxbrunsfeld/tree-sitter-css/branch/master) [![discord][discord]](https://discord.gg/w7nTvsVJhm)
[![matrix][matrix]](https://matrix.to/#/#tree-sitter-chat:matrix.org)
[![crates][crates]](https://crates.io/crates/tree-sitter-css)
[![npm][npm]](https://www.npmjs.com/package/tree-sitter-css)
CSS grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter). CSS grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter).
References References
* [CSS Syntax Guide](https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax) - [CSS Syntax Guide](https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax)
[ci]: https://img.shields.io/github/actions/workflow/status/tree-sitter/tree-sitter-css/ci.yml?logo=github&label=CI
[discord]: https://img.shields.io/discord/1063097320771698699?logo=discord&label=discord
[matrix]: https://img.shields.io/matrix/tree-sitter-chat%3Amatrix.org?logo=matrix&label=matrix
[npm]: https://img.shields.io/npm/v/tree-sitter-css?logo=npm
[crates]: https://img.shields.io/crates/v/tree-sitter-css?logo=rust

@ -0,0 +1,36 @@
# tree-sitter-css
This crate provides a CSS grammar for the [tree-sitter][] parsing library.
To use this crate, add it to the `[dependencies]` section of your `Cargo.toml`
file. (Note that you will probably also need to depend on the
[`tree-sitter`][tree-sitter crate] crate to use the parsed result in any useful
way.)
```toml
[dependencies]
tree-sitter = "~0.20.10"
tree-sitter-css = "0.20.0"
```
Typically, you will use the [language][language func] function to add this
grammar to a tree-sitter [Parser][], and then use the parser to parse some code:
```rust
let code = r#"
.foo {
color: red;
}
"#;
let mut parser = Parser::new();
parser.set_language(tree_sitter_css::language()).expect("Error loading CSS grammar");
let parsed = parser.parse(code, None);
```
If you have any questions, please reach out to us in the [tree-sitter
discussions] page.
[language func]: https://docs.rs/tree-sitter-css/*/tree_sitter_css/fn.language.html
[Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
[tree-sitter]: https://tree-sitter.github.io/
[tree-sitter crate]: https://crates.io/crates/tree-sitter
[tree-sitter discussions]: https://github.com/tree-sitter/tree-sitter/discussions

@ -1,16 +1,19 @@
fn main() { fn main() {
let src_dir = std::path::Path::new("src"); let src_dir = std::path::Path::new("src");
let mut c_config = cc::Build::new(); let mut c_config = cc::Build::new();
c_config.include(&src_dir); c_config.include(src_dir);
c_config c_config
.flag_if_supported("-Wno-unused-parameter") .flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable") .flag_if_supported("-Wno-unused-but-set-variable")
.flag_if_supported("-Wno-trigraphs"); .flag_if_supported("-Wno-trigraphs");
let parser_path = src_dir.join("parser.c"); let parser_path = src_dir.join("parser.c");
let scanner_path = src_dir.join("scanner.c");
c_config.file(&parser_path); c_config.file(&parser_path);
let scanner_path = src_dir.join("scanner.c");
c_config.file(&scanner_path); c_config.file(&scanner_path);
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
c_config.compile("parser"); c_config.compile("parser");
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
} }

@ -6,7 +6,7 @@
//! ``` //! ```
//! let code = ""; //! let code = "";
//! let mut parser = tree_sitter::Parser::new(); //! let mut parser = tree_sitter::Parser::new();
//! parser.set_language(tree_sitter_css::language()).expect("Error loading css grammar"); //! parser.set_language(tree_sitter_css::language()).expect("Error loading CSS grammar");
//! let tree = parser.parse(code, None).unwrap(); //! let tree = parser.parse(code, None).unwrap();
//! ``` //! ```
//! //!
@ -31,9 +31,9 @@ pub fn language() -> Language {
/// The content of the [`node-types.json`][] file for this grammar. /// The content of the [`node-types.json`][] file for this grammar.
/// ///
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm");
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -42,6 +42,6 @@ mod tests {
let mut parser = tree_sitter::Parser::new(); let mut parser = tree_sitter::Parser::new();
parser parser
.set_language(super::language()) .set_language(super::language())
.expect("Error loading css language"); .expect("Error loading CSS grammar");
} }
} }

@ -0,0 +1,16 @@
#ifndef TREE_SITTER_CSS_H_
#define TREE_SITTER_CSS_H_
typedef struct TSLanguage TSLanguage;
#ifdef __cplusplus
extern "C" {
#endif
extern TSLanguage *tree_sitter_css();
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_CSS_H_

@ -1,17 +1,29 @@
/**
* @file CSS grammar for tree-sitter
* @author Max Brunsfeld <maxbrunsfeld@gmail.com>
* @author Amaan Qureshi <amaanq12@gmail.com>
* @license MIT
*/
/* eslint-disable arrow-parens */
/* eslint-disable camelcase */
/* eslint-disable-next-line spaced-comment */
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
module.exports = grammar({ module.exports = grammar({
name: 'css', name: 'css',
extras: $ => [ extras: $ => [
/\s/, /\s/,
$.comment, $.comment,
$.js_comment,
], ],
externals: $ => [ externals: $ => [
$._descendant_operator, $._descendant_operator,
], $._pseudo_class_selector_colon,
$.__error_recovery,
conflicts: $ => [
[$._selector, $.declaration],
], ],
inline: $ => [ inline: $ => [
@ -31,7 +43,7 @@ module.exports = grammar({
$.namespace_statement, $.namespace_statement,
$.keyframes_statement, $.keyframes_statement,
$.supports_statement, $.supports_statement,
$.at_rule $.at_rule,
), ),
// Statements // Statements
@ -40,32 +52,32 @@ module.exports = grammar({
'@import', '@import',
$._value, $._value,
sep(',', $._query), sep(',', $._query),
';' ';',
), ),
media_statement: $ => seq( media_statement: $ => seq(
'@media', '@media',
sep1(',', $._query), sep1(',', $._query),
$.block $.block,
), ),
charset_statement: $ => seq( charset_statement: $ => seq(
'@charset', '@charset',
$._value, $._value,
';' ';',
), ),
namespace_statement: $ => seq( namespace_statement: $ => seq(
'@namespace', '@namespace',
optional(alias($.identifier, $.namespace_name)), optional(alias($.identifier, $.namespace_name)),
choice($.string_value, $.call_expression), choice($.string_value, $.call_expression),
';' ';',
), ),
keyframes_statement: $ => seq( keyframes_statement: $ => seq(
choice( choice(
'@keyframes', '@keyframes',
alias(/@[-a-z]+keyframes/, $.at_keyword) alias(/@[-a-z]+keyframes/, $.at_keyword),
), ),
alias($.identifier, $.keyframes_name), alias($.identifier, $.keyframes_name),
$.keyframe_block_list, $.keyframe_block_list,
@ -74,42 +86,49 @@ module.exports = grammar({
keyframe_block_list: $ => seq( keyframe_block_list: $ => seq(
'{', '{',
repeat($.keyframe_block), repeat($.keyframe_block),
'}' '}',
), ),
keyframe_block: $ => seq( keyframe_block: $ => seq(
choice($.from, $.to, $.integer_value), choice($.from, $.to, $.integer_value),
$.block $.block,
), ),
from: $ => 'from', from: _ => 'from',
to: $ => 'to', to: _ => 'to',
supports_statement: $ => seq( supports_statement: $ => seq(
'@supports', '@supports',
$._query, $._query,
$.block $.block,
), ),
postcss_statement: $ => prec(-1, seq(
$.at_keyword,
repeat($._value),
';',
)),
at_rule: $ => seq( at_rule: $ => seq(
$.at_keyword, $.at_keyword,
sep(',', $._query), sep(',', $._query),
choice(';', $.block) choice(';', $.block),
), ),
// Rule sets // Rule sets
rule_set: $ => seq( rule_set: $ => seq(
$.selectors, $.selectors,
$.block $.block,
), ),
selectors: $ => sep1(',', $._selector), selectors: $ => sep1(',', $._selector),
block: $ => seq('{', block: $ => seq(
'{',
repeat($._block_item), repeat($._block_item),
optional(alias($.last_declaration, $.declaration)), optional(alias($.last_declaration, $.declaration)),
'}' '}',
), ),
_block_item: $ => choice( _block_item: $ => choice(
@ -121,7 +140,8 @@ module.exports = grammar({
$.namespace_statement, $.namespace_statement,
$.keyframes_statement, $.keyframes_statement,
$.supports_statement, $.supports_statement,
$.at_rule $.postcss_statement,
$.at_rule,
), ),
// Selectors // Selectors
@ -139,12 +159,13 @@ module.exports = grammar({
$.child_selector, $.child_selector,
$.descendant_selector, $.descendant_selector,
$.sibling_selector, $.sibling_selector,
$.adjacent_sibling_selector $.adjacent_sibling_selector,
$.namespace_selector,
), ),
nesting_selector: $ => '&', nesting_selector: _ => '&',
universal_selector: $ => '*', universal_selector: _ => '*',
class_selector: $ => prec(1, seq( class_selector: $ => prec(1, seq(
optional($._selector), optional($._selector),
@ -154,33 +175,33 @@ module.exports = grammar({
pseudo_class_selector: $ => seq( pseudo_class_selector: $ => seq(
optional($._selector), optional($._selector),
':', alias($._pseudo_class_selector_colon, ':'),
alias($.identifier, $.class_name), alias($.identifier, $.class_name),
optional(alias($.pseudo_class_arguments, $.arguments)) optional(alias($.pseudo_class_arguments, $.arguments)),
), ),
pseudo_element_selector: $ => seq( pseudo_element_selector: $ => seq(
optional($._selector), optional($._selector),
'::', '::',
alias($.identifier, $.tag_name), alias($.identifier, $.tag_name),
optional(alias($.pseudo_element_arguments, $.arguments)) optional(alias($.pseudo_element_arguments, $.arguments)),
), ),
id_selector: $ => seq( id_selector: $ => seq(
optional($._selector), optional($._selector),
'#', '#',
alias($.identifier, $.id_name) alias($.identifier, $.id_name),
), ),
attribute_selector: $ => seq( attribute_selector: $ => seq(
optional($._selector), optional($._selector),
'[', '[',
alias($.identifier, $.attribute_name), alias(choice($.identifier, $.namespace_selector), $.attribute_name),
optional(seq( optional(seq(
choice('=', '~=', '^=', '|=', '*=', '$='), choice('=', '~=', '^=', '|=', '*=', '$='),
$._value $._value,
)), )),
']' ']',
), ),
child_selector: $ => prec.left(seq($._selector, '>', $._selector)), child_selector: $ => prec.left(seq($._selector, '>', $._selector)),
@ -191,16 +212,18 @@ module.exports = grammar({
adjacent_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( pseudo_class_arguments: $ => seq(
token.immediate('('), token.immediate('('),
sep(',', choice($._selector, repeat1($._value))), sep(',', choice($._selector, repeat1($._value))),
')' ')',
), ),
pseudo_element_arguments: $ => seq( pseudo_element_arguments: $ => seq(
token.immediate('('), token.immediate('('),
sep(',', choice($._selector, repeat1($._value))), sep(',', choice($._selector, repeat1($._value))),
')' ')',
), ),
// Declarations // Declarations
@ -211,10 +234,10 @@ module.exports = grammar({
$._value, $._value,
repeat(seq( repeat(seq(
optional(','), optional(','),
$._value $._value,
)), )),
optional($.important), optional($.important),
';' ';',
), ),
last_declaration: $ => prec(1, seq( last_declaration: $ => prec(1, seq(
@ -223,12 +246,12 @@ module.exports = grammar({
$._value, $._value,
repeat(seq( repeat(seq(
optional(','), optional(','),
$._value $._value,
)), )),
optional($.important) optional($.important),
)), )),
important: $ => '!important', important: _ => '!important',
// Media queries // Media queries
@ -238,7 +261,7 @@ module.exports = grammar({
$.binary_query, $.binary_query,
$.unary_query, $.unary_query,
$.selector_query, $.selector_query,
$.parenthesized_query $.parenthesized_query,
), ),
feature_query: $ => seq( feature_query: $ => seq(
@ -246,31 +269,31 @@ module.exports = grammar({
alias($.identifier, $.feature_name), alias($.identifier, $.feature_name),
':', ':',
repeat1($._value), repeat1($._value),
')' ')',
), ),
parenthesized_query: $ => seq( parenthesized_query: $ => seq(
'(', '(',
$._query, $._query,
')' ')',
), ),
binary_query: $ => prec.left(seq( binary_query: $ => prec.left(seq(
$._query, $._query,
choice('and', 'or'), choice('and', 'or'),
$._query $._query,
)), )),
unary_query: $ => prec(1, seq( unary_query: $ => prec(1, seq(
choice('not', 'only'), choice('not', 'only'),
$._query $._query,
)), )),
selector_query: $ => seq( selector_query: $ => seq(
'selector', 'selector',
'(', '(',
$._selector, $._selector,
')' ')',
), ),
// Property Values // Property Values
@ -282,30 +305,32 @@ module.exports = grammar({
$.integer_value, $.integer_value,
$.float_value, $.float_value,
$.string_value, $.string_value,
$.grid_value,
$.binary_expression, $.binary_expression,
$.parenthesized_value, $.parenthesized_value,
$.call_expression $.call_expression,
$.important,
)), )),
parenthesized_value: $ => seq( parenthesized_value: $ => seq(
'(', '(',
$._value, $._value,
')' ')',
), ),
color_value: $ => seq('#', token.immediate(/[0-9a-fA-F]{3,8}/)), color_value: _ => seq('#', token.immediate(/[0-9a-fA-F]{3,8}/)),
string_value: $ => token(choice( string_value: _ => choice(
seq("'", /([^'\n]|\\(.|\n))*/, "'"), seq('\'', /([^'\n]|\\(.|\n))*/, '\''),
seq('"', /([^"\n]|\\(.|\n))*/, '"') seq('"', /([^"\n]|\\(.|\n))*/, '"'),
)), ),
integer_value: $ => seq( integer_value: $ => seq(
token(seq( token(seq(
optional(choice('+', '-')), optional(choice('+', '-')),
/\d+/ /\d+/,
)), )),
optional($.unit) optional($.unit),
), ),
float_value: $ => seq( float_value: $ => seq(
@ -315,59 +340,87 @@ module.exports = grammar({
choice( choice(
seq('.', /\d+/), seq('.', /\d+/),
seq(/[eE]/, optional('-'), /\d+/), seq(/[eE]/, optional('-'), /\d+/),
seq('.', /\d+/, /[eE]/, optional('-'), /\d+/) seq('.', /\d+/, /[eE]/, optional('-'), /\d+/),
) ),
)), )),
optional($.unit) optional($.unit),
), ),
unit: $ => token.immediate(/[a-zA-Z%]+/), unit: _ => token.immediate(/[a-zA-Z%]+/),
grid_value: $ => seq(
'[',
sep1(',', $._value),
']',
),
call_expression: $ => seq( call_expression: $ => seq(
alias($.identifier, $.function_name), alias($.identifier, $.function_name),
$.arguments $.arguments,
), ),
binary_expression: $ => prec.left(seq( binary_expression: $ => prec.left(seq(
$._value, $._value,
choice('+', '-', '*', '/'), choice('+', '-', '*', '/'),
$._value $._value,
)), )),
arguments: $ => seq( arguments: $ => seq(
token.immediate('('), token.immediate('('),
sep(choice(',', ';'), repeat1($._value)), sep(choice(',', ';'), repeat1($._value)),
')' ')',
), ),
identifier: $ => /(--|-?[a-zA-Z_])[a-zA-Z0-9-_]*/, identifier: _ => /(--|-?[a-zA-Z_])[a-zA-Z0-9-_]*/,
at_keyword: _ => /@[a-zA-Z-_]+/,
at_keyword: $ => /@[a-zA-Z-_]+/, js_comment: _ => token(prec(-1, seq('//', /.*/))),
comment: $ => token(seq( comment: _ => token(seq(
'/*', '/*',
/[^*]*\*+([^/*][^*]*\*+)*/, /[^*]*\*+([^/*][^*]*\*+)*/,
'/' '/',
)), )),
plain_value: $ => token(seq( plain_value: _ => token(seq(
repeat(choice( repeat(choice(
/[-_]/, /[-_]/,
/\/[^\*\s,;!{}()\[\]]/ // Slash not followed by a '*' (which would be a comment) /\/[^\*\s,;!{}()\[\]]/, // Slash not followed by a '*' (which would be a comment)
)), )),
/[a-zA-Z]/, /[a-zA-Z]/,
repeat(choice( repeat(choice(
/[^/\s,;!{}()\[\]]/, // Not a slash, not a delimiter character /[^/\s,;!{}()\[\]]/, // Not a slash, not a delimiter character
/\/[^\*\s,;!{}()\[\]]/ // Slash not followed by a '*' (which would be a comment) /\/[^\*\s,;!{}()\[\]]/, // Slash not followed by a '*' (which would be a comment)
)) )),
)) )),
} },
}) });
function sep (separator, rule) { /**
return optional(sep1(separator, rule)) * Creates a rule to optionally match one or more of the rules separated by `separator`
*
* @param {RuleOrLiteral} separator
*
* @param {RuleOrLiteral} rule
*
* @return {ChoiceRule}
*
*/
function sep(separator, rule) {
return optional(sep1(separator, rule));
} }
function sep1 (separator, rule) { /**
return seq(rule, repeat(seq(separator, rule))) * Creates a rule to match one or more of the rules separated by `separator`
*
* @param {RuleOrLiteral} separator
*
* @param {RuleOrLiteral} rule
*
* @return {SeqRule}
*
*/
function sep1(separator, rule) {
return seq(rule, repeat(seq(separator, rule)));
} }

@ -5,7 +5,8 @@
"main": "bindings/node", "main": "bindings/node",
"keywords": [ "keywords": [
"parser", "parser",
"lexer" "lexer",
"css"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
@ -14,12 +15,17 @@
"author": "Max Brunsfeld", "author": "Max Brunsfeld",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"nan": "^2.14.1" "nan": "^2.18.0"
}, },
"devDependencies": { "devDependencies": {
"tree-sitter-cli": "^0.20.0" "eslint": ">=5.16.0",
"eslint-config-google": "^0.14.0",
"tree-sitter-cli": "^0.20.8"
}, },
"scripts": { "scripts": {
"build": "tree-sitter generate && node-gyp build",
"lint": "eslint grammar.js",
"parse": "tree-sitter parse",
"test": "tree-sitter test && tree-sitter parse examples/*.css --quiet --time", "test": "tree-sitter test && tree-sitter parse examples/*.css --quiet --time",
"test-windows": "tree-sitter test" "test-windows": "tree-sitter test"
}, },
@ -29,7 +35,10 @@
"file-types": [ "file-types": [
"css" "css"
], ],
"injection-regex": "^css$" "injection-regex": "^css$",
"highlights": [
"queries/highlights.scm"
]
} }
] ]
} }

@ -304,6 +304,30 @@
} }
] ]
}, },
"postcss_statement": {
"type": "PREC",
"value": -1,
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "at_keyword"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "_value"
}
},
{
"type": "STRING",
"value": ";"
}
]
}
},
"at_rule": { "at_rule": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@ -469,6 +493,10 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "supports_statement" "name": "supports_statement"
}, },
{
"type": "SYMBOL",
"name": "postcss_statement"
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "at_rule" "name": "at_rule"
@ -534,6 +562,10 @@
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "adjacent_sibling_selector" "name": "adjacent_sibling_selector"
},
{
"type": "SYMBOL",
"name": "namespace_selector"
} }
] ]
}, },
@ -595,7 +627,12 @@
] ]
}, },
{ {
"type": "STRING", "type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "_pseudo_class_selector_colon"
},
"named": false,
"value": ":" "value": ":"
}, },
{ {
@ -725,8 +762,17 @@
{ {
"type": "ALIAS", "type": "ALIAS",
"content": { "content": {
"type": "SYMBOL", "type": "CHOICE",
"name": "identifier" "members": [
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "namespace_selector"
}
]
}, },
"named": true, "named": true,
"value": "attribute_name" "value": "attribute_name"
@ -867,6 +913,27 @@
] ]
} }
}, },
"namespace_selector": {
"type": "PREC_LEFT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_selector"
},
{
"type": "STRING",
"value": "|"
},
{
"type": "SYMBOL",
"name": "_selector"
}
]
}
},
"pseudo_class_arguments": { "pseudo_class_arguments": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@ -1337,6 +1404,10 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "string_value" "name": "string_value"
}, },
{
"type": "SYMBOL",
"name": "grid_value"
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "binary_expression" "name": "binary_expression"
@ -1348,6 +1419,10 @@
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "call_expression" "name": "call_expression"
},
{
"type": "SYMBOL",
"name": "important"
} }
] ]
} }
@ -1386,46 +1461,43 @@
] ]
}, },
"string_value": { "string_value": {
"type": "TOKEN", "type": "CHOICE",
"content": { "members": [
"type": "CHOICE", {
"members": [ "type": "SEQ",
{ "members": [
"type": "SEQ", {
"members": [ "type": "STRING",
{ "value": "'"
"type": "STRING", },
"value": "'" {
}, "type": "PATTERN",
{ "value": "([^'\\n]|\\\\(.|\\n))*"
"type": "PATTERN", },
"value": "([^'\\n]|\\\\(.|\\n))*" {
}, "type": "STRING",
{ "value": "'"
"type": "STRING", }
"value": "'" ]
} },
] {
}, "type": "SEQ",
{ "members": [
"type": "SEQ", {
"members": [ "type": "STRING",
{ "value": "\""
"type": "STRING", },
"value": "\"" {
}, "type": "PATTERN",
{ "value": "([^\"\\n]|\\\\(.|\\n))*"
"type": "PATTERN", },
"value": "([^\"\\n]|\\\\(.|\\n))*" {
}, "type": "STRING",
{ "value": "\""
"type": "STRING", }
"value": "\"" ]
} }
] ]
}
]
}
}, },
"integer_value": { "integer_value": {
"type": "SEQ", "type": "SEQ",
@ -1610,6 +1682,44 @@
"value": "[a-zA-Z%]+" "value": "[a-zA-Z%]+"
} }
}, },
"grid_value": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "["
},
{
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_value"
},
{
"type": "REPEAT",
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": ","
},
{
"type": "SYMBOL",
"name": "_value"
}
]
}
}
]
},
{
"type": "STRING",
"value": "]"
}
]
},
"call_expression": { "call_expression": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@ -1738,6 +1848,26 @@
"type": "PATTERN", "type": "PATTERN",
"value": "@[a-zA-Z-_]+" "value": "@[a-zA-Z-_]+"
}, },
"js_comment": {
"type": "TOKEN",
"content": {
"type": "PREC",
"value": -1,
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "//"
},
{
"type": "PATTERN",
"value": ".*"
}
]
}
}
},
"comment": { "comment": {
"type": "TOKEN", "type": "TOKEN",
"content": { "content": {
@ -1811,19 +1941,26 @@
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "comment" "name": "comment"
},
{
"type": "SYMBOL",
"name": "js_comment"
} }
], ],
"conflicts": [ "conflicts": [],
[
"_selector",
"declaration"
]
],
"precedences": [], "precedences": [],
"externals": [ "externals": [
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "_descendant_operator" "name": "_descendant_operator"
},
{
"type": "SYMBOL",
"name": "_pseudo_class_selector_colon"
},
{
"type": "SYMBOL",
"name": "__error_recovery"
} }
], ],
"inline": [ "inline": [

@ -31,6 +31,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -106,14 +110,26 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{ {
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "important",
"named": true
},
{ {
"type": "integer_value", "type": "integer_value",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -196,6 +212,73 @@
] ]
} }
}, },
{
"type": "attribute_name",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "adjacent_sibling_selector",
"named": true
},
{
"type": "attribute_selector",
"named": true
},
{
"type": "child_selector",
"named": true
},
{
"type": "class_selector",
"named": true
},
{
"type": "descendant_selector",
"named": true
},
{
"type": "id_selector",
"named": true
},
{
"type": "namespace_selector",
"named": true
},
{
"type": "nesting_selector",
"named": true
},
{
"type": "pseudo_class_selector",
"named": true
},
{
"type": "pseudo_element_selector",
"named": true
},
{
"type": "sibling_selector",
"named": true
},
{
"type": "string_value",
"named": true
},
{
"type": "tag_name",
"named": true
},
{
"type": "universal_selector",
"named": true
}
]
}
},
{ {
"type": "attribute_selector", "type": "attribute_selector",
"named": true, "named": true,
@ -244,14 +327,26 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{ {
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "important",
"named": true
},
{ {
"type": "integer_value", "type": "integer_value",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -315,6 +410,14 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{
"type": "important",
"named": true
},
{ {
"type": "integer_value", "type": "integer_value",
"named": true "named": true
@ -405,6 +508,10 @@
"type": "namespace_statement", "type": "namespace_statement",
"named": true "named": true
}, },
{
"type": "postcss_statement",
"named": true
},
{ {
"type": "rule_set", "type": "rule_set",
"named": true "named": true
@ -459,6 +566,14 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{
"type": "important",
"named": true
},
{ {
"type": "integer_value", "type": "integer_value",
"named": true "named": true
@ -510,6 +625,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -577,6 +696,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -637,6 +760,10 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{ {
"type": "important", "type": "important",
"named": true "named": true
@ -696,6 +823,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -755,6 +886,14 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{
"type": "important",
"named": true
},
{ {
"type": "integer_value", "type": "integer_value",
"named": true "named": true
@ -789,6 +928,57 @@
] ]
} }
}, },
{
"type": "grid_value",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "binary_expression",
"named": true
},
{
"type": "call_expression",
"named": true
},
{
"type": "color_value",
"named": true
},
{
"type": "float_value",
"named": true
},
{
"type": "grid_value",
"named": true
},
{
"type": "important",
"named": true
},
{
"type": "integer_value",
"named": true
},
{
"type": "parenthesized_value",
"named": true
},
{
"type": "plain_value",
"named": true
},
{
"type": "string_value",
"named": true
}
]
}
},
{ {
"type": "id_selector", "type": "id_selector",
"named": true, "named": true,
@ -825,6 +1015,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -888,6 +1082,14 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{
"type": "important",
"named": true
},
{ {
"type": "integer_value", "type": "integer_value",
"named": true "named": true
@ -1042,6 +1244,73 @@
] ]
} }
}, },
{
"type": "namespace_selector",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "adjacent_sibling_selector",
"named": true
},
{
"type": "attribute_selector",
"named": true
},
{
"type": "child_selector",
"named": true
},
{
"type": "class_selector",
"named": true
},
{
"type": "descendant_selector",
"named": true
},
{
"type": "id_selector",
"named": true
},
{
"type": "namespace_selector",
"named": true
},
{
"type": "nesting_selector",
"named": true
},
{
"type": "pseudo_class_selector",
"named": true
},
{
"type": "pseudo_element_selector",
"named": true
},
{
"type": "sibling_selector",
"named": true
},
{
"type": "string_value",
"named": true
},
{
"type": "tag_name",
"named": true
},
{
"type": "universal_selector",
"named": true
}
]
}
},
{ {
"type": "namespace_statement", "type": "namespace_statement",
"named": true, "named": true,
@ -1124,6 +1393,69 @@
"type": "float_value", "type": "float_value",
"named": true "named": true
}, },
{
"type": "grid_value",
"named": true
},
{
"type": "important",
"named": true
},
{
"type": "integer_value",
"named": true
},
{
"type": "parenthesized_value",
"named": true
},
{
"type": "plain_value",
"named": true
},
{
"type": "string_value",
"named": true
}
]
}
},
{
"type": "postcss_statement",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "at_keyword",
"named": true
},
{
"type": "binary_expression",
"named": true
},
{
"type": "call_expression",
"named": true
},
{
"type": "color_value",
"named": true
},
{
"type": "float_value",
"named": true
},
{
"type": "grid_value",
"named": true
},
{
"type": "important",
"named": true
},
{ {
"type": "integer_value", "type": "integer_value",
"named": true "named": true
@ -1183,6 +1515,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -1250,6 +1586,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -1332,6 +1672,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -1395,6 +1739,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -1458,6 +1806,10 @@
"type": "id_selector", "type": "id_selector",
"named": true "named": true
}, },
{
"type": "namespace_selector",
"named": true
},
{ {
"type": "nesting_selector", "type": "nesting_selector",
"named": true "named": true
@ -1489,6 +1841,11 @@
] ]
} }
}, },
{
"type": "string_value",
"named": true,
"fields": {}
},
{ {
"type": "stylesheet", "type": "stylesheet",
"named": true, "named": true,
@ -1615,6 +1972,10 @@
"named": true, "named": true,
"fields": {} "fields": {}
}, },
{
"type": "\"",
"named": false
},
{ {
"type": "#", "type": "#",
"named": false "named": false
@ -1623,6 +1984,10 @@
"type": "$=", "type": "$=",
"named": false "named": false
}, },
{
"type": "'",
"named": false
},
{ {
"type": "(", "type": "(",
"named": false "named": false
@ -1723,10 +2088,6 @@
"type": "at_keyword", "type": "at_keyword",
"named": true "named": true
}, },
{
"type": "attribute_name",
"named": true
},
{ {
"type": "class_name", "type": "class_name",
"named": true "named": true
@ -1755,6 +2116,10 @@
"type": "important", "type": "important",
"named": true "named": true
}, },
{
"type": "js_comment",
"named": true
},
{ {
"type": "keyframes_name", "type": "keyframes_name",
"named": true "named": true
@ -1795,10 +2160,6 @@
"type": "selector", "type": "selector",
"named": false "named": false
}, },
{
"type": "string_value",
"named": true
},
{ {
"type": "tag_name", "type": "tag_name",
"named": true "named": true
@ -1815,6 +2176,10 @@
"type": "{", "type": "{",
"named": false "named": false
}, },
{
"type": "|",
"named": false
},
{ {
"type": "|=", "type": "|=",
"named": false "named": false

File diff suppressed because it is too large Load Diff

@ -1,53 +1,84 @@
#include <tree_sitter/parser.h> #include "tree_sitter/parser.h"
#include <wctype.h> #include <wctype.h>
enum TokenType { enum TokenType {
DESCENDANT_OP, DESCENDANT_OP,
PSEUDO_CLASS_SELECTOR_COLON,
ERROR_RECOVERY,
}; };
static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
void *tree_sitter_css_external_scanner_create() { return NULL; } void *tree_sitter_css_external_scanner_create() { return NULL; }
void tree_sitter_css_external_scanner_destroy(void *p) {}
void tree_sitter_css_external_scanner_reset(void *p) {}
unsigned tree_sitter_css_external_scanner_serialize(void *p, char *buffer) { return 0; }
void tree_sitter_css_external_scanner_deserialize(void *p, const char *b, unsigned n) {}
bool tree_sitter_css_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) { void tree_sitter_css_external_scanner_destroy(void *payload) {}
if (iswspace(lexer->lookahead) && valid_symbols[DESCENDANT_OP]) {
lexer->result_symbol = DESCENDANT_OP;
lexer->advance(lexer, true); void tree_sitter_css_external_scanner_reset(void *payload) {}
while (iswspace(lexer->lookahead)) {
lexer->advance(lexer, true); unsigned tree_sitter_css_external_scanner_serialize(void *payload, char *buffer) { return 0; }
void tree_sitter_css_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) {}
bool tree_sitter_css_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
if (valid_symbols[ERROR_RECOVERY]) {
return false;
} }
lexer->mark_end(lexer);
if (iswspace(lexer->lookahead) && valid_symbols[DESCENDANT_OP]) {
if ( lexer->result_symbol = DESCENDANT_OP;
lexer->lookahead == '#' ||
lexer->lookahead == '.' || lexer->advance(lexer, true);
lexer->lookahead == '[' || while (iswspace(lexer->lookahead)) {
lexer->lookahead == '-' || lexer->advance(lexer, true);
lexer->lookahead == '*' || }
iswalnum(lexer->lookahead) lexer->mark_end(lexer);
) {
return true; if (lexer->lookahead == '#' || lexer->lookahead == '.' || lexer->lookahead == '[' || lexer->lookahead == '-' ||
lexer->lookahead == '*' || iswalnum(lexer->lookahead)) {
return true;
}
if (lexer->lookahead == ':') {
lexer->advance(lexer, false);
if (iswspace(lexer->lookahead)) {
return false;
}
for (;;) {
if (lexer->lookahead == ';' || lexer->lookahead == '}' || lexer->eof(lexer)) {
return false;
}
if (lexer->lookahead == '{') {
return true;
}
lexer->advance(lexer, false);
}
}
} }
if (lexer->lookahead == ':') { if (valid_symbols[PSEUDO_CLASS_SELECTOR_COLON]) {
lexer->advance(lexer, false); while (iswspace(lexer->lookahead)) {
if (iswspace(lexer->lookahead)) return false; lexer->advance(lexer, true);
for (;;) { }
if ( if (lexer->lookahead == ':') {
lexer->lookahead == ';' || advance(lexer);
lexer->lookahead == '}' || if (lexer->lookahead == ':') {
lexer->eof(lexer) return false;
) return false; }
if (lexer->lookahead == '{') { lexer->mark_end(lexer);
return true; // We need a { to be a pseudo class selector, a ; indicates a property
while (lexer->lookahead != ';' && lexer->lookahead != '}' && !lexer->eof(lexer)) {
advance(lexer);
if (lexer->lookahead == '{') {
lexer->result_symbol = PSEUDO_CLASS_SELECTOR_COLON;
return true;
}
}
return false;
} }
lexer->advance(lexer, false);
}
} }
}
return false; return false;
} }

@ -13,9 +13,8 @@ extern "C" {
#define ts_builtin_sym_end 0 #define ts_builtin_sym_end 0
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
typedef uint16_t TSStateId;
#ifndef TREE_SITTER_API_H_ #ifndef TREE_SITTER_API_H_
typedef uint16_t TSStateId;
typedef uint16_t TSSymbol; typedef uint16_t TSSymbol;
typedef uint16_t TSFieldId; typedef uint16_t TSFieldId;
typedef struct TSLanguage TSLanguage; typedef struct TSLanguage TSLanguage;
@ -140,7 +139,8 @@ struct TSLanguage {
lexer->advance(lexer, skip); \ lexer->advance(lexer, skip); \
start: \ start: \
skip = false; \ skip = false; \
lookahead = lexer->lookahead; lookahead = lexer->lookahead; \
eof = lexer->eof(lexer);
#define ADVANCE(state_value) \ #define ADVANCE(state_value) \
{ \ { \
@ -166,7 +166,7 @@ struct TSLanguage {
* Parse Table Macros * Parse Table Macros
*/ */
#define SMALL_STATE(id) id - LARGE_STATE_COUNT #define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT)
#define STATE(id) id #define STATE(id) id
@ -176,7 +176,7 @@ struct TSLanguage {
{{ \ {{ \
.shift = { \ .shift = { \
.type = TSParseActionTypeShift, \ .type = TSParseActionTypeShift, \
.state = state_value \ .state = (state_value) \
} \ } \
}} }}
@ -184,7 +184,7 @@ struct TSLanguage {
{{ \ {{ \
.shift = { \ .shift = { \
.type = TSParseActionTypeShift, \ .type = TSParseActionTypeShift, \
.state = state_value, \ .state = (state_value), \
.repetition = true \ .repetition = true \
} \ } \
}} }}

@ -209,6 +209,7 @@ a {
Comments right after numbers Comments right after numbers
======================================= =======================================
// A comment
a { a {
shape-outside: circle(20em/*=*/at 50% 50%); shape-outside: circle(20em/*=*/at 50% 50%);
shape-outside: inset(1em, 1em, 1em, 1em); shape-outside: inset(1em, 1em, 1em, 1em);
@ -217,6 +218,7 @@ a {
--- ---
(stylesheet (stylesheet
(js_comment)
(rule_set (rule_set
(selectors (tag_name)) (selectors (tag_name))
(block (block
@ -267,3 +269,65 @@ div {
(declaration (declaration
(property_name) (property_name)
(integer_value))))) (integer_value)))))
=============================================
No spaces after colons
=============================================
div {
all:unset;
display:flex;
justify-content:center;
}
---
(stylesheet
(rule_set
(selectors (tag_name))
(block
(declaration (property_name) (plain_value))
(declaration (property_name) (plain_value))
(declaration (property_name) (plain_value)))))
=============================================
PostCSS
=============================================
.selector {
@apply variable-a meta-variable-b lots-of-combined-properties-c !important;
}
@layer components {
.btn-blue {
@apply --mixin sm:space-x-0 left-[11%] border-foreground/20 !important;
}
}
---
(stylesheet
(rule_set
(selectors (class_selector (class_name)))
(block
(postcss_statement
(at_keyword)
(plain_value)
(plain_value)
(plain_value)
(important))))
(at_rule
(at_keyword)
(keyword_query)
(block
(rule_set
(selectors (class_selector (class_name)))
(block
(postcss_statement
(at_keyword)
(plain_value)
(plain_value)
(plain_value)
(grid_value (integer_value (unit)))
(plain_value)
(important)))))))

@ -70,6 +70,9 @@ Media statements
@media (min-height: 680px), screen and (orientation: portrait) {} @media (min-height: 680px), screen and (orientation: portrait) {}
@media not all and (monochrome) {} @media not all and (monochrome) {}
@media only screen {} @media only screen {}
@media screen and (min-width: 0 0) {
.tooltipped-multiline: after {}
}
--- ---
@ -88,7 +91,15 @@ Media statements
(media_statement (media_statement
(binary_query (unary_query (keyword_query)) (parenthesized_query (keyword_query))) (binary_query (unary_query (keyword_query)) (parenthesized_query (keyword_query)))
(block)) (block))
(media_statement (unary_query (keyword_query)) (block))) (media_statement (unary_query (keyword_query)) (block))
(media_statement
(binary_query
(keyword_query)
(feature_query (feature_name) (integer_value) (integer_value)))
(block
(rule_set
(selectors (pseudo_class_selector (class_selector (class_name)) (class_name)))
(block)))))
============================== ==============================
Supports statements Supports statements