mirror of
https://github.com/ivuorinen/tree-sitter-shellspec.git
synced 2026-01-26 03:34:03 +00:00
- Convert GitHub Actions from local to inline actions for better maintainability - Add comprehensive caching for npm dependencies, tree-sitter CLI, and build artifacts - Fix checkout steps missing in test matrix jobs - Apply defensive programming in test coverage validation - Use local tree-sitter CLI via npx instead of global installation - Update tree-sitter-cli to v0.25.0 for compatibility with tree-sitter-bash - Add proper tree-sitter field to package.json with grammar metadata - Fix grammar precedence for Data blocks (#| lines now have higher precedence) - Standardize dates in memory files to September 12, 2025 - Enhance workflow robustness with dynamic workflow ID resolution - Improve test file pattern matching and error handling This commit addresses all CodeRabbit review suggestions and optimizes GitHub Actions workflows for better performance and reliability.
196 lines
5.6 KiB
JavaScript
196 lines
5.6 KiB
JavaScript
/**
|
|
* @file ShellSpec grammar for tree-sitter (extends bash)
|
|
* @author Ismo Vuorinen <ismo@ivuorinen.net>
|
|
* @license MIT
|
|
*/
|
|
|
|
/// <reference types="tree-sitter-cli/dsl" />
|
|
// @ts-check
|
|
|
|
const bashGrammar = require("tree-sitter-bash/grammar");
|
|
|
|
module.exports = grammar(bashGrammar, {
|
|
name: "shellspec",
|
|
|
|
// Add conflicts to handle ambiguity between commands and ShellSpec constructs
|
|
conflicts: ($, previous) =>
|
|
previous.concat([
|
|
// Essential bash conflicts only
|
|
[$._expression, $.command_name],
|
|
[$.command, $.variable_assignments],
|
|
[$.redirected_statement, $.command],
|
|
[$.redirected_statement, $.command_substitution],
|
|
[$.function_definition, $.command_name],
|
|
[$.pipeline],
|
|
// Required ShellSpec conflicts
|
|
[$.command_name, $.shellspec_data_block],
|
|
[$.shellspec_hook_block],
|
|
]),
|
|
|
|
rules: {
|
|
// Extend the main statement rule to include ShellSpec blocks and directives
|
|
_statement_not_subshell: ($, original) =>
|
|
choice(
|
|
original,
|
|
$.shellspec_describe_block,
|
|
$.shellspec_context_block,
|
|
$.shellspec_it_block,
|
|
$.shellspec_hook_block,
|
|
$.shellspec_utility_block,
|
|
$.shellspec_data_block,
|
|
$.shellspec_hook_statement,
|
|
$.shellspec_directive_statement,
|
|
),
|
|
|
|
// ShellSpec Describe blocks
|
|
shellspec_describe_block: ($) =>
|
|
prec.right(
|
|
1,
|
|
seq(
|
|
choice("Describe", "fDescribe", "xDescribe"),
|
|
field("description", choice($.string, $.raw_string, $.word)),
|
|
repeat($._terminated_statement),
|
|
"End",
|
|
),
|
|
),
|
|
|
|
// ShellSpec Context/ExampleGroup blocks
|
|
shellspec_context_block: ($) =>
|
|
prec.right(
|
|
1,
|
|
seq(
|
|
choice("Context", "ExampleGroup", "fContext", "xContext"),
|
|
field("description", choice($.string, $.raw_string, $.word)),
|
|
repeat($._terminated_statement),
|
|
"End",
|
|
),
|
|
),
|
|
|
|
// ShellSpec It/Example/Specify blocks
|
|
shellspec_it_block: ($) =>
|
|
prec.right(
|
|
1,
|
|
seq(
|
|
choice(
|
|
"It",
|
|
"Example",
|
|
"Specify",
|
|
"fIt",
|
|
"fExample",
|
|
"fSpecify",
|
|
"xIt",
|
|
"xExample",
|
|
"xSpecify",
|
|
),
|
|
field("description", choice($.string, $.raw_string, $.word)),
|
|
repeat($._terminated_statement),
|
|
"End",
|
|
),
|
|
),
|
|
|
|
// ShellSpec hooks as blocks (with End)
|
|
shellspec_hook_block: ($) =>
|
|
prec.right(
|
|
1,
|
|
seq(
|
|
choice(
|
|
"BeforeEach",
|
|
"AfterEach",
|
|
"BeforeAll",
|
|
"AfterAll",
|
|
"BeforeCall",
|
|
"AfterCall",
|
|
"BeforeRun",
|
|
"AfterRun",
|
|
),
|
|
field("label", optional(choice($.string, $.raw_string, $.word))),
|
|
repeat($._terminated_statement),
|
|
"End",
|
|
),
|
|
),
|
|
|
|
// ShellSpec utility blocks (Parameters, Skip, Pending, Todo - Data has its own rule)
|
|
shellspec_utility_block: ($) =>
|
|
prec.right(
|
|
1,
|
|
seq(
|
|
choice("Parameters", "Skip", "Pending", "Todo"),
|
|
field("label", optional(choice($.string, $.raw_string, $.word))),
|
|
repeat($._terminated_statement),
|
|
"End",
|
|
),
|
|
),
|
|
|
|
// ShellSpec Data blocks with advanced syntax support
|
|
shellspec_data_block: ($) =>
|
|
choice(
|
|
// Block style with #| lines - highest precedence to ensure #| lines are parsed correctly
|
|
prec.right(
|
|
5,
|
|
seq(
|
|
"Data",
|
|
optional(seq(":", field("modifier", choice("raw", "expand")))),
|
|
optional(
|
|
field(
|
|
"filter",
|
|
seq("|", repeat1(choice($.word, $.string, $.raw_string))),
|
|
),
|
|
),
|
|
repeat1(seq("#|", field("data_line", /[^\n]*/))),
|
|
"End",
|
|
),
|
|
),
|
|
// Block style with regular statements
|
|
prec.right(
|
|
4,
|
|
seq(
|
|
"Data",
|
|
optional(seq(":", field("modifier", choice("raw", "expand")))),
|
|
optional(field("label", choice($.string, $.raw_string, $.word))),
|
|
field("statements", repeat($._terminated_statement)),
|
|
"End",
|
|
),
|
|
),
|
|
// String or function argument style (no End) - lowest precedence
|
|
seq(
|
|
"Data",
|
|
optional(seq(":", field("modifier", choice("raw", "expand")))),
|
|
field("argument", choice($.string, $.raw_string, $.word)),
|
|
optional(
|
|
field(
|
|
"filter",
|
|
seq("|", repeat1(choice($.word, $.string, $.raw_string))),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// ShellSpec hooks as statements (Before/After without End)
|
|
shellspec_hook_statement: ($) =>
|
|
prec.right(
|
|
2,
|
|
seq(
|
|
choice("Before", "After"),
|
|
repeat1(field("argument", choice($.string, $.raw_string, $.word))),
|
|
),
|
|
),
|
|
|
|
// ShellSpec directives (Include, Skip with conditions)
|
|
shellspec_directive_statement: ($) =>
|
|
prec.right(
|
|
2,
|
|
choice(
|
|
// Include directive
|
|
seq("Include", field("path", choice($.string, $.raw_string, $.word))),
|
|
// Skip with conditions (only conditional skip, simple skip handled by utility_block)
|
|
seq(
|
|
"Skip",
|
|
"if",
|
|
field("reason", choice($.string, $.raw_string, $.word)),
|
|
field("condition", repeat1(choice($.word, $.string, $.raw_string))),
|
|
),
|
|
),
|
|
),
|
|
},
|
|
});
|