#groovy #gradle #jenkins

dekobon-tree-sitter-groovy

Apache Groovy grammar for tree-sitter (dekobon fork)

3 releases

Uses new Rust 2024

0.2.2 Jun 4, 2026
0.2.1 May 25, 2026
0.2.0 May 19, 2026
0.1.0 May 19, 2026

#722 in Parser implementations

Download history 102/week @ 2026-05-19 135/week @ 2026-05-26 137/week @ 2026-06-02 367/week @ 2026-06-09 319/week @ 2026-06-16 417/week @ 2026-06-23 367/week @ 2026-06-30 491/week @ 2026-07-07 614/week @ 2026-07-14

1,927 downloads per month
Used in 3 crates

Apache-2.0 OR MIT

5.5MB
152K SLoC

C 150K SLoC // 0.0% comments JavaScript 1K SLoC // 0.3% comments Groovy 290 SLoC // 0.0% comments Scheme 272 SLoC // 0.3% comments Rust 108 SLoC

tree-sitter-groovy

CI CodeQL npm crates.io PyPI

Tree-sitter grammar for Apache Groovy — the JVM scripting language used in Jenkins pipelines, Gradle build files, and Spock test specifications.

Published as: @dekobon/tree-sitter-groovy on npm, dekobon-tree-sitter-groovy on crates.io and PyPI, github.com/dekobon/tree-sitter-groovy as a Go module / Swift package. The unscoped name belongs to the upstream comparison grammar linked below; the C library (no naming conflict) still ships as libtree-sitter-groovy.

This grammar is purpose-built for Groovy (it does not extend the tree-sitter-java grammar). It synthesises the best ideas from the two existing community grammars while closing every operator and statement gap they left open:

  • amaanq/tree-sitter-groovy — clean precedence table and shebang handling, but mis-shapes Groovy-specific syntax (Elvis ?:, safe navigation ?., spread operators, regex =~ / ==~, traits, exclusive ranges, identity === / !==, spaceship <=>, etc.) as Java-shaped parse trees.
  • murtaza64/tree-sitter-groovy — purpose-built for Groovy, broad operator coverage and slashy strings, but has open bugs around empty block comments, multi-catch, multi-assignment, enums, switch-arrow, and a pipeline-at-EOF constraint.

The full design is in SPECIFICATION.md.

Status

Fully functional Groovy 2.x--4.x parser with complete operator coverage, GString interpolation, generics, closures, command chains, and six language bindings. See Known limitations for deferred items.

Operator coverage

Every Groovy operator emits a distinct named node so downstream tooling can identify each by node kind alone — no MISSING-child inspection, no tree-shape heuristics. This is the contract that closes dekobon/big-code-analysis#246 (Elvis short-circuit counting) and dekobon/big-code-analysis#247 (all Groovy-specific operator gaps).

Category Operators
Arithmetic + - * / % ** ++ --
Relational < <= > >= == != <=> (spaceship)
Identity === !==
Regex =~ (find) ==~ (match)
Logical && || ! ==> (implication)
Bitwise & | ^ ~ << >> >>>
Conditional ? : (ternary) ?: (Elvis)
Assignment = += -= *= /= %= **= <<= >>= >>>= &= ^= |= ?= (Elvis assign)
Range .. ..< <.. <..<
Member access . ?. ??. (safe chain dot) *. (spread-dot) .& (method pointer) .@ (direct field) :: (method reference)
Subscript [] ?[] (safe index)
Spread *args *: (spread-map)
Membership in !in instanceof !instanceof
Coercion as

Editor support

Tree-sitter queries (queries/groovy/highlights.scm, folds.scm, indents.scm, injections.scm) are provided for editor integration. Slashy and dollar-slashy string bodies inject as regex for nested regex highlighting.

Recognized file extensions: .groovy, .gradle, .gvy, .gy, and Jenkinsfile.

Filetype detection

The repo ships ftdetect/groovy.lua and ftplugin/groovy.lua for Neovim. Neovim's built-in filetype detection already maps *.groovy, *.gradle, and the bare filename Jenkinsfile to filetype groovy; the shipped ftdetect/groovy.lua fills the gaps by mapping:

  • *.gvy and *.gy — declared in tree-sitter.json but not in Neovim's defaults.
  • *.jenkinsfile — uncommon (the canonical name is Jenkinsfile) but declared in tree-sitter.json and used by some teams as an editor-detection hook.
  • Jenkinsfile.* variants (Jenkinsfile.ci, Jenkinsfile.release, …) — common in repos with multiple pipelines, not handled by Neovim's built-in detection.

The existing ftplugin/groovy.lua then calls vim.treesitter.start(). Highlighting only renders once the parser binary and queries/groovy/highlights.scm are registered with nvim-treesitter (or equivalent) — without that, the start() call is a no-op and the buffer falls back to non-treesitter highlighting.

For files that don't match any of those (e.g. a hand-named build-pipeline with no extension), opt in with one of:

  • Modeline at the top of the file: // vim: set filetype=groovy :

  • Per-project autocmd in .nvim.lua (sourced after :cd into the project via Neovim's 'exrc'). Note vim.fn.getcwd() is evaluated when the autocmd is defined, so this snippet belongs in a per-project config, not a global init.lua:

    vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
      pattern = vim.fn.getcwd() .. "/jenkins/*",
      callback = function() vim.bo.filetype = "groovy" end,
    })
    

Other editors follow the same pattern: rely on built-in detection for the common extensions and override per-directory or via a header comment for the rest.

Building

npm install
make build
make test

Bindings

Available bindings: C, Go, Node.js, Python, Rust, Swift. See bindings/ for binding-specific READMEs and tests.

For Rust consumers:

[dependencies]
dekobon-tree-sitter-groovy = "0.1"
tree-sitter = "0.25"
let mut parser = tree_sitter::Parser::new();
let language = dekobon_tree_sitter_groovy::LANGUAGE;
parser.set_language(&language.into()).expect("Error loading Groovy parser");
let tree = parser.parse(source, None).unwrap();

Known limitations

These are documented in detail in docs/divergences-from-spec.md.

  • Groovy 5 contextual keywords (val, async, await, defer) are not yet exposed as keyword tokens. They parse as plain identifiers, matching Groovy 2.x--4.x compiler behavior.
  • Typed local declarations require an initializer -- String x is ambiguous with String(x) (method call). Use String x = ... or def x instead.
  • Leading-operator line continuation -- a\n+ b greedily joins as a + b rather than splitting into two statements. Idiomatic Groovy places continuation operators at the end of the previous line.

Contributing

See CONTRIBUTING.md for how to build, test, and submit changes. AGENTS.md covers project conventions (commit messages, versioning, validation gates) and SPECIFICATION.md is the authoritative grammar design document.

Acknowledgements

We are grateful for the work done in the murtaza64/tree-sitter-groovy and amaanq/tree-sitter-groovy. These projects served as inspiration for some of the approaches in this project

License

Dual-licensed under Apache License 2.0 or MIT.

Dependencies