Skip to content
atelicoPublic

About

A fast, opinionated linter and formatter for GDScript (Godot 4.x), in Rust.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

66 stars

Watchers

1 watching

Forks

Repository files navigation

gdstyle

Crates.io docs.rs License: MIT CI Build

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.

Features

  • 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: --fix for the safe ones, --unsafe-fix for renames. Renames follow into other .gd files and into the .tscn/.tres scene 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 (gdstyle and gdstyle-fmt hooks).
  • 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.

Installation

Pre-built binaries are available for all major platforms. You don't need a Rust toolchain unless you want to build from source.

Pre-built binaries (recommended)

  1. Go to the latest release
  2. 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
  3. Extract the gdstyle binary and place it somewhere on your PATH:
# Example for Linux / macOS
tar xzf gdstyle-*.tar.gz
cp gdstyle ~/.local/bin/   # or /usr/local/bin/, or anywhere on your PATH

Building from source

To 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/

From crates.io

cargo install gdstyle

Quick start

# 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 120

Rules

gdstyle ships with 57 rules organized into five categories. Most rules are enabled by default (a few advisory rules are opt-in).

Syntax (2 rules)

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 -

Naming (11 rules)

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

Formatting (20 rules)

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

Ordering (1 rule)

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:

  1. @tool
  2. @icon
  3. class_name
  4. extends
  5. Doc comments (##)
  6. Signals
  7. Enums
  8. Constants
  9. Static variables
  10. @export variables
  11. Regular variables
  12. @onready variables
  13. Virtual methods (_init, _ready, _process, etc.)
  14. Regular methods
  15. Inner classes

Quality (23 rules)

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 -

Auto-fix

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-fix

Fixes are written to disk in place. There's no backup and no confirmation prompt. Commit or stash before running --fix or --unsafe-fix, then review the diff. (gdstyle fmt --diff previews 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.

Formatter

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 --diff

The 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/or operators in if/elif/while conditions, 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.

Configuration

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

Example config file

# 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 rule

Default configuration

When 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

Suppressing diagnostics

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).

Per-line suppression

Standalone (applies to the next code line):

# gdstyle:ignore=naming/variable-name-snake-case
var BadName: int = 5

Inline (applies to the same line):

var BadName: int = 5  # gdstyle:ignore=naming/variable-name-snake-case

Bare (suppresses every rule on the target line):

# gdstyle:ignore
var BadName: int = 5

Multiple rules in one directive:

# gdstyle:ignore=naming/variable-name-snake-case,format/max-line-length
var SomeReallyLongVariableNameThatExceedsTheMaxLineLengthAndAlsoUsesTheBadNamingConvention: int = 5

Per-file suppression

Use # 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 follow

Multiple 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 BigConfigDocument

A 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.

When to use which

  • 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_name line, which makes the signature noisy) and impossible at worst (generated files have no natural spot for 50 inline comments).

Project-wide silencing

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.

Pinning a member against reordering

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.

When the formatter leaves the order alone

gdstyle fmt skips reordering a file when moving members could change what the code does:

  • A class-level var/const initializer refers to another class-level declaration. GDScript runs initializers in source order.
  • The move would change which code sits between @warning_ignore_start and @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.

CLI reference

gdstyle [COMMAND] [OPTIONS] [PATHS]...

Subcommands

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

check options

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

fmt options

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

init options

Option Description
--force Overwrite existing config file

Exit codes

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.

CI/CD integration

GitHub Actions

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 0

Pre-commit hook

gdstyle 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 place

Then 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
fi

JSON output format

When 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"
  }
]

Using as a library

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\""));
}

Project structure

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

Examples

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/

Testing

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 check

Godot editor plugin

A 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 gdstyle binary. If it isn't on PATH, a Download button fetches the right release from GitHub.

You can switch between backends at any time from the mode dropdown in the toolbar.

Plugin features

  • 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/include config, 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.

Installation (pre-built plugin)

  1. Download gdstyle-godot-plugin.zip from the latest release
  2. Extract the addons/gdstyle/ folder into your Godot project
  3. Enable the plugin in Project > Project Settings > Plugins

GDExtension API

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)

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b my-feature
  3. Write tests first, then implement
  4. Run the full test suite: cargo test
  5. Run clippy: cargo clippy
  6. Commit and push
  7. Open a pull request

License

MIT

About

A fast, opinionated linter and formatter for GDScript (Godot 4.x), in Rust.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

66 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages