A Pascal/Delphi source code formatter, written in Delphi 13. Reformats .pas
files according to a configurable rule set: line-length budgeting, structural
re-indentation, capitalization, and column alignment.
YADF ships in three flavours, built from the same engine:
YADF.exe— a stand-alone CLI for formatting files, folders, and whole.dpr/.dprojprojects (see CLI quick start).YADFSetup.exe— a visual settings editor / format playground (see YADFSetup).YADFOT.bpl— a Delphi IDE design-time package ("YADF Open Tools") that adds aHelp → Tools → YADFOT: Format Current Buffermenu item and aCtrl+Shift+Alt+Fshortcut to format the current source-editor buffer (see YADFOT install).
All three artifacts are produced by this single project tree and share one
yadf.ini configuration. build_all.bat builds them together with a single
version stamp.
YADFSetup.exe is a three-column GUI — Settings | Source | Result. Load any
.pas on the left, watch it reformat live on the right, and tune every option
in between. It is the visual front-end for the whole family's configuration:
what you change here is what YADF.exe and YADFOT.bpl use.
- Get the binary. Download
YADFSetup.exefrom the release zip (alongsideYADF.exe,YADFOT.bpl, andDemo\Sample.pas), or build it yourself (see Building). - No installation required. It is a standalone Win32 executable — just run
it. Keep
Demo\Sample.pasnext to it (or in aDemo\subfolder) and it auto-loads on first launch so you immediately see formatting at work. - First run creates the config. If no
yadf.iniexists yet, YADFSetup writes a fully-commented one to%APPDATA%\YADF\yadf.iniand starts editing it (see Where the INI lives).
Heads-up: every change you make autosaves to the shared
yadf.ini, so it immediately changes how the CLI and IDE wizard format from then on. Use Save As… to snapshot a profile before experimenting, and Reset to return to the shipped defaults.
YADFSetup reads and writes the shared per-user config:
%APPDATA%\YADF\yadf.ini
which expands to e.g. C:\Users\<you>\AppData\Roaming\YADF\yadf.ini. This is
the same file YADF.exe and YADFOT.bpl fall back to, so all three tools stay
in sync. (The CLI and IDE wizard can also pick up a project-local yadf.ini
placed next to your source — see the full
INI lookup order. YADFSetup always edits the shared file and
shows the active path at the bottom of the Settings column.)
To keep alternative option sets, use Save As… to export a named .ini
anywhere, and Load Settings… to bring one back later.
The window has three resizable columns (drag the splitters):
- Settings (left) — every formatting option, grouped (Line length,
Reflow & whitespace, Casing, Alignment, Uses, Declarations, Labels, File &
CLI). Hover any control for a one-line explanation. Changing any option
re-formats the Result pane instantly and saves to the shared
yadf.ini.- Load Settings… — import an option profile from any
.ini. - Save As… — export the current options to an
.ini(does not touch the shared file). - Reset — restore the shipped defaults (this does overwrite the shared
yadf.ini; you'll be asked to confirm).
- Load Settings… — import an option profile from any
- Source (middle) — paste Pascal here, or click Open File… to load any
.pas/.dpr/.inc. Edits re-format after a brief pause. - Result (right) — the formatted output, read-only. Copy puts it on the clipboard.
The status line under Settings shows the active INI path and a (saved) marker after each change. The status by the Result pane shows OK or a [Format error] message if the input can't be parsed (the app never crashes on bad input).
Options in the File & CLI group (Backup, BackupDir, ResultDir, Encoding,
Logging) configure the CLI's file handling and have no effect on the live
preview — they're saved for YADF.exe to use.
YADFOT is a design-time, Win32-only Delphi IDE package. The IDE itself is 32-bit, so the BPL must be Win32 even on a 64-bit machine.
- Open
YADFOT.dprojin Delphi 13. - Active platform: Win32, configuration Release (Debug works too).
- Project → Build YADFOT. The output is
Win32\Release\EXE\YADFOT.bpl, right next to whereYADF.exelands. - Component → Install Packages → Add… → browse to that
YADFOT.bpl→ OK.
The wizard registers itself on package load -- no IDE restart needed.
cmd.exe /c "call \"C:\Program Files (x86)\Embarcadero\Studio\37.0\bin\rsvars.bat\" ^
&& msbuild /t:Build /p:Config=Release /p:Platform=Win32 YADFOT.dproj"Then install via the IDE (step 4 above).
Two entry points appear in the IDE after install:
- Menu →
Help→Tools→YADFOT: Format Current Buffer(this is the standard location for IOTAMenuWizard items in the Delphi IDE — installed wizards live under Help → Tools, not directly under Tools). - Keyboard shortcut →
Ctrl + Shift + Alt + F
The shortcut is registered as a Delphi keyboard binding named
YADFOT. If the chord clashes with something on your machine you
can rebind it under Tools → Options → User Interface → Editor → Key Mappings (look for the YADFOT entry), or change it in source
at the call to BindingServices.AddKeyBinding in
YADFOT.Wizard.pas and rebuild.
YADFOT verifies the active edit view is a code buffer before doing anything.
Supported extensions: .pas .dpr .dpk .inc. If the active view is a
form designer, project options page, welcome page, or any other non-code
editor, the wizard reports it and exits without changes -- it deliberately
does not auto-jump to a sibling .pas behind a form. Click into the
code editor first, then invoke the wizard.
The buffer does not need to be saved -- the in-memory contents are read, formatted, and rewritten through an undoable IDE edit (so Ctrl+Z reverts the format).
Encoding. All buffer I/O is UTF-8 by way of the IDE's edit
reader/writer; the on-disk encoding of the file is irrelevant. The CLI's
Encoding INI key (ANSI / UTF-8 / UTF-16) only affects file I/O
and is intentionally ignored by YADFOT.
INI lookup (unified across YADF.exe and YADFOT.bpl since 1.0.2).
Both the CLI and the IDE wizard look for yadf.ini in the same order
(first hit wins) so whichever tool runs first creates the file the other
will find:
- The source file's directory, walking up to 8 parents (project-local override; YADFOT only)
- The active project's directory, walking up to 8 parents (YADFOT only)
- The CLI's current working directory, walking up to 9 parents (YADF.exe only — typically the project root)
- The directory of
YADF.exeitself, walking up to 5 parents (portable- app pattern: drop ayadf.ininext to the EXE) - Legacy
%APPDATA%\YADFOT\yadf.ini(read-only fallback for users upgrading from 1.0.1.x; never written to) %APPDATA%\YADF\yadf.ini— shared per-user fallback. If nothing exists anywhere above, a fully-commented template is written here on first run and used. TypicallyC:\Users\<user>\AppData\Roaming\YADF\yadf.ini.
First-run experience. No INI ships in the release zip. The first time
you run YADF.exe (or save a file with YADFOT registered), an INI is
created at %APPDATA%\YADF\yadf.ini with every option, its default, and
a one-line explanation. Edit values and re-run; or drop a project-local
yadf.ini to override per-project.
The CLI prints Created default config: <path> on the first run that
materialises the template, so you know where to find it.
The [Format] section keys match the CLI keys exactly. The only key the
IDE wizard ignores is Encoding, since IDE buffer I/O is always UTF-8.
Component → Install Packages → select YADFOT → Remove.
yadf MyUnit.pasFormat a single .pas file in place — the source is overwritten with the
formatted output, no backup. This is the default; if you want a copy of the
original kept, opt in:
yadf MyUnit.pas --bIn-place format with a sibling backup MyUnit.pas.BCK1 (then BCK2, BCK3,
...) next to the source. To send backups to a central folder with timestamped
.bak names instead, give --b a folder argument:
yadf MyUnit.pas --b D:\YADF-backupsTo send the formatted output somewhere else and leave the source untouched:
yadf MyUnit.pas --o D:\out\MyUnit.formatted.pas :: write to this exact file
yadf C:\src\*.pas --of D:\formatted :: every result into this folderyadf MyProject.dprFormat the .dpr and every local .pas named in its uses clause. *.pas
wildcards (yadf C:\src\*.pas) and .dproj project files work the same way
— .dproj is parsed for its <DCCReference Include="...pas"/> entries.
yadf -hShows the full option list, every CLI flag, and the current effective
values pulled from yadf.ini.
Layout
- Configurable max line length (default 180), indent step (default 2), tab width (default 4)
- Full reflow: drops user line breaks where structurally non-essential, joins lines that fit, breaks lines that overflow
- Block-aware breaking:
usesclauses, parenthesized argument lists, bracket literals all break to one item per line when needed - Operator-chain breaking: long arithmetic/logical expressions break with the operator leading each new line
- Hanging indent for parens-broken statements: items at
LineWS + 2*Indent, closing paren atLineWS
Indentation
- Re-indents every line based on structural depth, ignoring source whitespace
- Tracks
type/var/constsections,class/record/object/interfacedeclarations,begin/endblocks,case/try/asm - Visibility keyword bonus: members inside
private/public/protected/publishedindent one level deeper if/while/forbody bonus: single-statement bodies afterthen/do/elseindent one level deeper, persisting across multi-line bodies- Case-alternative bonus: bodies after
LABEL:indent one level deeper elsealigns with itsif;else ifchains stay on one line
Capitalization
- Lowercase reserved keywords (
begin,end,if,else,var, ...) - Uppercase hex digits in
$NNnumeric literals and exponent letterE - Uppercase compiler-directive names (
{$IFDEF},{$DEFINE}, ...) - First-occurrence identifier-case normalization (consistent casing throughout the file)
Pass-2 alignment
- Plain align of
:in declarations and=inconstblocks across consecutive lines - Smart align of
:=assignments: when adjacent lines share the same token shape (same sequence of operators/punctuation, identifier names may differ), every common anchor (.,:=,(,,,),;) is padded so the anchors line up - Aligned columns are kept tight: an anchor's spacing rule wins
(member-access
.is glued on both sides, no space before;,:=spacing perAssignNoSpaceBefore/AssignSpaceAfter), and a column is never pushed right by padding that every line in the run shares — a space appears before an anchor only on the shorter lines that need it to reach the shared column
Other
- Always emits CRLF line endings
- Reads and writes file bytes in the encoding chosen by
Encoding=inyadf.ini(defaultANSI;UTF-8-BOMandUTF-16-BOMalso supported). Existing BOM in input is auto-detected on load. (YADFOT bypasses this setting and always exchanges UTF-8 with the IDE buffer.) - Backups are opt-in: in-place formatting overwrites the source unless
you set
Backup=trueinyadf.inior pass--bon the command line. When backups are on, they go to a sibling<source>.BCK<N>(default) or to a folder you specify with--b <folder>(timestamped.bak). - Trailing whitespace stripped on every line
- Consecutive blank lines collapsed to a configurable cap (default 1)
- Optional blank-line insertion before sections, methods, type sections
- Block-end labels: long
begin..endblocks get// while,// procedure, etc. trailing comments - Optional unclosed-block markers: when source has unmatched
begin/record, emit// TODO -oYADF : 'begin' on line N has no matching 'end'so they show up in the IDE's To Do List
Robustness
- The format pipeline is idempotent:
format(format(x)) == format(x) --check <file>verifies byte-faithful round-trip (token stream emits identical bytes back)- Tolerates structurally-broken Pascal source (missing
ends) without crashing
YADF (CLI) and YADFOT (IDE wizard) share a unified lookup hierarchy
documented in detail under YADFOT install → INI lookup
above. Summary: project-local yadf.ini (walked up from the source file,
the active project, or the CLI's cwd), then the EXE's own folder, then
the shared fallback %APPDATA%\YADF\yadf.ini. No INI ships in the
release zip — first run creates a fully-commented template at the
fallback location. CLI flags override INI values; INI values override
compiled-in defaults. See the auto-created yadf.ini for the
documented option list.
YADFOT (IDE wizard) reads the same [Format] keys with a different search
order — see YADFOT install above.
Requires Delphi 13 (RAD Studio 37.0). Depends on
DelphiAST for its lexer; the
project files expect DelphiAST checked out at ..\DelphiAST relative to YADF.
Build the CLI:
cmd /c "call \"C:\Program Files (x86)\Embarcadero\Studio\37.0\bin\rsvars.bat\" ^
&& msbuild /t:Build /p:Config=Release /p:Platform=Win64 YADF.dproj"Build the IDE wizard package:
cmd /c "call \"C:\Program Files (x86)\Embarcadero\Studio\37.0\bin\rsvars.bat\" ^
&& msbuild /t:Build /p:Config=Release /p:Platform=Win32 YADFOT.dproj"Build the settings editor:
cmd /c "call \"C:\Program Files (x86)\Embarcadero\Studio\37.0\bin\rsvars.bat\" ^
&& msbuild /t:Build /p:Config=Release /p:Platform=Win32 YADFSetup.dproj"Or build all three at once with one shared version stamp:
build_all.batbuild_all.bat is the single source of truth for the release version: it sets
YADF_MAJOR/MINOR/RELEASE/BUILD once and stamps the identical FileVersion into
YADF.exe, YADFSetup.exe, and YADFOT.bpl (keep YADF.Version.inc in sync
for the in-app version string). All outputs land in Win32\Release\EXE\
(plus Win64\Release\EXE\ for the CLI); YADFOT.bpl is Win32-only, since the
Delphi IDE is 32-bit, while YADF.exe builds for Win32 and Win64.
YADF\
YADF.Tokens.pas lexer wrapper + token stream
YADF.Options.pas TYadfOptions + YADF_OPTIONS table + INI load/save
YADF.Groups.pas structural group parser (begin/end nesting, etc.)
YADF.Layout.pas FormatSource -- the engine entry point
YADF.Debug.pas debug-tree dump (CLI --debug-tree)
YADF.Version.inc single-source version string (display)
YadfMain.pas CLI argument parser + driver
YADF.dpr / .dproj CLI project
uYADFSetupMain.pas YADFSetup form (3-column playground)
YADFSetup.dpr/.dproj settings-editor GUI project
YADFOT.Wizard.pas IDE wizard (ToolsAPI + buffer I/O + INI loader)
YADFOT.dpk / .dproj IDE design-time package
Demo\Sample.pas feature-demonstration unit (auto-loaded by YADFSetup)
build_all.bat builds all 3 artifacts with one version stamp
yadf.ini shared CLI + wizard + GUI config (optional)
YADF is built on top of DelphiAST
by Roman Yankovsky and contributors. The
lexer (SimpleParser.Lexer) handles all token-level details including modern
Delphi syntax (multi-line strings, inline vars, generic constraints, etc.) and
made this project possible.
To build YADF from source you'll need DelphiAST checked out alongside this repo — clone it from RomanYankovsky/DelphiAST.
DelphiAST is Copyright (c) 2014-2020 Roman Yankovsky (roman@yankovsky.me) et al, released under the Mozilla Public License v2.0.
YADF is released under the Mozilla Public License v2.0. See LICENSE for the
full text. This is the same license as DelphiAST.
Copyright (c) 2026 Alexander Liberov
This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/.