demo.mp4
A fast, opinionated linter and formatter for GDScript (Godot 4.x), built in Rust.
gdstyle catches style violations, naming inconsistencies, and common code-quality issues, and auto-formats GDScript to the official Godot style guide. Many of the conventions are taken from Nathan Lovato and GDQuest's GDScript style guide.
- 57 lint rules across syntax, naming, formatting, ordering, and code quality.
- Formatter (
gdstyle fmt) that's in-place and idempotent, and reorders class members into the canonical Godot order. - Auto-fix:
--fixfor the safe ones,--unsafe-fixfor renames. Renames follow into other.gdfiles and into the.tscn/.tresscene wiring. - Single static binary. No Python, no Rust toolchain, no Godot install required to run it.
- Optional Godot editor plugin: bottom panel with clickable diagnostics, single-click fixes, Lint/Format on save. Uses the GDExtension when present, falls back to the CLI binary otherwise.
- pre-commit framework integration out of the box (
gdstyleandgdstyle-fmthooks). - Text and JSON output with configurable exit codes, so it slots into CI.
- TOML config (
gdstyle.toml) with per-rule overrides and CLI flags for one-off tweaks. - Per-line (
# gdstyle:ignore) and per-file (# gdstyle:ignore-file) suppression comments, both with optional rule-list narrowing. - Usable as a Rust library, not just a CLI.
Pre-built binaries are available for all major platforms. You don't need a Rust toolchain unless you want to build from source.
- Go to the latest release
- Download the archive for your platform:
- Linux:
gdstyle-x86_64-unknown-linux-gnu.tar.gz - macOS (Intel):
gdstyle-x86_64-apple-darwin.tar.gz - macOS (Apple Silicon):
gdstyle-aarch64-apple-darwin.tar.gz - Windows:
gdstyle-x86_64-pc-windows-msvc.zip
- Linux:
- Extract the
gdstylebinary and place it somewhere on yourPATH:
# Example for Linux / macOS
tar xzf gdstyle-*.tar.gz
cp gdstyle ~/.local/bin/ # or /usr/local/bin/, or anywhere on your PATHTo build from source you need a Rust toolchain.
git clone https://github.com/atelico/gdstyle.git
cd gdstyle
cargo build --release
# The binary is at target/release/gdstyle
cp target/release/gdstyle ~/.local/bin/cargo install gdstyle# Lint the current directory (recursively finds all .gd files)
gdstyle
# Lint specific files or directories
gdstyle check src/player.gd src/enemies/
# Auto-fix safe violations
gdstyle check --fix
# Auto-fix all violations including unsafe ones
gdstyle check --unsafe-fix
# Format all GDScript files in place
gdstyle fmt
# Check formatting without modifying files (exit 1 if changes needed)
gdstyle fmt --check
# Show formatting diff without modifying files
gdstyle fmt --diff
# Output lint results as JSON (for CI integration)
gdstyle check --format json
# List all available rules
gdstyle rules
# Generate a starter config file
gdstyle init
# Only check naming rules
gdstyle check --select naming
# Ignore specific rules
gdstyle check --ignore "format/max-line-length,format/double-quotes"
# Override max line length
gdstyle check --max-line-length 120gdstyle ships with 57 rules organized into five categories. Most rules are enabled by default (a few advisory rules are opt-in).
| Rule | Description | Fixable |
|---|---|---|
syntax/lex-error |
Report lexer errors: unterminated strings, invalid numbers, unexpected characters | - |
syntax/reserved-identifier |
Reserved words (namespace, trait, yield, self, ...) used as a function, parameter, variable, constant, signal, enum, class or loop-variable name. Godot refuses to parse these files, so this defaults to error |
- |
| Rule | Description | Fixable |
|---|---|---|
naming/class-name-pascal-case |
Class names must use PascalCase |
unsafe |
naming/function-name-snake-case |
Function names must use snake_case |
unsafe |
naming/variable-name-snake-case |
Variable names must use snake_case |
unsafe |
naming/constant-name-screaming-case |
Constants must use SCREAMING_SNAKE_CASE (or PascalCase for preloads) |
unsafe |
naming/signal-name-snake-case |
Signal names must use snake_case |
unsafe |
naming/enum-name-pascal-case |
Enum type names must use PascalCase |
unsafe |
naming/enum-member-screaming-case |
Enum members must use SCREAMING_SNAKE_CASE |
unsafe |
naming/file-name-snake-case |
File names must use snake_case |
- |
naming/signal-past-tense |
Signal names should use past tense (handles irregular verbs, gerunds, nouns) | unsafe |
naming/private-underscore-prefix |
Private members with _ should not have @export |
- |
naming/node-name-pascal-case |
$NodePath references should use PascalCase |
unsafe |
| Rule | Description | Fixable |
|---|---|---|
format/max-line-length |
Lines must not exceed the configured max length (default: 100) | fmt |
format/trailing-whitespace |
No trailing whitespace on any line | safe |
format/trailing-newline |
Files must end with a newline character | safe |
format/no-tabs-as-spaces |
Indentation must use tabs (configurable to spaces) | safe |
format/boolean-operators |
Use and/or/not instead of &&/||/! |
safe |
format/double-quotes |
Prefer double quotes for strings | safe |
format/comment-spacing |
Comments must have a space after # |
safe |
format/no-unnecessary-parens |
No unnecessary parentheses in if/while/elif conditions |
safe |
format/number-literals |
Hex digits must be lowercase (0xff, not 0xFF) |
safe |
format/one-statement-per-line |
One statement per line (no semicolons to separate statements). Match arms and inline callable bodies are exempt, since their statements are not peers of the enclosing line | safe |
format/blank-lines |
Collapse 3+ blank lines to 2 | safe |
format/trailing-comma |
Trailing comma on last item of multi-line collections whose closing bracket starts its own line | safe |
format/operator-spacing |
One space around binary operators | safe |
format/colon-spacing |
No space before :, one space after (except := and end of line) |
safe |
format/comma-spacing |
No space before ,, one space after (except newline / closing bracket) |
safe |
format/brace-spacing |
Single-line dictionary literals need a space after { and before } |
safe |
format/call-paren-spacing |
No space between a callee and its opening ( |
safe |
format/float-literal-zeros |
Float literals need leading/trailing zeros (0.5, not .5) |
safe |
format/large-number-underscores |
Numbers at or above large_number_threshold (default 10_000) should use underscores (10_000). Covers ints and the integer part of floats (1_000_000.5); hex/binary literals are skipped |
safe |
format/enum-one-per-line |
Each enum member on its own line | safe |
| Rule | Description | Fixable |
|---|---|---|
order/class-member-order |
Class members must follow the canonical Godot ordering | fmt |
The canonical ordering enforced by order/class-member-order is:
@tool@iconclass_nameextends- Doc comments (
##) - Signals
- Enums
- Constants
- Static variables
@exportvariables- Regular variables
@onreadyvariables- Virtual methods (
_init,_ready,_process, etc.) - Regular methods
- Inner classes
| Rule | Description | Default | Fixable |
|---|---|---|---|
quality/max-function-length |
Functions must not exceed the configured max body length (default: 50 lines) | on | - |
quality/max-file-length |
Files must not exceed the configured max length (default: 1000 lines) | on | - |
quality/max-parameters |
Functions must not have more than the configured max parameters (default: 5) | on | - |
quality/unnecessary-pass |
pass alongside other statements is unnecessary |
on | - |
quality/no-debug-print |
Debug print()/prints()/printerr() calls left in code |
off | - |
quality/self-comparison |
Comparing a value with itself (x == x) |
on | - |
quality/no-self-assign |
Self-assignment (x = x) |
on | - |
quality/duplicate-dict-key |
Duplicate keys in dictionary literals | on | - |
quality/duplicated-load |
Same path passed to load()/preload() multiple times |
on | - |
quality/type-hint |
Missing type hints on variables, parameters, and return types | off | - |
quality/empty-function |
Empty or pass-only functions | off | - |
quality/max-class-variables |
Too many class-level variables (default: 15) | on | - |
quality/max-public-methods |
Too many public methods per class (default: 20) | on | - |
quality/max-inner-classes |
Too many inner classes per file (default: 5) | on | - |
quality/no-else-return |
Unnecessary else/elif after return |
on | - |
quality/unreachable-code |
Code after return, break, or continue |
on | - |
quality/await-in-loop |
await used inside a for/while loop |
on | - |
quality/allocation-in-loop |
Object allocation (.new()) inside a loop |
on | - |
quality/process-get-node |
Node lookups ($, get_node()) in _process/_physics_process |
on | - |
quality/max-nesting-depth |
Nesting depth exceeds limit (default: 4) | on | - |
quality/max-returns |
Too many return statements per function (default: 6) |
on | - |
quality/max-branches |
Too many branches (if/elif/match) per function (default: 8) |
on | - |
quality/max-local-variables |
Too many local variables per function (default: 10) | on | - |
gdstyle can automatically fix many violations:
# Fix safe violations only (formatting, naming conventions)
gdstyle check --fix
# Fix all violations including unsafe ones (signal renaming, member reordering)
gdstyle check --unsafe-fixFixes are written to disk in place. There's no backup and no confirmation prompt. Commit or stash before running
--fixor--unsafe-fix, then review the diff. (gdstyle fmt --diffpreviews formatter changes without writing.)
Safe fixes preserve behavior. Unsafe fixes can change semantics (renaming signals, variables, and other identifiers), so review the diff before committing.
When --unsafe-fix renames an identifier, gdstyle follows the rename across every .gd file in the project and into the .tscn/.tres scene files that wire signals or methods to that name. Anything it can't safely rewrite is reported as a warning so you can fix it by hand.
The fmt subcommand reformats GDScript files in a single pass:
# Format all .gd files in place
gdstyle fmt
# Check if files are already formatted (exit 1 if not)
gdstyle fmt --check
# Show a diff of what would change
gdstyle fmt --diffThe formatter normalizes indentation, trailing whitespace, blank lines (including the spacing between class members), boolean operators (&&/||/! to and/or/not), string quotes, comment spacing, colon and comma spacing, float and hex literals, trailing newlines, and single-line enums (expanded to multi-line). It reorders class members to match the canonical style guide order, and wraps long lines at commas inside parentheses, brackets, and braces. Running it twice produces the same output.
Note: Line wrapping breaks at comma boundaries inside delimiters (parentheses, brackets, braces),
and/oroperators inif/elif/whileconditions, and word boundaries in long comments. Lines without any breakable pattern (e.g., long strings, property chains, continuation lines without enclosing delimiters) are left alone.
gdstyle looks for a config file named gdstyle.toml or .gdstyle.toml in the current directory and walks up the directory tree until it finds one. You can also specify a config file explicitly with --config, or generate a starter one:
gdstyle init# gdstyle.toml
# Maximum line length (default: 100)
max_line_length = 100
# Use tabs for indentation (default: true)
use_tabs = true
# Maximum function body length in lines (default: 50)
max_function_length = 50
# Maximum file length in lines (default: 1000)
max_file_length = 1000
# Maximum number of function parameters (default: 5)
max_parameters = 5
# Maximum return statements per function (default: 6)
max_returns = 6
# Maximum nesting depth inside a function (default: 4)
max_nesting_depth = 4
# Maximum local variables per function (default: 10)
max_local_variables = 10
# Maximum branches (if/elif/match) per function (default: 8)
max_branches = 8
# Maximum class-level variables (default: 15)
max_class_variables = 15
# Maximum public methods per class (default: 20)
max_public_methods = 20
# Maximum inner classes per file (default: 5)
max_inner_classes = 5
# Smallest number format/large-number-underscores asks to group (default: 10_000).
# For floats the integer part is compared. Raise it to 1_000_000 to leave
# five- and six-digit constants (sample rates, PCM limits) ungrouped.
large_number_threshold = 10_000
# Put one blank line between public and private (`_`-prefixed) constants and
# variables of the same kind when formatting (default: false). Applies to
# script-level members; inner-class bodies keep their blank lines as written.
blank_line_between_public_and_private = false
# File/directory patterns to exclude
exclude = [".godot", "addons"]
# Patterns to force-include even when `exclude` matches them. An include always
# wins over an exclude, so you can lint one plugin inside an otherwise-excluded
# directory. Empty by default.
include = ["addons/my_plugin"]
# Per-rule severity overrides
# Values: "off", "warn", "error"
[rules]
"format/double-quotes" = "off" # Disable a rule entirely
"naming/class-name-pascal-case" = "error" # Escalate to error
"quality/max-function-length" = "warn" # Keep as warning
"quality/no-debug-print" = "warn" # Enable an off-by-default ruleWhen no config file is found, gdstyle uses these defaults:
| Setting | Default |
|---|---|
max_line_length |
100 |
use_tabs |
true |
max_function_length |
50 |
max_file_length |
1000 |
max_parameters |
5 |
max_returns |
6 |
max_nesting_depth |
4 |
max_local_variables |
10 |
max_branches |
8 |
max_class_variables |
15 |
max_public_methods |
20 |
max_inner_classes |
5 |
large_number_threshold |
10_000 |
blank_line_between_public_and_private |
false |
exclude |
[".godot", "addons"] |
include |
[] |
Most rules are enabled by default with warn severity. Three advisory rules (quality/type-hint, quality/empty-function, quality/no-debug-print) are off by default and must be explicitly enabled.
Severity is the CI knob: check exits 1 as soon as one diagnostic has
error severity, and 0 when everything is a warning. Set the rules you
want to block a build to "error" in [rules], or use
--max-warnings <N> to cap warnings without escalating
individual rules.
A [rules] key that matches no known rule is reported on stderr rather
than silently ignored, so a typo doesn't look like a setting that took
effect:
$ gdstyle check
warning: unknown rule name in [rules]: naming/variable-snake-case
run `gdstyle rules` to see the available rules
gdstyle has two ways to silence a warning from source: per-line for
spot exemptions and per-file for class- or file-scope rules. Both
use a # gdstyle:ignore comment, with the same =rule1,rule2 syntax
for narrowing to specific rules.
| Directive | Scope | Form |
|---|---|---|
# gdstyle:ignore |
the next code line | standalone (own line) |
# gdstyle:ignore |
the same code line | inline (end of a code line) |
# gdstyle:ignore-file |
every diagnostic in the file | anywhere in the file |
Add =rule1,rule2 to any of the above to narrow the suppression to a
comma-separated list of rule IDs. Without the =... suffix, every rule
is suppressed within that scope. Rule IDs are the full
category/rule-name shown in the diagnostic output (e.g.
naming/variable-name-snake-case).
Standalone (applies to the next code line):
# gdstyle:ignore=naming/variable-name-snake-case
var BadName: int = 5Inline (applies to the same line):
var BadName: int = 5 # gdstyle:ignore=naming/variable-name-snake-caseBare (suppresses every rule on the target line):
# gdstyle:ignore
var BadName: int = 5Multiple rules in one directive:
# gdstyle:ignore=naming/variable-name-snake-case,format/max-line-length
var SomeReallyLongVariableNameThatExceedsTheMaxLineLengthAndAlsoUsesTheBadNamingConvention: int = 5Use # gdstyle:ignore-file when the diagnostic isn't attached to a
single line, or when you genuinely want a whole-file exemption.
Anchor it at the top of the file by convention so future readers see
it immediately, but the parser accepts it anywhere.
# gdstyle:ignore-file=quality/max-public-methods
class_name OrchestrationFacade
extends Node
# ... 25 public methods followMultiple rules on one directive, or several directives stacked, both work:
# gdstyle:ignore-file=quality/max-public-methods,quality/max-class-variables
# gdstyle:ignore-file=quality/max-inner-classes
class_name BigConfigDocumentA bare # gdstyle:ignore-file (no =...) disables every rule in
the file. Useful for generated code or third-party drops you don't
own:
# gdstyle:ignore-file
# This file is generated by build/gen.py, do not edit.-
Per-line is the right tool for one-off exemptions where the diagnostic clearly belongs to a single line (a deliberately weird variable name, a long string literal, a public field you want to keep snake_case-violating because it mirrors a JSON key).
-
Per-file is the right tool for rules that report against the whole class or file rather than a specific statement:
quality/max-public-methods(reports at the class header)quality/max-class-variables(same)quality/max-inner-classes(same)quality/max-file-length(reports at line 1)- Generated files where any rule is moot.
Trying to suppress these per-line is awkward at best (you have to put the comment inline on the
class_nameline, which makes the signature noisy) and impossible at worst (generated files have no natural spot for 50 inline comments).
If you want to disable a rule across the entire project rather than
file-by-file, set it to "off" in gdstyle.toml instead:
[rules]
"quality/max-public-methods" = "off"The TOML config and inline suppressions are independent; either one silencing a rule is enough to drop the diagnostic.
order/class-member-order is enforced by the formatter, so ignoring it
does double duty: it silences the check diagnostic and pins the
member where you wrote it, so gdstyle fmt won't move it. Everything
else still reorders around the pinned member. This is handy when two
declarations are meant to sit together, for example a const lookup
table kept next to the enum it mirrors:
enum SaveGameFormat {
BINARY,
TEXT,
}
# gdstyle:ignore=order/class-member-order
const SAVE_GAME_FORMAT_DISPLAY_NAMES: PackedStringArray = [
"OPTIONS_GENERAL_BINARY",
"OPTIONS_GENERAL_TEXT",
]The directive may sit directly above the declaration (or above its
annotations). A # gdstyle:ignore-file=order/class-member-order pins
every member, disabling reordering for the whole file.
gdstyle fmt skips reordering a file when moving members could change
what the code does:
- A class-level
var/constinitializer refers to another class-level declaration. GDScript runs initializers in source order. - The move would change which code sits between
@warning_ignore_startand@warning_ignore_restore. The region would otherwise end up silencing warnings on different members.
check still reports order/class-member-order for such a file, so you
can fix the order by hand.
gdstyle [COMMAND] [OPTIONS] [PATHS]...
| Command | Description |
|---|---|
check |
Lint files (default when no subcommand given) |
fmt |
Format files in place |
rules |
List all available lint rules |
init |
Generate a starter gdstyle.toml configuration file |
| Option | Description |
|---|---|
--fix |
Auto-fix safe violations |
--unsafe-fix |
Auto-fix all violations including unsafe ones |
--format <FORMAT> |
Output format: text (default) or json |
-c, --config <PATH> |
Path to configuration file |
--select <RULES> |
Only check specific rules (comma-separated, supports partial matching) |
--ignore <RULES> |
Ignore specific rules (comma-separated) |
--max-line-length <N> |
Override the maximum line length |
--max-warnings <N> |
Exit 1 when more than N warnings are found |
--no-color |
Disable colored output |
| Option | Description |
|---|---|
--check |
Dry-run: exit 1 if any file would change |
--diff |
Print a diff of what would change |
-c, --config <PATH> |
Path to configuration file |
--no-color |
Disable colored output |
| Option | Description |
|---|---|
--force |
Overwrite existing config file |
| Code | Meaning |
|---|---|
0 |
Linting/formatting completed (warnings only, or no issues) |
1 |
Linting completed with errors, --max-warnings was exceeded, or fmt --check found changes |
2 |
Configuration error |
There are two ways to make warnings fail a build. Set the rules you care
about to "error" in [rules] to fail on those specifically, or pass
--max-warnings <N> to cap the total regardless of rule. --max-warnings 0
means "no warnings at all"; --max-warnings 5 fails only at six or more.
name: GDScript Style
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install gdstyle
run: cargo install gdstyle
- name: Check formatting
run: gdstyle fmt --check
- name: Lint GDScript files
run: gdstyle check
# Or fail on warnings too, without escalating individual rules:
# run: gdstyle check --max-warnings 0gdstyle ships hooks for the pre-commit framework.
Add the following to your project's .pre-commit-config.yaml:
repos:
- repo: https://github.com/atelico/gdstyle
rev: v0.3.3 # pin to a released tag; bump with `pre-commit autoupdate`
hooks:
- id: gdstyle # lint (fails the commit on diagnostics)
- id: gdstyle-fmt # format in placeThen install the git hook with pre-commit install. The first run builds
gdstyle from source via cargo, so the user needs a Rust toolchain on their
machine; subsequent runs are cached.
If you'd rather not depend on the pre-commit framework, a minimal raw git hook also works:
#!/bin/bash
# .git/hooks/pre-commit
GD_FILES=$(git diff --cached --name-only --diff-filter=ACM -- '*.gd')
if [ -n "$GD_FILES" ]; then
gdstyle check $GD_FILES
if [ $? -ne 0 ]; then
echo "GDScript lint failed. Fix with 'gdstyle check --fix' or suppress with '# gdstyle:ignore'."
exit 1
fi
fiWhen using --format json, gdstyle outputs a JSON array of diagnostics:
[
{
"rule": "naming/variable-name-snake-case",
"message": "Variable 'BadName' should use snake_case: 'bad_name'",
"severity": "warning",
"span": {
"line": 5,
"column": 1
},
"file": "src/player.gd"
}
]You can also use gdstyle as a Rust library. Full API docs live at docs.rs/gdstyle.
use gdstyle::config::Config;
use gdstyle::linter;
use gdstyle::formatter;
fn main() {
let config = Config::default();
// Lint a file
let diagnostics = linter::lint_file(
std::path::Path::new("player.gd"),
&config,
).unwrap();
for d in &diagnostics {
println!("line {}: [{}] {}", d.span.line, d.rule, d.message);
}
// Format a source string
let source = "var x = 'hello'\n";
let formatted = formatter::format_source(source, &config);
assert!(formatted.contains("\"hello\""));
}gdstyle/
├── src/
│ ├── main.rs # CLI entry point (clap subcommands)
│ ├── lib.rs # Library root
│ ├── token.rs # Token types (Span, TokenKind, Token)
│ ├── lexer.rs # Tokenizer (indentation-aware, GDScript 4.x)
│ ├── ast.rs # AST node types for linting
│ ├── parser.rs # Lightweight parser (just enough for linting)
│ ├── diagnostic.rs # Diagnostic, Fix, and Replacement types
│ ├── config.rs # TOML configuration loading
│ ├── linter.rs # Main lint pipeline (tokenize -> parse -> rules -> filter)
│ ├── reporter.rs # Text and JSON output formatting
│ ├── fixer.rs # Auto-fix engine (applies replacements)
│ ├── formatter.rs # Multi-pass formatter
│ └── rules/
│ ├── mod.rs # Rule dispatcher
│ ├── naming.rs # 11 naming convention rules
│ ├── formatting.rs # 20 formatting rules
│ ├── ordering.rs # Class member ordering rule
│ └── quality.rs # 23 code quality rules
├── gdstyle-gdext/ # GDExtension wrapper (exposes linter/formatter to Godot)
│ ├── Cargo.toml
│ └── src/lib.rs
├── godot-plugin/ # Godot 4.x editor plugin
│ └── addons/gdstyle/
│ ├── plugin.cfg
│ ├── plugin.gd
│ ├── gdstyle_panel.gd
│ └── gdstyle.gdextension
├── tests/
│ ├── integration_test.rs # End-to-end integration tests
│ └── fixtures/ # GDScript test fixtures
├── .github/workflows/
│ ├── ci.yml # rustfmt, clippy, and tests on every PR
│ └── release.yml # builds + publishes CLI + GDExtension for all platforms
├── examples/ # Example GDScript files for trying out gdstyle
├── Cargo.toml
└── gdstyle.example.toml
The examples/ directory contains sample GDScript files you can use to try out linting and formatting:
# Lint the examples. Expect warnings about naming, formatting, and quality.
gdstyle check examples/
# See what the formatter would change
gdstyle fmt --diff examples/
# Auto-fix all safe violations
gdstyle check --fix examples/
# Format everything
gdstyle fmt examples/gdstyle has 383 tests: 161 unit tests, 219 integration tests, and 3 doctests.
cargo test # Run all tests
cargo test --lib # Unit tests only
cargo test --test integration_test # Integration tests only
cargo clippy # Lint checkA Godot 4.x editor plugin lives in godot-plugin/. It adds a bottom panel that runs gdstyle and shows clickable diagnostics inside the editor.
The plugin supports two backends:
- GDExtension (native). If the GDExtension library is present, it calls into Rust directly with no process overhead. Requires Godot 4.6+.
- CLI fallback. Spawns the
gdstylebinary. If it isn't onPATH, a Download button fetches the right release from GitHub.
You can switch between backends at any time from the mode dropdown in the toolbar.
- Lint Project / Lint File. Run the linter on every script in the project, or just the one you have open. Project-wide runs honor your
exclude/includeconfig, matching the CLI. - Fix File. Apply all available auto-fixes to the current script in one click.
- Format Project / Format File. Same split for the formatter (also config-aware for project runs).
- Lint on Save. Lint after every save (on by default).
- Format on Save. Format before linting on save.
- Right-click Fix. Right-click any diagnostic with an auto-fix to apply it in place.
- Click to navigate. Double-click a diagnostic to jump to the source line.
- In-memory editing. Lint, fix, and format work directly on the editor buffer, no disk I/O.
- Download
gdstyle-godot-plugin.zipfrom the latest release - Extract the
addons/gdstyle/folder into your Godot project - Enable the plugin in Project > Project Settings > Plugins
When using the GDExtension backend, you can use GdStyle directly in GDScript:
var style = GdStyle.new()
# Lint a file.
var diagnostics = style.lint_res_file("res://player.gd")
for d in diagnostics:
print("Line %d: [%s] %s" % [d["line"], d["rule"], d["message"]])
if d["has_fix"]:
print(" (auto-fixable, safe=%s)" % d["is_safe_fix"])
# Format a source string.
var formatted = style.format_source(source_code)
# Auto-fix violations.
var fixed = style.fix_source(source_code, "player.gd")
# Fix a single diagnostic by line and rule.
style.fix_at_line("res://player.gd", 12, "naming/variable-name-snake-case")
# Configure.
style.set_max_line_length(120)
style.disable_rule("format/double-quotes")
style.load_config_res("res://gdstyle.toml")
# Set a rule's severity: "off", "warn", or "error", the same vocabulary
# gdstyle.toml uses. Returns false and logs an error for anything else.
style.set_rule_severity("naming/variable-name-snake-case", "error")
# Report [rules] keys in the loaded config that match no known rule.
for name in style.unknown_rule_names():
push_warning("gdstyle: unknown rule name %s" % name)
# Collect the project's .gd files the config would lint (honors exclude/include).
# Load the config first so the walk reflects it.
for res_path in style.collect_project_gd_files():
style.lint_res_file(res_path)- Fork the repository
- Create a feature branch:
git checkout -b my-feature - Write tests first, then implement
- Run the full test suite:
cargo test - Run clippy:
cargo clippy - Commit and push
- Open a pull request
MIT