To add am_list
support for a new language, you mostly need to know the usage
patterns of autometrics in this language, and understand how to make and use
tree-sitter queries.
The existing implementations can also be used as a stencil to add a new language. They all follow the same pattern:
- a
src/{lang}.rs
which implements walking down a directory and collecting theExpectedAmLabel
labels on the files it traverses. This is the location where thecrate::ListAmFunctions
trait is implemented. - a
src/{lang}/queries.rs
which is a wrapper around a tree-sitter query, using the rust bindings. - a
runtime/queries/{lang}
folder which contains the actual queries being passed to tree-sitter:- the
.scm
files are direct queries, ready to be used "as-is", - the
.scm.tpl
files are format strings, which are meant to be templated at runtime according to other detection happening in the file. For example, when autometrics is a wrapper function that can be renamed in a file (import { autometrics as am } from '@autometrics/autometrics'
in typescript), we need to change the exact query to detect wrapper usage in the file.
- the
The hardest part about supporting a language is to build the queries for it. The best resources to create those are on the main website of tree-sitter library:
- the help section on Query syntax helps understand how to create queries, and
- the playground allows to paste some text and see both the syntax tree and the current highlight from a query. This enables creating queries incrementally.
If you're using Neovim, it also has a very good playground plugin you can use.
The goal is to test the query on the common usage patterns of autometrics, and make sure that you can match the function names. There are non-trivial limitations about what a tree-sitter query can and cannot match, so if you're blocked, don't hesitate to create a discussion or an issue to ask a question, and we will try to solve the problem together.
Once you have you query, it's easier to wrap it in a Rust structure to keep track of the indices of the "captures" created within the query. It is best to look at how the queries are used in the existing implementations and try to replicate this, seeing how the bindings are used. In order of complexity, you should look at:
- the Go implementation, which has a very simple query.
- the Typescript implementation, which uses the result of a first query to dynamically create a second one, and collect the results of everything.
- the Rust implementation, which uses dynamically created queries, but also uses a recursion pattern to keep track of modules declared in-file.
The complete release cycle is handled and automated by the combination of cargo-dist
and cargo-release
.
When ready for a release, using cargo release --(patch|minor)
or cargo release NEW_MAJOR
should be enough.
Only cargo-release
is needed to create a new release:
$ cargo install --locked cargo-release
Also, you need to have the authorization to both:
- create and push tags on
release/*
branches in the Github repo - create and push crates on
crates-io
registry foram_list
crate
- Create a branch
release/0.Y
from main - Run
cargo release 0.Y.0
and make sure everything is okay
If everything went ok
- Run the same command, but with
--execute
flag
If something went wrong
- Nothing got actually published, so you can reset the
release/0.Y
branch tomain
, - Then make and commit the changes on your
release/0.Y
branch (that is still local), - Then try the
cargo release 0.Y.0
again.
In the example, we will push a new 0.2.3
version on 0.2
- Create a release branch
rel_0.2.3
with all the changes that need to be included- new features are cherry-picked from
main
- bugfixes happen directly on the release branch (and later get cherry-picked to
main
if relevant)
- new features are cherry-picked from
- Push the new branch
rel_0.2.3
and create a PR torelease/0.2
- Prepare the release:
cargo release --no-publish --no-tag --allow-branch=rel_0.2.3 patch
- Cleanup the
CHANGELOG.md
that got bad replacement patterns because of the 2-step process - Push the new commits, and merge the PR
- Switch locally to
release/0.2
(the "main" release branch), and pull the latest changes - Publish the tag and the new crate:
cargo release publish --execute && cargo release tag --execute && cargo release push --execute
Bonus:
- Merge the CHANGELOG/README/CONTRIBUTING back from
release/0.2
tomain