npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codincod/codemirror-lang-groovy

v0.1.0

Published

Groovy language support for the CodeMirror code editor

Readme

@codincod/codemirror-lang-groovy NPM version

[ CHANGELOG ]

This package implements Groovy language support for the CodeMirror code editor, using a Lezer grammar written for this package.

CodeMirror 6 has never shipped Groovy, and no Lezer grammar for it existed before this one. What gets used instead is the stream mode ported from CodeMirror 5, which colours a list of keywords and builds no tree, so nothing above it can fold a closure, indent a switch or ask what the word under the cursor is. It also has nothing to say about the parts of Groovy that are not Java: a build file's parenthesis-free calls, a slashy regular expression, a ${} inside a string.

Written in part for CodinCod, a competitive coding platform, where it colours the editor people solve puzzles in.

This code is released under an MIT license.

Usage

import {EditorView, basicSetup} from "codemirror"
import {groovy} from "@codincod/codemirror-lang-groovy"

const view = new EditorView({
  parent: document.body,
  doc: `class Greeter {
    def name

    String greet() {
        "Hello, $name!"
    }
}
`,
  extensions: [basicSetup, groovy()]
})

Coverage

Groovy 4, and with it the Java that Groovy is a superset of. Classes, interfaces, traits, enums, records and annotation types, generics with wildcards and bounds, closures and lambdas, def in every position it is allowed, multiple assignment, command expressions with named arguments and trailing closures, the Elvis, spread, safe navigation, attribute and method pointer operators, ranges, ==~ and =~, Spock's string method names and labels, switch as a statement and as an expression, and every way Groovy writes a string: single and triple quoted, GStrings with $name and ${} holes, slashy, and dollar slashy.

Measured against 20 662 Groovy and Gradle files from six open source projects (Apache Groovy, Gradle, Spock, Grails, Ratpack and Geb), which is 84 MiB of code, of which 98.48% parse with no error node.

What the grammar cannot say

Five things about Groovy are settled in src/tokens.ts rather than in the grammar, because no context free rule can decide them.

A newline ends a statement, except when it does not. Groovy's semicolons are optional, so a\nb is two statements and a +\nb is one. The tokenizer emits a zero width insertSemi when a line break stands between two things that could each begin a statement, and holds it back when the next line opens with something that can only continue the one before: a dot in any of Groovy's five spellings, a comma, a closing bracket, &&, ||, ?:, an arrow, or one of extends, implements and throws, which a wrapped signature puts at the head of the next line.

A brace opens a block or a closure. { is the same character either way, and only the arrow a closure's parameters end with tells them apart. Where a statement may begin the grammar settles it with a cut, because a block is the only thing wanted there. Everywhere else the tokenizer looks ahead for the arrow, giving up at the first character a parameter list cannot hold, so that { foo(a -> b) } is not mistaken for a closure that takes foo.

A parenthesis opens a call or a lambda. a.b() and (a, b) -> a + b are the same shape until the arrow after the closing bracket, and a call is written far too often for the parser to be left weighing the two. The tokenizer counts the depth along the line and emits LambdaParen only when the bracket that closes the group is followed by an arrow.

A < is a comparison or a type argument list. a < b and Map<K, V> are the same two characters, and Groovy makes it harder than Java does: a command expression means the parser has a comparison available everywhere a type could stand. The tokenizer reads the whole angle bracketed group, then reads the name after it, and decides on what follows that name. A name being declared is followed by its value, by another name, by its parameters, by the colon or in of a for-each, or by the end of the line; a name being compared is followed by an operator.

A slash opens a string or divides. Groovy's slashy string needs no escaping and is what regular expressions are written in, and it starts on the same character as division. The tokenizer looks at what stands in front: after a name, a number, a string or a closing bracket there is a value to divide, and everywhere else there is not. Groovy's own lexer reads the character this way, so len / (w + 1) divides and m ==~ /\d+/ matches, here as in the language.

Development

npm install
npm test          # build, then the parse specs and the editor tests
npm run build     # regenerate src/parser.js and dist/

The corpus scripts want lezer-survey checked out beside this package, and a corpus of real Groovy to point it at.