Skip to content

GDQuest/godot-gdscript-formatter-tree-sitter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Godot GDScript Formatter

A fast code formatter for Godot's GDScript programming language built with Tree Sitter GDScript and Topiary.

The goal of this project is to provide a simple and fast GDScript code formatter that's easy to iterate on and contribute to while we wait for an official one to be integrated into the Godot game engine.

You can learn more about the motivation behind this project in the Motivation section below.

Status

Work in progress - 07/07/2025 - The formatter works but it's far from perfect. It has some formatting rules implemented, including:

  • Spaces: leaving one space consistently between many operators, a number of keywords, or after commas in function calls, arrays, and dictionaries
  • Multi-line structures: simple arrays and dictionaries can be wrapped on one or multiple lines with indentation
  • Indentation: consistent indentation for blocks, function definitions, and control structures

In this system, every rule needs to be written explicitly, including putting a space between different keywords, operators, and punctuation. The formatter doesn't automatically add spaces or indentation unless it's explicitly defined in the rules.

Next steps:

  • Implement correct vertical spacing (blank lines between functions, classes, after the extends statement, etc.)
  • Ensure idempotence (running the formatter more than once should not change the output code for all the features it supports)

Technology limitations:

Topiary doesn't handle maximum line length. Instead, for wrapping code on a single or multiple lines, it uses cues from you: if an array is on a single line, it will remain on a single line, and if it is at least on two lines, Topiary will wrap it on multiple lines.

It's always possible to implement a maximum line length as a post-processing step by using Topiary as a library and then running a custom function to wrap lines that are too long. This isn't planned yet, but it is possible.

Contributing

Contributions are welcome! I've compiled some guides and guidelines below to help you get started with contributing to the GDScript formatter. If you need more information or want to discuss ideas for the formatter, please get in touch on the GDQuest Discord.

Adding new formatting rules

To add new formatting rules to the GDScript formatter, you can follow these steps:

  1. Add test cases with real-world GDScript code: To add a test case, create input/expected file pairs in tests/input/ and tests/expected/ respectively. For example, if you want to test a new rule for function definitions, create tests/input/function_definition.gd and tests/expected/function_definition.gd:
    • The input file contains the GDScript code before running the formatter
    • The expected file contains the GDScript code after applying the new formatting rules
  2. Run tests: Use cargo test to run the formatter on every input/expected file pair in the tests/ directory. This will check if the formatter produces the expected output for each of them
  3. Update queries: Modify queries/gdscript.scm with the formatting rules. This is the file that defines how the formatter should format GDScript code. You can use the existing rules as a reference for writing new ones (and the topiary documentation links below for more details)

Development resources

Important: we will likely not be able to implement all the guidelines from the official style guide with this formatter. What we gain in ease of implementation and maintenance, we lose in flexibility and advanced patterns.

Current development priorities

  • Complete the basics: Review and ensure the basic formatting rules (spaces etc.) cover the most common GDScript code patterns
  • Vertical Spacing: Implement consistent blank lines between functions and classes
  • Idempotence: Ensure formatter produces identical output when run multiple times
  • Edge Cases: Handle complex nested structures and edge cases

Project structure

Here are the most important directories and files in the project:

  • src/: Contains the Rust code to compile and run the formatter using the CLI. It's currently a simple wrapper around the Topiary command line program, but later it could use Topiary as a library instead to pack everything into a simple binary
  • tests/: Contains test files for the formatter. It has input files with unformatted GDScript code and expected output files that the formatter should produce when run on the input files
  • queries/: Contains the Topiary formatting rules for GDScript. The gdscript.scm file is where you define how GDScript code should be formatted based on Tree Sitter queries and Topiary features to mark nodes/patterns for formatting
  • config/: Contains configuration files for Topiary - basically a small file that tells Topiary how to run the formatter for GDScript
  • docs/: This folder will compile images and recaps or cheat sheets with some tricks to help when working with tree sitter queries and topiary.

Installing and running the formatter

Install Topiary's command line tool: https://topiary.tweag.io/book/getting-started/installation/index.html

Build the formatter as a program (requires the Rust language compiler, the Rust language build system cargo, and Topiary to be installed):

cargo build

Format a file:

cargo run -- path/to/file.gd

Format with check mode, to use in a build system (exit code 1 if changes needed):

