Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ It is my fervent wish that this file guide every AI coding agent working with co

## Documentation

`docs/internals.md` is the source of truth for how DressCode works: the tree as a rule sees it, the mutation API, the engine of gaps and the rules. Read it before any non-trivial change.
`docs/internals.md` is the source of truth for how DressCode works: the tree as a rule sees it, the mutation API, the engine of gaps, the rules and the interop. Read it before any non-trivial change. `docs/skills/writing-rules.md` is the procedure of writing a rule or porting one from another tool, the one an agent outside this repository follows too.

## Project overview

DressCode is a PHP code style checker and fixer built on a **lossless concrete syntax tree**: every token of the source is in the tree, whitespace and comments are trivia attached to tokens, and printing the tree reproduces the input byte for byte.

The tree itself is the `phpsyntax/phpsyntax` library (namespace `PhpSyntax`), developed in a repository of its own. This one holds `DressCode` (`src/`): engine, rules API, the library of generic rules, configuration, CLI.
The tree itself is the `phpsyntax/phpsyntax` library (namespace `PhpSyntax`), developed in a repository of its own. This one holds `DressCode` (`src/`): engine, rules API, the library of generic rules, configuration, CLI. The names of other tools live only in `Interop/`, never in a rule.

Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs from it is public API for plugins too. What the rules only share among themselves (`Rules\NodeHelpers`, `Rules\Compiler` and the helpers beside the rules that carry `@internal`) is `@internal`; the grammar of a map of members (`Upgrading\MemberMapGrammar`, the map itself `MemberMap` with `findAccess()`, its `MemberMapEntry` and `MemberMapMatch`, `MemberPattern`, `ArgumentPattern`, `FunctionPattern`) is not, a rule shipped by a package being fed the same way, and neither is `GlobalCalls`, which answers which global function a call calls and why that may be another one, nor `CodeWriter`, which writes code in the shape of the file (a class written the way the file reaches it, an import added the way the file writes its imports) for a rule of a package as for one of the core. Presets define style; DressCode has no style of its own. The three standards carry one: `dresscode/perCs` (PER Coding Style 3.1), `dresscode/psr12` and `dresscode/nette` (the Nette Coding Standard, PER with tabs, the layout of the Nette libraries and their departures, which the repository itself is checked with). What a project wants beyond the looks of its code comes in a set, a preset of one intent that decides nothing about the layout and is used beside the standard (`modernizations`, `deprecations`, `cleanup`, `correctness`, `types`, `compilerOptimizations`); a decision the core gains goes into its set, one that writes a newer construct and the replacement of what PHP deprecated alike into every set whose intent it meets (`upgrading.syntax.firstClassCallables` in `modernizations` and `deprecations`), and what no set holds the project decides itself.
Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs from it is public API for plugins too. What the rules only share among themselves (`Rules\NodeHelpers`, `Rules\Compiler` and the helpers beside the rules that carry `@internal`) is `@internal`; the grammar of a map of members (`Upgrading\MemberMapGrammar`, the map itself `MemberMap` with `findAccess()`, its `MemberMapEntry` and `MemberMapMatch`, `MemberPattern`, `ArgumentPattern`, `FunctionPattern`) is not, a rule shipped by a package being fed the same way, and neither is `GlobalCalls`, which answers which global function a call calls and why that may be another one, nor `CodeWriter`, which writes code in the shape of the file (a class written the way the file reaches it, an import added the way the file writes its imports) for a rule of a package as for one of the core. Presets define style; DressCode has no style of its own. The five standards carry one: `dresscode/perCs` (PER Coding Style 3.1), `dresscode/psr12`, `dresscode/nette` (the Nette Coding Standard, PER with tabs, the layout of the Nette libraries and their departures, which the repository itself is checked with), `dresscode/symfony` and `dresscode/laravel` (the preset laravel of Laravel Pint). What a project wants beyond the looks of its code comes in a set, a preset of one intent that decides nothing about the layout and is used beside the standard (`modernizations`, `deprecations`, `cleanup`, `correctness`, `types`, `compilerOptimizations`); a decision the core gains goes into its set, one that writes a newer construct and the replacement of what PHP deprecated alike into every set whose intent it meets (`upgrading.syntax.firstClassCallables` in `modernizations` and `deprecations`), and what no set holds the project decides itself.

