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 really fast GDScript code formatter that's easy to contribute to, and easy to maintain. It also benefits GDScript support in code editors like Zed, Neovim, and Emacs as we use the project to improve the Tree Sitter GDScript parser.
This project aims to conform to the official GDScript style guide
Use a version control system: Please consider using a version control system like Git to track changes to your code before running the formatter. Even though we already use the formatter ourselves at work, GDScript is a complex language and a formatter needs testing on all possible syntax combinations in the language to ensure the output is rock solid. There can always be edge cases or less common syntax that may not be handled correctly yet.
For detailed documentation and guides, check out these pages:
-
GDScript Formatter docs: Learn how to install the formatter, use it from the command line, and integrate it with your code editor.
-
Godot 4 addon manual: If you want to use the formatter directly from the Godot editor, this page will tell you how to install, configure, and use the official Godot 4 addon (it works with the latest version of Godot 4).
- Format GDScript files nearly instantly (less than 100ms for a 1000-line file on a mid-range laptop, less than 30ms for most files)
- Lint GDScript files for style and convention issues
- Reorder GDScript code to match the official GDScript style guide (variables at the top, then functions, etc.)
- Format code in place (overwrite the file) or print to the standard output
- Check if a file is formatted (for CI/build systems)
- Configure spaces vs tabs and indentation size
You can find binaries for Windows, macOS, and Linux in the releases tab of this repository. Download the binary for your platform, unzip it, rename it to the command name you want (e.g. gdscript-formatter) and place it somewhere in your system PATH.
Alternatively for Arch Linux, there is the community maintained AUR Package gdscript-formatter-bin.
To format a file, run:
gdscript-formatter path/to/file.gdUse the --safe flag to add a safety check that prevents overwriting files if the formatter makes unwanted changes (any change that would modify the code meaning, like removing a piece of functional code). This is useful when you use a development version of the formatter or when you want to be extra careful:
gdscript-formatter --safe path/to/file.gdFormat with check mode, to use in a build system (exit code 1 if changes needed):
gdscript-formatter --check path/to/file.gdTo see other possible options, run gdscript-formatter without any arguments.
The formatter also includes a linter that checks for style and convention issues according to the official GDScript style guide.
To lint a file, run:
gdscript-format lint path/to/file.gdThis will output issues in the format:
filepath:line:rule:severity: description
To see all available linting rules:
gdscript-format lint --list-rulesYou can disable specific rules using the --disable flag:
gdscript-format lint --disable class-name,signal-name path/to/file.gdThe linter provides several configurable options:
gdscript-format lint --max-line-length 120 path/to/file.gdBy default, the linter outputs one line for each warning/error.
For more human readable output, use the --pretty flag:
gdscript-format lint --pretty path/to/file.gdThe linter can be instructed to ignore specific rules for specific lines using special comments.
Ignore a specific rule for the next line:
# gdlint-ignore-next-line private-access
obj._private_method()Ignore a specific rule inline:
obj._private_method() # gdlint-ignore private-accessMultiple rules can be ignored with a comma-separated list:
# gdlint-ignore-next-line constant-name,max-line-length
const anotherBadConstName = "this line is also very long but it will be ignored by the comment above this"Ignore ALL rules for the next line:
# gdlint-ignore-next-line
obj._private_method()Ignore ALL rules for the current line inline:
obj._private_method() # gdlint-ignorefunction-name- validates function names (snake_case,_private_snake_case)class-name- validates class names (PascalCase)signal-name- validates signal names (snake_case)variable-name- validates class variable names (snake_caseor_private_snake_case)function-argument-name- validates function argument names (snake_caseor_private_snake_case)loop-variable-name- validates loop variable names (snake_caseor_private_snake_case)enum-name- validates enum names (PascalCase)enum-member-name- validates enum element names (CONSTANT_CASE)constant-name- validates constant names (CONSTANT_CASE)duplicated-load- detects copy-pasted load() calls for the same pathstandalone-expression- detects standalone expressions that aren't usedunnecessary-pass- detects pass statements when other statements are presentunused-argument- detects unused function argumentscomparison-with-itself- detects redundant comparisons likex == xprivate-access- detects calls to private methods or variable references (prefixed with_)max-line-length- validates maximum line lengthno-else-return- detects unnecessary else afterif/elifblocks that end withreturn
Note
If you managed to make the formatter work in a code editor that isn't listed here, consider contributing to this section or sharing your findings in this issue.
In this section, you'll find instructions for setting up the formatter in several code editors.
As a reminder: don't turn this on when working on an important project without using a version control system like Git!.
- Install the godot-format extension in VSCode. Press
Ctrl+Pand run:
ext install DoHe.godot-format
- This extension ships with the formatter binary pre-installed, so you don't need to download the formatter separately.
Once installed, visit the extension page to see the available settings and set your preferences.
-
Install the formatter (see instructions above).
-
Install zed-gdscript extension. This is needed to ensure that the formatter will only format GDScript files.
-
After installing the extension, add the following JSON configuration to your
settings.jsonfile:
{
"languages": {
"GDScript": {
"formatter": {
"external": {
"command": "gdscript-formatter",
"arguments": []
}
}
}
}
}-
If you renamed the binary to something else during installation, adjust the
commandname accordingly. It can also be a full path to the binary. -
argumentsis a comma separated list of flags. For example, to enable code reordering and use spaces instead of tabs, replace[]with["--reorder-code", "--use-spaces"].
Once this is done, you can start using the formatter. By default Zed will run the formatter every time you save a file. If don't want this to happen, set the format_on_save setting in settings.json to false. To format manually, execute the editor: format command in Zed.
As a reminder: don't leave this on when working on an important project without using a VCS!.
-
Follow the instructions carefully on installing the formatter and make sure it's in your PATH.
-
Go to Helix config directory to edit your languages configuration file. Since I use helix as my terminal editor and I'm on macOS, I'll open it up with the hx command:
cd ~/.config/helix && hx languages.toml- Add this line inside your [[Language]] block assigned to gdscript:
formatter = { command = "gdscript-formatter", args = ["--reorder-code"] }Keep in mind, using gdscript with Helix requires more configuration than this, including changing a few options inside Godot editor and possibly making a script for activating Helix in your terminal of choice.
- Auto-format on save can be enabled by adding this line to your gdscript language options, as shown in the linked example at Helix repository:
auto-format = true- First, install the formatter on your computer.
- Open Rider and go to your IDE settings. You can find them under
Tools > File Watchers. - Click the
+button to add a new file watcher and pick<custom>from the dropdown list. - Now fill in these fields:
- Name: GDScript Formatter
- File Type: GDScript
- Scope: Current File
- Program:
gdscript-formatter(or write the full path to the binary if it's not in your PATH) - Arguments:
$FilePath$or$FilePath$ --reorder-code - Output Paths to refresh:
$FilePath$ - Working Directory:
$ModuleFilePath$ - You can optionally check any of the checkboxes for auto-save and triggering the watcher when files change outside the editor.
- Keep the box for
Create output file from stdoutunchecked.
If you lose work because of the formatter, you can usually get it back with a simple "undo" (Cmd/Ctrl + Z). This will show you the "undo reload from disk" popup. You can also check the local history by right-clicking on the file in the project explorer and selecting Local History > Show History.
09/18/2025 - The formatter now has many formatting rules implemented and is ready to test. It includes:
- Spaces: leaving one space consistently between many operators, most 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 with configurable indent types (tabs or spaces)
- Vertical spacing: proper blank lines between functions, classes, and other major code structures
And more!
Please report any issues you find with code snippets! GDScript has grown into a complex language with many different syntax patterns. While the formatter covers many common cases, there can always be edge cases or less common syntax that may not be handled correctly yet. You can find known issues in the GitHub issues section.
The formatter's technology doesn't handle maximum line length automatically. Instead, for wrapping code on a single or multiple lines, it uses cues from you, the developer. For example, if you write an array on a single line, it will remain on a single line. This input:
var numbers: Array[int] = [1,2,3,4,5]Will be formatted like this:
var numbers: Array[int] = [1, 2, 3, 4, 5]If you insert a line return, the array will wrap on multiple lines instead. This input:
var dialogue_items: Array[String] = ["I'm learning about Arrays...",
"...and it is a little bit complicated.", "Let's see if I got it right: an array is a list of values!", "Did I get it right? Did I?", "Hehe! Bye bye~!"]Will be formatted like this:
var dialogue_items: Array[String] = [
"I'm learning about Arrays...",
"...and it is a little bit complicated.",
"Let's see if I got it right: an array is a list of values!",
"Did I get it right? Did I?",
"Hehe! Bye bye~!"
]You can insert the line returns anywhere in the array, and the formatter will keep it on multiple lines. The same applies to other structures.
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.
To build the formatter locally for testing, you need the Rust language compiler and the Rust language build system cargo. Then you can run:
cargo buildIt'll download all the dependencies, compile them, and build a binary in a target/debug/ folder. You can then run the built program with cargo run -- [args].
To add new formatting rules to the GDScript formatter, you can follow these steps:
- Add test cases with real-world GDScript code: To add a test case, create input/expected file pairs in
tests/input/andtests/expected/respectively. For example, if you want to test a new rule for function definitions, createtests/input/function_definition.gdandtests/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
- Run tests: Use
cargo testto run the formatter on every input/expected file pair in thetests/directory. This will check if the formatter produces the expected output for each of them - Update queries: Modify
queries/gdscript.scmwith 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)
- Tree-sitter Query Syntax: Reference for writing tree-sitter queries - it's essential to understand how to write queries for formatting
- Topiary Documentation: Complete guide to query syntax and formatting
- GDScript Style Guide: Official Godot style guidelines
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.
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.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. Thegdscript.scmfile 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.
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.nclRunning 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.gdIf 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.gdTo run the formatter's test suite, use this command:
cargo testTo 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.nclYou 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.svgIt will produce an SVG image like this:
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.gdThis 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.
The Godot team has wanted an official GDScript formatter since the early days, 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.
We put a lot of work into this project at GDQuest. Then, following the suggestion to break up the work into smaller contributions, 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 stuck for a while now. The process looks like it will take a long time, and we need solutions we can work on 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 many Godot developers have used. So, why start another one?
The main reason to try this project is that Scony's formatter has grown quite complex 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 fix.
Since Scony made his great formatter, new technologies have come up that could make it much easier to build and maintain one: Tree Sitter and Topiary. This project started as a suggestion from one of Godot's core contributors to test these new technologies for GDScript formatting. While testing it, I could get results within just a couple of hours and found it to be a very promising approach that could lead to a simple and fast formatter that's relatively easy to maintain and extend for the community.