Commit c27eebc
authored
* C#: Add .editorconfig-based formatting style detection for RoslynFormatter
Replace the runtime source-text heuristic (FormatStyle.DetectStyle) with a
proper .editorconfig-based style resolver. A new CSharpFormatStyle marker is
attached to each CompilationUnit during parsing and flows through RPC to the
Java side.
- Add CSharpFormatStyle marker (C# + Java) with RPC codecs carrying all
Roslyn formatting options: tabs/spaces, indent size, newline style,
10 brace placement options, 3 keyword newline options, 2 wrapping options
- Add EditorConfigResolver that walks .editorconfig files hierarchically
from file directory up to project root, layering child over parent,
stopping at root=true. Caches resolved styles per directory.
- SolutionParser attaches the resolved CSharpFormatStyle to each CU
- RoslynFormatter reads the marker instead of detecting at format time
and wires all brace/keyword options into CSharpFormattingOptions
- Delete dead FormatStyle class superseded by EditorConfigResolver
* Move CSharpFormatStyle from marker to style package, extend NamedStyles
Follow the established convention where style markers extend NamedStyles
(like PrettierStyle, Autodetect) and live in a .style package rather than
.marker. The CSharpFormatStyle now extends NamedStyles and stores its
configuration directly (empty styles collection), matching the PrettierStyle
pattern for formatters that delegate to a single external tool.
- Move Java class from csharp.marker to csharp.style package
- Extend NamedStyles instead of implementing Marker directly
- Update RPC type name to org.openrewrite.csharp.style.CSharpFormatStyle
- Add style package convention to RpcReceiveQueue type resolution
* Strip :severity suffixes from .editorconfig values
Real-world .editorconfig files use Roslyn severity suffixes like
csharp_new_line_before_open_brace = all:error or
csharp_new_line_before_else = true:suggestion. Strip the :suffix
before processing so these values are correctly recognized.
* Cover all 47 Roslyn CSharpFormattingOptions in CSharpFormatStyle
Expand CSharpFormatStyle to carry every option that Roslyn's
CSharpFormattingOptions exposes, and wire them all through BuildOptions.
Added options:
- Indentation: IndentBlock, IndentBraces, IndentSwitchCaseSection,
IndentSwitchCaseSectionWhenBlock, IndentSwitchSection, LabelPositioning
- New lines: NewLineForClausesInQuery, NewLineForMembersInAnonymousTypes,
NewLineForMembersInObjectInit
- Spacing: all 24 Space* options including SpacingAroundBinaryOperator
(enum mapped as int: 0=before_and_after, 1=ignore, 2=none)
EditorConfigResolver now parses .editorconfig keys for all of these,
including csharp_indent_labels (flush_left/one_less_than_current/no_change),
csharp_space_around_binary_operators (before_and_after/ignore/none), and
csharp_space_around_declaration_statements (ignore/false).
* Pack 47 boolean flags into a single long for compact RPC
Replace 47 individual boolean fields with bit-packed long storage.
Public API is unchanged — all boolean properties remain as computed
accessors (e.g., style.SpaceAfterCast reads bit 22 from the packed
long). RPC now sends 6 fields instead of 52.
Bit positions are assigned in declaration order and are append-only
to maintain wire compatibility. Both C# and Java sides use matching
bit position constants.
* Add DefaultFlags constant for Roslyn default boolean flag values
The packed long has 27 of 47 bits set by default (Allman style,
standard spacing). Making this an explicit constant (0x600033BFFFFAL)
documents the defaults, simplifies the Default instance construction,
and protects against accidental zero-initialization.
* Cache Roslyn OptionSet on CSharpFormatStyle marker instance
The OptionSet built from 51 WithChangedOption calls is now lazily
cached as a transient field on the immutable CSharpFormatStyle marker.
Since markers are shared across files in the same directory, the
cached OptionSet is reused for all format calls on those files.
The field is volatile for safe publication and excluded from RPC
serialization and equality — it's rebuilt on demand after deserialization.
* Add tests verifying Roslyn respects brace placement and spacing options
Two new tests confirm that CSharpFormatStyle options flow through to
Roslyn's formatter correctly:
- K&R brace style (NewLinesForBracesInTypes/Methods=false) produces
braces on same line as declaration
- SpaceAfterCast=true inserts a space after cast expressions
* Default all convenience constructor parameters to Roslyn defaults
All boolean, int, and string parameters in the convenience constructor
now have default values matching Roslyn/VS defaults. Callers only need
to specify the values they want to override, e.g.:
new CSharpFormatStyle(id, newLinesForBracesInTypes: false)
* WhitespaceReconciler: skip mismatched subtrees instead of aborting
When a subtree has a structural mismatch (e.g., a recipe constructed a
J.Identifier where the parser would produce J.Primitive), the reconciler
now skips that subtree — keeping its original whitespace — and continues
reconciling the rest of the tree. Previously, any mismatch aborted the
entire reconciliation and returned the original tree unchanged.
- StructureMismatch now records the mismatch count but does not abort
- Remove all early-exit guards that checked _compatible flag
- Add MismatchCount property and MaxMismatches limit (throws when
exceeded, intended for test assertions)
- RoslynFormatter no longer discards reconciled results on mismatch
The skipped test (SkipsMismatchedSubtreeAndReconcilesSurroundingCode)
demonstrates the intended behavior but needs investigation — the
result propagation doesn't update surrounding whitespace as expected.
* Fix WhitespaceReconciler to continue past mismatches in lists
The last remaining early-exit guard (inside VisitList's for loop)
was preventing siblings from being reconciled after a mismatch in
an earlier sibling. Removing it allows the reconciler to process
all list elements, skipping only the subtrees that actually mismatch.
The new test verifies this: when a recipe replaces J.Primitive("int")
with J.Identifier("int") on the first method's return type, the
second method and its body still get proper indentation from Roslyn.
* Add Validations class for controlling test invariant checks
Introduce Validations as a centralized way to enable/disable test
invariant checks in RewriteRun, following the Java-side TypeValidation
pattern. Tests configure it via RecipeSpec:
spec.SetValidations(new Validations { WhitespaceInSpaces = false })
Current flags:
- WhitespaceInSpaces: validate Space fields contain only whitespace
- PrintEqualsInput: validate round-trip fidelity
- PrintIdempotence: validate re-parse produces identical output
- MaxWhitespaceMismatches: limit for reconciler structural mismatches
All enabled by default. Validations.None disables everything.
* Wire Validations.MaxWhitespaceMismatches through to WhitespaceReconciler
RewriteRun now sets WhitespaceReconciler.DefaultMaxMismatches from the
Validations config before running recipes and restores it after. The
static field defaults to 0 which maps to int.MaxValue (unlimited) in
production. Tests get the Validations.All default of 5.
* Simplify mismatch validation to a boolean AllowWhitespaceMismatches
Replace MaxWhitespaceMismatches (int) with AllowWhitespaceMismatches
(bool). The reconciler now collects mismatches during the full walk
and throws after completion with a report of the first 5 — no early
abort. Tests get ThrowOnMismatch=true by default (via Validations.All).
Production gets ThrowOnMismatch=false (static default).
Tests that expect mismatches disable throwing:
spec.SetValidations(new Validations { AllowWhitespaceMismatches = true })
* Rename AllowWhitespaceMismatches to FormattingReconciliation
Better describes what's being validated: structural compatibility
between recipe output and Roslyn-formatted output during reconciliation.
1 parent 816f6f3 commit c27eebc
14 files changed
Lines changed: 1674 additions & 201 deletions
File tree
- rewrite-csharp
- csharp/OpenRewrite
- CSharp
- Format
- Rpc
- Core/Rpc
- Tests/Format
- Test
- src/main/java/org/openrewrite/csharp/style
Lines changed: 292 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 395 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 0 additions & 113 deletions
This file was deleted.
Lines changed: 66 additions & 23 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
77 | | - | |
78 | | - | |
| 77 | + | |
| 78 | + | |
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
94 | | - | |
95 | | - | |
96 | 94 | | |
97 | 95 | | |
98 | 96 | | |
| |||
110 | 108 | | |
111 | 109 | | |
112 | 110 | | |
113 | | - | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | 117 | | |
121 | 118 | | |
122 | 119 | | |
| |||
251 | 248 | | |
252 | 249 | | |
253 | 250 | | |
254 | | - | |
255 | | - | |
| 251 | + | |
| 252 | + | |
256 | 253 | | |
257 | 254 | | |
258 | 255 | | |
| |||
268 | 265 | | |
269 | 266 | | |
270 | 267 | | |
271 | | - | |
272 | | - | |
273 | 268 | | |
274 | 269 | | |
275 | 270 | | |
| |||
300 | 295 | | |
301 | 296 | | |
302 | 297 | | |
303 | | - | |
304 | | - | |
305 | | - | |
306 | 298 | | |
307 | 299 | | |
308 | 300 | | |
| |||
512 | 504 | | |
513 | 505 | | |
514 | 506 | | |
515 | | - | |
| 507 | + | |
516 | 508 | | |
517 | 509 | | |
518 | 510 | | |
519 | 511 | | |
520 | 512 | | |
521 | | - | |
| 513 | + | |
522 | 514 | | |
523 | 515 | | |
524 | 516 | | |
525 | 517 | | |
526 | 518 | | |
527 | 519 | | |
528 | 520 | | |
529 | | - | |
| 521 | + | |
530 | 522 | | |
531 | 523 | | |
532 | 524 | | |
533 | 525 | | |
534 | 526 | | |
535 | | - | |
| 527 | + | |
536 | 528 | | |
537 | 529 | | |
538 | 530 | | |
| |||
565 | 557 | | |
566 | 558 | | |
567 | 559 | | |
568 | | - | |
| 560 | + | |
569 | 561 | | |
570 | | - | |
| 562 | + | |
571 | 563 | | |
572 | | - | |
573 | | - | |
| 564 | + | |
| 565 | + | |
574 | 566 | | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
575 | 618 | | |
576 | 619 | | |
577 | 620 | | |
| |||
Lines changed: 14 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
30 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
31 | 33 | | |
32 | 34 | | |
33 | 35 | | |
| |||
131 | 133 | | |
132 | 134 | | |
133 | 135 | | |
134 | | - | |
| 136 | + | |
135 | 137 | | |
136 | 138 | | |
137 | 139 | | |
| |||
203 | 205 | | |
204 | 206 | | |
205 | 207 | | |
206 | | - | |
| 208 | + | |
207 | 209 | | |
208 | 210 | | |
209 | 211 | | |
| |||
244 | 246 | | |
245 | 247 | | |
246 | 248 | | |
247 | | - | |
| 249 | + | |
248 | 250 | | |
249 | 251 | | |
250 | 252 | | |
| |||
275 | 277 | | |
276 | 278 | | |
277 | 279 | | |
278 | | - | |
| 280 | + | |
279 | 281 | | |
280 | 282 | | |
281 | 283 | | |
| |||
290 | 292 | | |
291 | 293 | | |
292 | 294 | | |
293 | | - | |
| 295 | + | |
294 | 296 | | |
295 | 297 | | |
296 | 298 | | |
| |||
326 | 328 | | |
327 | 329 | | |
328 | 330 | | |
329 | | - | |
| 331 | + | |
330 | 332 | | |
331 | 333 | | |
332 | 334 | | |
| |||
346 | 348 | | |
347 | 349 | | |
348 | 350 | | |
349 | | - | |
| 351 | + | |
350 | 352 | | |
351 | 353 | | |
352 | 354 | | |
| |||
366 | 368 | | |
367 | 369 | | |
368 | 370 | | |
369 | | - | |
| 371 | + | |
370 | 372 | | |
371 | 373 | | |
372 | 374 | | |
| |||
387 | 389 | | |
388 | 390 | | |
389 | 391 | | |
390 | | - | |
| 392 | + | |
391 | 393 | | |
392 | 394 | | |
393 | 395 | | |
| |||
428 | 430 | | |
429 | 431 | | |
430 | 432 | | |
431 | | - | |
| 433 | + | |
432 | 434 | | |
433 | 435 | | |
434 | 436 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
| 127 | + | |
| 128 | + | |
127 | 129 | | |
128 | 130 | | |
129 | 131 | | |
| |||
0 commit comments