Loading

Reflow

Reflow is the one layout decision IDE0055 has no opinion about: where to break a line that is too long. dotnet format never wraps. Prettier-style formatters always do, and decide everything else themselves.

max_line_length is where Curb splits the difference:

[*.cs]
max_line_length = 120
		
No max_line_length A width
Line lengths never changed wrapped to the width
Where breaks come from yours, reproduced the tokens and the width
Formatting twice idempotent, by measurement idempotent, by construction
Fixed point of dotnet format 1196/1196 1196/1196

With no width, Curb is a whitespace formatter. It fixes indentation, spacing, brace placement and blank lines, and leaves every line exactly as long as it was.

With a width, Curb owns the layout. Line breaks become a function of your tokens and your width, not of where the previous author happened to press return. A construct either fits on one line or breaks; there is no third state carried over from the file's history.

Two properties hold, and they are different properties.

Idempotencyf(f(x)) = f(x). Formatting twice gives the same result as formatting once. In the no-width mode this is measured on every fixture and on the full corpus. In the width mode it follows from the design: tokens do not move under formatting, so the second run asks the same question of the same input and gets the same answer. The practical consequence is that curb check cannot report a file that curb format just wrote.

Input-independencef(x) = f(y) when tokens(x) = tokens(y). This is the stronger property, and it only holds in the width mode. Two files with different whitespace but identical tokens produce identical formatted output. In formal-methods terms it is closest to confluence — all starting points reach the same normal form — or canonicality. Without a width, csharp_keep_existing_linebreaks reproduces the breaks the author chose, so the property does not hold; that is deliberate.

The reasoning behind which keys are admissible in each mode is in the git history under docs/contribute/layout-decisions.md.

See Churn for the file and line counts across configurations, and a worked example of what Curb's first run looks like on a repository that is already dotnet format-clean.

[*.cs]
max_line_length = 120
csharp_keep_existing_linebreaks = true
		

Now Curb only breaks lines that are too long, and reproduces the breaks you already have. This is the closest thing to "wrap, but do not touch anything else".

The trade is that this reintroduces the one class of bug the deterministic mode does not have — an option whose answer depends on layout that Curb itself produced. Several ReSharper keys are therefore unavailable here and say so when you set them; see what needs a width.

csharp_keep_existing_linebreaks = false without a max_line_length is refused (CURB1007). With no width nothing is ever too long, so it would join every construct in the file onto one line.

Setting a width on an existing repository is one large commit. curb check will report most of your files on the first run, and the build integration will rewrite them on the first compile.

Nothing forces you to take it in one step. A repository can adopt Curb with no max_line_length — where it is a dotnet format whitespace equivalent — and add a width later, as its own commit, when the churn is convenient.

curb print-config Foo.cs prints every resolved option and says which mode you are in and what selected it. Worth running before the reformatting commit rather than after.

Some ReSharper wrapping keys are only honoured under deterministic layout, and setting one without a width reports CURB1005 rather than going quiet:

Key What it does
csharp_wrap_arguments_style = chop_always every argument on its own line, fit or not
csharp_wrap_object_and_collection_initializer_style = chop_always the same for initializers
csharp_place_method_attribute_on_same_line = if_owner_is_single_line join an attribute to a member that fits on one line — also _field_, _property_, _event_

The reason is not arbitrary. Forcing a construct to wrap moves whatever is nested inside it, and in the preserving mode other rules read that nesting's indentation from your source, so the file formats differently on the second pass. Deterministic layout has no rule that reads indentation from the source, so the keys become admissible there and nowhere else.

Two IDE0055 keys go the other way. csharp_preserve_single_line_blocks and csharp_preserve_single_line_statements ask Curb to keep what you put on one line, which a width takes out of their hands; both report CURB1004 with what they still do. An empty { } stays collapsed either way.