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-hy

v0.1.0

Published

Hy language support for the CodeMirror code editor

Readme

@codincod/codemirror-lang-hy NPM version

[ CHANGELOG ]

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

Hy had no grammar for CodeMirror anywhere: nothing on npm under any spelling of the name, and the one tree-sitter grammar that exists says of itself that it is alpha and very incomplete. A .hy file was plain text, or it was handed to a Lisp grammar, which is worse in a way that is easy to miss.

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 {hy} from "@codincod/codemirror-lang-hy"

const view = new EditorView({
  parent: document.body,
  doc: `(defn greet [name]\n  (print f"hello, {name}"))`,
  extensions: [basicSetup, hy()]
})

Why not a Lisp grammar

Hy parses clean under one. Everything made of parentheses does, which is why the number is worth nothing here: over 14942 files of published Hy the Lisp grammar in this family reads 97.85% of them without a single error node, and colours barely half of what it read.

| Hy writes | A Lisp grammar sees | It is | | --- | --- | --- | | f"hello, {name}" | the symbol f, then a string | an f-string, and name inside it is code | | #(1 2) | a vector | a tuple | | #{1 2} | a vector | a set | | #* args | a symbol #* | Python's *args | | #^ int x | plain symbols | a type annotation on x | | #[delim[ ... ]delim] | a reader error | a raw string carrying its own delimiter | | 1_000, 1,000 | symbols | one million, twice | | .append, self.name | plain symbols | attribute access, the whole of Python interop | | True, False, None | plain symbols | Python's constants | | defn, setv, lfor | plain symbols | the forms the language is built from |

Measured over the same files, this grammar puts 13 colours on the page where the Lisp grammar puts 10, and leaves 31.12% of the coloured text as an uncoloured name where the Lisp grammar leaves 41.44%. The definitions are the sharpest of it: 3.69% of the text against 0.14%, because a Lisp grammar knows defn names something only by accident. Attribute access, Python's constants and everything inside an f-string it does not see at all.

It goes the other way too. Hy has no #| block comment, no #\ character literal, no dotted pair, no vector reader, and one spelling of quote where Common Lisp has a family of them.

What the reader decides

Every rule in the grammar comes from hy/reader/hy_reader.py in the compiler rather than from the manual, because the reader is where the surprises are.

An identifier ends at whitespace or at one of ()[]{};"'`` ``~, and at nothing else. So a,b, x:y, @deco, foo#barand1abc` are each one symbol: characters other languages punctuate with are ordinary letters here, and a name may begin with a digit.

That rule is what makes #* a prefix that needs a space after it. A # reads the whole identifier that follows and looks it up as a reader macro, so #* args unpacks and #*args is the undefined reader macro *args. The same goes for #**, #^ and #_. This grammar reads #*args as unpacking anyway, since that is what it meant in every version of Hy before 1.0 and what it still means to whoever wrote it.

A number is a name the reader changed its mind about. It reads the identifier first and then asks int, float and complex, in that order, whether it was a number; a token all of them refuse stays a name. 1_000 and 1,000 are both one million, Inf and NaN are floats while inf and nan are names, and 1abc is a symbol.

An f-string component holds exactly one form, and the reader takes that form before it looks for !, = or :. Since each is an identifier character, f"{x:>5}" is the single symbol x:>5 with no format spec at all, and the space in f"{x :>5}" is the whole difference. A component may also hold a string of its own: f"{(get d "k")}" is one f-string, and the quote in the middle of it closes nothing.

Measured on

17787 .hy files, every version of every one of them, out of all 532 repositories GitHub files under Hy. Hy's own reader was run over the same files as an oracle and turns 2845 of them down, and of the 14942 that are valid Hy this grammar reads 100.00% with no error node. The raw share, junk included, is 96.71%.

It is deliberately looser than the reader in the other direction, and reads 2260 of the files Hy rejects: #*args and #^int from before Hy 1.0, reader macros the module imported rather than defined, and half-typed forms, which is what an editor is looking at most of the time.

npm run measure runs the grammar over a directory of real Hy, and npm run gaps groups whatever it could not parse by shape. npm run harvest builds that directory out of every public repository GitHub files under Hy, taking every version of every .hy file and asking Hy's own reader which of them are syntactically Hy: tools/read.py is that question, and a file it turns down is not a file the grammar failed. It needs hy importable by the python on the path, and it reads without evaluating, so none of the code it is given is ever run.