.clang-format at the repository root is authoritative for C/C++. It is a
Linux-kernel/git-derived style:
| Setting | Value |
|---|---|
| Indentation | hard tabs, width 8 (UseTab: Always) |
| Braces | Allman (BreakBeforeBraces: Allman) |
| Line length | 100 columns |
| Pointers | bound to the name — char *p |
| Includes | never reordered (SortIncludes: false) |
| Comments | never reflowed (ReflowComments: false) |
| Language standard | c++20, matching src/Makefile |
ForEachMacros teaches clang-format about LOOP_THRU_PEOPLE,
LOOP_EVENTS_CH, and LOOP_EVENTS_OBJ, so those are spaced like control
statements rather than calls.
Requirement
clang-format 14 or later (18 is what this tree is validated against):
sudo apt-get install clang-format # Debian/Ubuntu; ships git-clang-format too
It is also in packaging/duris-build-deps.equivs.
Formatting your changes
./scripts/format.sh # format the lines you changed, in place
./scripts/format.sh --check # report only; exit 1 if anything is off
./scripts/format.sh --staged # format staged lines directly in the index
./scripts/format.sh --rev origin/master
Without --all, the script wraps git clang-format, so it rewrites only
lines your diff touches. --staged applies formatting directly to the index
and mirrors it into the working tree when that can be done without conflicting
with unstaged edits. --check reports without changing anything.
Pre-commit hook
./scripts/install-hooks.sh # once per clone
./scripts/install-hooks.sh --uninstall # revert
This sets core.hooksPath to the versioned scripts/git-hooks/, whose
pre-commit auto-formats staged C/C++ lines directly in the index before the
commit is created. It does not stage unrelated worktree edits; when a partially
staged file prevents the same formatting patch from being mirrored safely, the
working tree is left alone. Hooks are per-clone git config, so each checkout
runs the installer once.
- Bypass a single commit with
git commit --no-verify. - If clang-format is not installed, the hook warns and lets the commit through rather than blocking work.
core.hooksPathreplaces.git/hookswholesale; the installer warns if you already have hooks there.
The whole tree is formatted
Every tracked C/C++ file under src/, migrations/tools/, areas/src/, and
tests/async/ matches .clang-format, and --all --check verifies that in
about 12 seconds:
./scripts/format.sh --all # re-format everything, iterated to a fixpoint
./scripts/format.sh --all --check # verify the tree; exit 1 and name offenders
Day to day you still want the default (changed lines only) — a full pass in a feature branch buries the real change in review. Reformat-only work belongs in its own commit, never mixed with a behavior change.
Things that fight the formatter
- clang-format is not idempotent here. A second pass can rewrite more than
the first.
--alliterates until a pass changes nothing, and fails loudly if five passes are not enough. - One statement oscillates forever.
do_vote()insrc/cmd/actnew.chas literal tab characters inside anfprintfformat string, which breaks clang-format's column arithmetic. It is fenced with// clang-format off. - Constant defines with long trailing comments. clang-format wrapped 23 of
them so the value landed on a backslash continuation line
(
#define MAX_TRADE \/16 /* ... */), which is unreadable and breaks every tool that scans headers for#define NAME VALUE. Their explanations now precede the one-line defines. Move a long comment above its define; fence the region only when restructuring is not practical.
When you hit a construct the formatter mangles, fence it the same way and say why in a comment. Do not disable the option globally.
Editor setup
Point your editor at the repo's .clang-format and enable format-on-save for
modified lines only — whole-file format-on-save will mangle legacy files.
- VS Code: C/C++ or clangd extension,
"C_Cpp.formatting": "clangFormat","editor.formatOnSaveMode": "modifications". - Vim/Neovim:
clang-format.pymapped to a key, orvim-clang-formatwithg:clang_format#detect_style_file = 1. - Emacs:
clang-format-regionon the region you edited.
History
The config shipped with two keys no released clang-format understands,
BreakAfterOpenBracketBracedList and BreakBeforeCloseBracketBracedList,
which made every clang-format invocation fail with
Error reading .clang-format: Invalid argument. Removing them exposed two more
problems, both found by actually formatting the tree:
RemoveBracesLLVM: truestripped the braces from single-statementif/else. TheADD_BYTE/ADD_SHORTmacros infiles.hexpand to a brace block that is notdo { } while (0)wrapped, soif (x) ADD_BYTE(...); else ...becameif (x) { ... }; elseand stopped compiling.AlignConsecutiveAssignments/Declarationspadded columns with tabs (they only line up at tab width 8), and clang-format chose different columns for a whole-file pass than for the line rangesgit clang-formatformats — so the hook rejected a fully formatted tree. Both are off now, as in the kernel, LLVM, and Google styles.
The deprecated boolean spellings (AlignOperands: true, ...) were also
replaced with the enum forms they map to, so the config stays valid as
clang-format retires the legacy aliases.
Removing the alignment padding had one side effect worth knowing: several
tests used to locate a function definition by its bare signature, and only
matched the definition because the forward declaration was column-padded and
therefore spelled differently. Anchor on signature\n{ instead.