## Essential commands

Expand All @@ -22,6 +22,7 @@ Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs
- `composer reference`: regenerates `docs/reference/decisions.md` (every decision with its values, the values of the standards and the rules declaring it) from the catalogue. Commit the output; CI diffs it.
- `composer api`: regenerates `docs/reference/api.md`, the public classes and members as reflection sees them. Commit the output and read its diff, which is what a change does to the public surface; CI diffs it.
- `php bin/dresscode check`: DressCode over its own sources with `dresscode.neon`; CI runs it too.
- `php bin/dresscode init` in the root of another project: writes the `dresscode.neon` measured from its code, or prints it when a configuration exists; how it measures is in `docs/internals.md`.

## Conventions

Expand Down Expand Up @@ -57,7 +58,7 @@ Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs
- A qualification writes a name imported or qualified only where that has an effect, so `function.optimized` takes a function only in a namespace where PHP optimizes one of its calls, the other functions there being decided by `function.global`.
- Comments only where the code itself is not enough; never restate what the code shows; density follows the surrounding file. No phpDoc for what the types already say.
- Code in a doc comment is written in backticks: a call, a variable or a property, an operator, a keyword, a literal, the case of an enum (`report()`, `$this`, `?->`, `static`, `'*'`, `Severity::Warning`). Doc comments are read as Markdown in an IDE, and what stands bare there is typeset as text: `->` becomes an arrow, `...` an ellipsis. Backticks mark the code itself, so what only resembles code stays bare, whatever the IDE makes of it: a keyword used as a word (a static method, a readonly class, but a `match` expression), `null`, `true` and `false` in "null when …", and the `=>` describing a map (path => its size).
- A framework or library is named in the core only where it is the subject of the code (its preset, a dependency, a file or key another tool defines). An example of a general mechanism uses invented names under the vendor `Acme`, in a domain of their own that mirrors no real API (`Acme\Shop\Order::STATUS_PAID`, `Acme\Mail\SmtpTransport`).
- A framework or library is named in the core only where it is the subject of the code (its preset, its interop, a dependency, a file or key another tool defines). An example of a general mechanism uses invented names under the vendor `Acme`, in a domain of their own that mirrors no real API (`Acme\Shop\Order::STATUS_PAID`, `Acme\Mail\SmtpTransport`).
- Code, comments, identifiers and messages in English.
- A string a `.neon` file has to quote is written in single quotes (`call: 'foo($a, $b)'`), double quotes only where it needs an escape or holds a single quote.

Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dresscode/dresscode",
"description": "👔 DressCode: checks and fixes PHP coding style, and upgrades code to newer PHP and to new versions of libraries. Built on a lossless syntax tree, so it changes only what it touches. PER Coding Style, PSR-12, Nette.",
"description": "👔 DressCode: checks and fixes PHP coding style, and upgrades code to newer PHP and to new versions of libraries. Built on a lossless syntax tree, so it changes only what it touches. PER Coding Style, PSR-12, Nette, Symfony and Laravel.",
"keywords": ["code style", "coding standard", "formatter", "fixer", "linter", "refactoring", "modernization", "upgrade", "deprecations", "psr-12", "per-cs", "php-cs-fixer", "phpcs", "rector"],
"homepage": "https://dresscode.run",
"license": "MIT",
Expand All @@ -12,8 +12,8 @@
],
"require": {
"php": "8.4 - 8.6",
"nette/command-line": "^2.0",
"nette/neon": "^3.4 || ^4.0",
"nette/command-line": "dev-master",
"nette/neon": "^4.0",
"nette/schema": "^1.4 || ^2.0",
"nette/utils": "^4.1",
"phpsyntax/phpsyntax": "^1.0",
Expand Down Expand Up @@ -50,7 +50,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
"dev-master": "1.4-dev"
}
}
}
Loading