CLI tool
ruby-syntax-tree/syntax_tree avatar
ruby-syntax-tree/syntax_tree

SyntaxTree turns the CRuby parser into a formatting and linting toolkit

A Ruby language formatter written in Ruby

610 stars67 forksRubyMIT

At a glance

What is it?
SyntaxTree is a Ruby gem from ruby-syntax-tree built on the internal CRuby parser, giving you a real syntax tree plus the tools to inspect, search and rewrite it, and it ships as both an stree command line interface and a library. It has no dependencies outside the standard library, which is unusual for a project of this scope.
Who is it for?
SyntaxTree suits Ruby teams that want one parser-backed tool for formatting, linting, search and language server support, and that value a dependency free gem. It does not suit teams that need a formatter with a configurable style, since the output is the project's own format, and it does not suit anyone who needs a different language's grammar unless they are willing to write a plugin.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 173 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The tree comes from the CRuby parser, not from a regex

SyntaxTree is described as a suite of tools built on top of the internal CRuby parser, and it provides the ability to generate a syntax tree from source along with the tools necessary to inspect and manipulate that tree. The stated use cases are building formatters, linters, language servers and more. What separates it from a formatter that pattern matches on lines is that the tool has a real parse tree, with a node for every construct and a location on every node, so a check can ask about structure rather than about text. The library is built with only standard library dependencies, which for a project of this ambition is the most striking fact in the documentation, and it additionally ships with a plugin system so you can build your own syntax trees from other languages and incorporate the same tools. That plugin system is the interesting commitment here, because it turns the CRuby parser from a hard limit into a default.

Two install paths, and which one you pick changes the command

The gem is both a command line interface and a library, and the documentation splits the install advice accordingly. If you only want the CLI, install it globally.

sh
gem install syntax_tree

You can then check the install with the version command.

sh
stree version

If you plan to use SyntaxTree inside your own project, the recommendation is to add it to the application's Gemfile instead.

ruby
gem "syntax_tree"

After that you run bundle install, and the CLI is invoked through bundle exec.

sh
bundle exec stree version

This split is a small thing but it is the difference between a tool that floats above your project and one that is pinned by your lockfile. The library use cases in the documentation are organised as a set of class methods, and the CLI, the Rake integration, the RuboCop integration and the editor integrations are all built on the same core.

Ten CLI commands cover inspection, checking and rewriting

The stree command carries a fixed set of subcommands, and each one is small enough to describe in a sentence. ast prints a textual representation of the syntax tree, so a file containing 1 + 1 gives you a nested s-expression rather than source. check verifies that files match the expected format and is meant for continuous integration or a git hook, so unformatted content never enters the codebase. format writes the formatted version to stdout and explicitly does not write back to the source files. write is the command that does write back. json emits a JSON representation of the tree that is functionally equivalent to the input, which the documentation says is mostly for accessing the tree from JavaScript or serialising it over a network. expr and match both output Ruby case-match expressions, with expr matching the first expression of the input and match matching the input. search and index cover pattern search over source, ctags emits a tag set suitable for use with ctags, and there is a configuration and globbing section covering how files are selected and how the format is configured. Most commands accept file paths after the options, and all of them can take content through standard input or through an inline script option instead.

check and format share the same print width

The formatter is deterministic and its main knob is the print width, which is the same option for both the checking and the formatting command. Checking a file that already matches gives a short confirmation, and checking files that do not match produces a warning naming each file and a line saying the listed files did not match the expected format. The same is true of format, which prints the reformatted source to stdout rather than touching the file.

sh
stree check --print-width=100 path/to/file.rb

The important thing to understand here is that check is not a linter in the RuboCop sense. It answers one question, does this file match the format, which makes it usable as a pre-commit check or a CI gate without pulling in a broader style system. The repository ships a .rubocop.yml, and the documentation lists a RuboCop integration alongside the Rake integration, so the two tools coexist rather than compete. The trade-off is the one every opinionated formatter has. There is no configuration file for choosing a different style here, only a print width, so a team that already has an established house format will find the formatter's output a proposal rather than a fit.

The node API is where the real work happens

The library section of the documentation is organised around a set of entry points, and they cover the range from reading a file to rewriting it. SyntaxTree.read takes a filepath, SyntaxTree.parse takes source, SyntaxTree.format formats source, SyntaxTree.mutation takes a block, SyntaxTree.search takes source and a query with a block, and SyntaxTree.index returns an index of the source. On the node side, the documented operations are child_nodes to walk the tree, copy with attribute overrides, pattern matching against node classes, pretty_print, to_json, format with a printer, equality, and the construct_keys list. Two visitor base classes are provided, BasicVisitor for read only traversal and MutationVisitor for rewriting, along with a WithScope helper. The example the documentation gives for expr shows the node classes being used directly as a case-match pattern, with SyntaxTree::Binary holding left, operator and right, which is a reminder that this is a real object model and not a string wrapper around a parse.

Language server support and the editor story

The language server section documents three capabilities. One is textDocument/formatting, which is the editor side of the same format command, so an editor can format a buffer without shelling out separately. Another is textDocument/inlayHint, which means the server can surface inlay hints as well as formatting. The third is a syntaxTree/visualizing request, which is a custom request the client can use to visualise the tree, presumably for a tree view in the editor. The editor integration is listed alongside the Rake and RuboCop integrations in the documentation. The point of the language server work is that it lets the same tree back both the formatting and the analysis in an editor, so a lint warning can point at a node rather than at a line, which is the kind of precision the AST approach was chosen for in the first place. The inlay hint support is a smaller feature but it is a sign the project is aiming at editor parity rather than only a CI check.

Editorial conclusion

SyntaxTree suits Ruby teams that want one parser-backed tool for formatting, linting, search and language server support, and that value a dependency free gem. It does not suit teams that need a formatter with a configurable style, since the output is the project's own format, and it does not suit anyone who needs a different language's grammar unless they are willing to write a plugin. Before adopting it, run stree check on your repository and look at the diff stree format would produce, since the first formatting run is a large change, and check the plugin API if you need to parse something other than Ruby.

Frequently asked questions

How do I install SyntaxTree?

For the command line interface, install the gem globally with gem install syntax_tree. If you want to use SyntaxTree as a library in your own project, add gem "syntax_tree" to your Gemfile and run bundle install.

What is the difference between stree format and stree write?

The format command writes the formatted version of each file to stdout and does not modify the source files. The write command is the one that changes the files on disk.

Does SyntaxTree have any dependencies outside the Ruby standard library?

No. The documentation states that it is built with only standard library dependencies, on top of the internal CRuby parser.

Can SyntaxTree parse languages other than Ruby?

It ships with a plugin system that lets you build your own syntax trees from other languages and incorporate the same tools, so other languages are supported through plugins you write rather than out of the box.

How do I change the print width used by SyntaxTree?

Both the check and format commands accept the --print-width option, for example stree check --print-width=100 path/to/file.rb. The default format is the project's own, so a different house style is not something the option set covers.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. ruby-syntax-tree/syntax_tree on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ruby-syntax-tree-syntax-tree.svg)](https://hysenlabs.com/projects/ruby-syntax-tree-syntax-tree)

Was this useful?