cargo run -- --check path/to/file.gd

Development workflow

To test formatting on a simple code snippet, you can use echo or cat to pass GDScript code into the Topiary formatter. This is useful for quick tests since the output is directly printed to the console.

echo 'var x=1+2' | TOPIARY_LANGUAGE_DIR=queries topiary format --language gdscript --configuration config/languages.ncl

Running the formatter on a file is also supported, but note that it overwrites the file in place:

TOPIARY_LANGUAGE_DIR=queries topiary format --configuration config/languages.ncl -- test.gd

If you get an error that the idempotence check failed, it means that running the formatter a second time changed the already formatted file, which should ideally not happen. When iterating over a new feature, this is okay - you can first implement the feature, then run the formatter, and finally fix the idempotence issue.

You can use the --skip-idempotence flag to skip this check temporarily while developing new features:

TOPIARY_LANGUAGE_DIR=queries topiary format --configuration config/languages.ncl --skip-idempotence -- test.gd

Running tests

To run the formatter's test suite, use this command:

cargo test

Debugging and visualizing the tree structure

To visualize the graph structure that Topiary uses for formatting, you can use the topiary visualise command. It produces markup that you can pass to the open source program Graphviz to generate a visual representation of the abstract syntax tree (AST) used by Topiary.

This command generates the markup:

echo 'class Test:' | TOPIARY_LANGUAGE_DIR=queries topiary visualise --language gdscript --configuration config/languages.ncl

You can pipe the output to Graphviz's dot command to generate a vector image:

echo 'class Test:' | TOPIARY_LANGUAGE_DIR=queries topiary visualise --language gdscript --configuration config/languages.ncl | dot -Tsvg > image.svg

It will produce an SVG image like this:

Example Graphviz graph generated from GDScript code

You can also use tree-sitter directly to parse GDScript files and visualize the concrete syntax tree. This shows you the raw structure that the tree-sitter parser generates, which you can then use to write formatting rules in Topiary:

tree-sitter parse --scope source.gdscript test.gd

This requires setting up tree-sitter on your computer and having the GDScript parser configured.

When you're getting started with contributing to the formatter, I recommend beginning with the tree visualization commands.

For example, if you want to add a formatting rule for function definitions, you'd first use these commands to see how the parser represents functions in the tree. Then you can write queries that target those specific nodes and apply formatting rules to them in queries/gdscript.scm.

License

MIT

Motivation

Having an official GDScript formatter has been planned since the early days of Godot, but it has always been part of the engine's development backlog. It's a tool Godot users would use daily, so in 2022, we set out to sponsor the development of an official GDScript formatter built into Godot 4.

A lot of work went into this project from GDQuest. Then, following the suggestion to chop up the work into small chunks, a dedicated contributor, Scony, took over the project and tried breaking down the implementation into small chunks to make it much easier to review and merge. However, there isn't an active maintainer to review and merge the work, and the project has been stalled for a while now. The process is looking to take a long time, and we need solutions we can iterate upon quickly and use today.

Scony has been maintaining a solid set of community tools for GDScript, including a code formatter written in Python: Godot GDScript Toolkit. It's a great project that has been used by many Godot developers. So, why start another one?

The main motivation to experiment with this project is that the ruleset of Scony's formatter has grown quite advanced over the years, and it has limitations for us at GDQuest that make it not work for our projects. Some of these limitations are also not easy to overcome.

Since Scony made his great formatter, new technologies have emerged that make it much easier to implement and maintain one. Thanks to Tree Sitter and Topiary, in just a couple of hours, you can now kickstart a code formatter that can be compiled to native code and used in any code editor or IDE.

Tree-sitter is a powerful parser generator that makes it easy to create programming language parsers that run natively (it generates C code). It gives you a simple query language to detect patterns in the code and process them, a bit like CSS selectors (but simpler and more restricted). It's used in modern code editors for syntax highlighting, code navigation, outline, folding, and more (Zed, Neovim, Emacs, Helix, Lapce...).

Topiary is a Rust library and command line program that makes it easy to implement a code formatter using a parser based on Tree Sitter. You use the Tree Sitter query language to define how the code should be formatted, and Topiary takes care of the rest.

About

A fast GDScript formatter for Godot 4, powered by Tree Sitter and Topiary

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published