You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -267,6 +278,62 @@ let ``descriptive test name, issue-number`` () =
267
278
- Check WriterEvents to understand formatting decisions
268
279
- Use MCP tools to test formatting in real-time
269
280
281
+
### Understanding Indentation
282
+
283
+
Indentation in Fantomas is **deferred** - it only takes effect after newline events:
284
+
285
+
```fsharp
286
+
// This pattern is very common in CodePrinter:
287
+
indent +> sepNln +> content +> unindent
288
+
289
+
// Or using the helper:
290
+
indentSepNlnUnindent content
291
+
```
292
+
293
+
**Key Points:**
294
+
-`indent` adds an `IndentBy` event to the context
295
+
- The actual indentation only applies when `WriteLine`/`WriteLineBecauseOfTrivia` events are processed
296
+
-`WriterModel.update` in `Context.fs` handles this by setting `Indent = max m.Indent m.AtColumn` on newlines
297
+
- Always pair `indent` with `unindent` to avoid indentation drift
298
+
- Use `atCurrentColumn` and `atCurrentColumnIndent` for fixed column positioning
299
+
300
+
**Example from CodePrinterHelperFunctionsTests.fs:**
301
+
```fsharp
302
+
let g = !-"first line" +> indent +> sepNln +> !-"second line" +> unindent
303
+
// Result: "first line\n second line"
304
+
```
305
+
306
+
### Understanding genNode and Trivia Processing
307
+
308
+
The `genNode` function is crucial for understanding how indentation interacts with trivia:
309
+
310
+
```fsharp
311
+
let genNode<'n when 'n :> Node> (n: 'n) (f: Context -> Context) =
312
+
enterNode n +> recordCursorNode f n +> leaveNode n
313
+
314
+
let enterNode<'n when 'n :> Node> (n: 'n) =
315
+
col sepNone n.ContentBefore (genTrivia n)
316
+
```
317
+
318
+
**Critical Understanding:**
319
+
-`genNode` processes `ContentBefore` trivia first, then runs the function, then `ContentAfter` trivia
320
+
-`genTrivia` handles directives and comments, often emitting `sepNlnForTrivia` events
321
+
-**Trivia processing can override indentation**: When `genTrivia` processes directives, it may emit newline events that apply indentation before your intended content
322
+
- This is why `indentSepNlnUnindent` patterns can fail when nodes have trivia - the trivia emits newlines that consume the indentation
323
+
324
+
**Common Issue Pattern:**
325
+
```fsharp
326
+
// This fails when node has ContentBefore trivia:
327
+
indentSepNlnUnindent (genIdentListNode node)
328
+
// Because genIdentListNode calls genNode, which processes trivia first
329
+
// The trivia emits newlines that apply indentation before the module name
330
+
```
331
+
332
+
**Solution Approaches:**
333
+
1.**Bypass genNode**: Write content directly without trivia processing
334
+
2.**Handle trivia separately**: Process trivia before applying indentation
335
+
3.**Use different indentation strategy**: Apply indentation at a different level in the tree
336
+
270
337
## MCP Tools
271
338
272
339
### Fantomas Format Code Tool
@@ -474,7 +541,7 @@ if not errors.IsEmpty then
474
541
```bash
475
542
# 1. Setup
476
543
git checkout -b fix-3188
477
-
dotnet fsi build.fsx
544
+
dotnet build
478
545
479
546
# 2. Write test
480
547
# Add test to ModuleTests.fs
@@ -484,7 +551,7 @@ dotnet fsi build.fsx
484
551
485
552
# 4. Test and verify
486
553
dotnet test src/Fantomas.Core.Tests/ --filter "3188"
0 commit comments