neon-sprawl/.cursor/rules/gdscript-style.md

5.3 KiB
Raw Blame History

description globs alwaysApply
GDScript naming, formatting, and Godot-idiomatic patterns (Godot docs style guide). **/*.gd true

GDScript style (Neon Sprawl)

Follow the Godot GDScript style guide. Prefer clarity and consistency with existing scripts in the repo.

CI also enforces gdlint and gdformat (see gdlintrc and .github/workflows/gdscript.yml) on client/scripts/ and client/test/. When this guide and personal preference differ, follow the linted version below so pushed files pass CI.

Naming

  • Functions, variables, signals: snake_case.
  • Classes (class_name, custom resources): PascalCase.
  • Constants (const): ALL_CAPS when they are true fixed values; otherwise match Godot/node conventions used nearby.
  • “Private” / internal: leading underscore _like_this for members meant as implementation detail (not enforced by the language).

Types and API

  • Add static types on parameters, locals, and return values where reasonable (-> void, var x: int, etc.).
  • Prefer @export (Godot 4) over legacy export syntax.
  • Use @onready for node references set after the scene tree is ready when that pattern fits.

Formatting

  • One statement per line; CI enforces max line length = 100.
  • Indentation: use tabs for new Godot work (editor default). Do not mix tabs and spaces in the same file; match the file if it already uses spaces.
  • Blank lines: separate functions with two blank lines; sparing single blank lines inside functions for logical groups.
  • Trailing commas in multi-line collections/function args when it improves diffs.
  • No trailing whitespace.

Structure

  • class_name only when the type must be referenced globally; otherwise anonymous extends is fine.
  • Declaration order should match gdlint class-definitions-order: @toolclass_nameextends → file/class docstrings → signalenumconst → static vars → @export vars → public vars → private vars (_foo) → @onready public vars → @onready private vars → everything else.
  • Virtual overrides (_ready, _process, _physics_process, etc.): keep small; extract helpers with leading _.
  • Scene / main script shape: For client/, follow godot-client-script-organization — thin main.gd, split picking, networking, and similar concerns into child scripts.

CI-enforced lint rules

  • Names: keep functions, local vars, loop vars, arguments, signals, and class vars in snake_case; keep classes / enums in PascalCase; keep enum elements and true constants in ALL_CAPS.
  • Signal callbacks: _on_Node_signal style handlers are allowed; otherwise use normal snake_case.
  • Preload / load identifiers: when storing a preloaded script or type-like handle, prefer PascalCase names such as CameraStateScript; keep ordinary constants in ALL_CAPS.
  • Avoid duplicated preload()s for the same path in one file.
  • Avoid bare expressions that are not assigned / returned / awaited.
  • Remove unnecessary pass.
  • Keep files reasonably small: CI enforces max 1000 lines, max 20 public methods, max 10 function arguments, and max 6 returns per function.
  • Prefer early-return style. gdlint forbids else / elif branches that directly follow a return.
  • Keep unused arguments intentional and named clearly if you truly need them.

Local verification

  • Before pushing GDScript changes, run the same tools CI uses: pip install "gdtoolkit==4.5.0" gdlint client/scripts client/test gdformat --check client/scripts client/test
  • If formatting fails, run: gdformat client/scripts client/test
  • Install the repos local pre-push hook from the repo root to enforce those checks when client/scripts/ or client/test/ .gd files change in the push: ./scripts/install-git-hooks.sh

GdUnit test layout (AAA)

Mandatory for every new or changed test function in client/test/**/*.gd (GdUnit suites, test_* methods).

  1. # Arrange, # Act, and # Assert — each appears once per test, in that order, as a full-line comment at the same indentation as the test body. No blank line is required after those comments; the next statement may follow immediately.

  2. Arrange — Transports, clients, enqueue/connect setup, test data.

  3. Act — The call(s) under test only (e.g. request_cast, call into SUT). No assert_that / assert_* in Act.

  4. Assert — All expectations; parse or inspect results here when parsing exists only to support assertions.

gdlint / gdformat do not enforce AAA text; review and agents do. Match the spirit of server xut snippets: three labeled phases, one scenario per test.

Comments

  • Use # comments; document non-obvious why, not what the next line literally does.
  • Doc comments: Godot 4 supports ## above declarations for the in-editor help—use for public-ish APIs when helpful.

Anti-patterns

  • Avoid get_node() string paths repeated everywhere; use @onready, exports, or groups when scale demands it.
  • Prefer typed collections and enums over “magic” string/int state when it stays readable.