Fantomas Documentation

repository·main·Indexed 21 days ago

https://github.com/fsprojects/fantomas

An opinionated F# source code formatter designed to maintain consistent code style across projects. It is distributed as a .NET tool via NuGet and utilizes a two-phase formatting process involving event generation and string materialization. The tool supports complex F# features, including conditional compilation directives through a multi-pass formatting and fragment-merging strategy.

Tokens
21.9K
Snippets
57
Records
115
Agent score
70%

What's inside Fantomas

  1. What is Trivia and how is it handled?

    main

    In Fantomas, Trivia refers to elements that are not part of the core logic of the AST but are essential for reconstructing the original source code:

    • Blank lines
    • Code comments
    • Conditional directives

    Detection

    Comments and conditional directives are detected within the trivia node of ParsedImplFileInput or ParsedSigFileInput. Blank lines are detected by scanning the ISourceText line by line.

    Insertion

    Once collected, trivia is inserted into the Oak tree. Every Node in the Oak model contains ContentBefore and ContentAfter properties, which are used to store and reconstruct these elements during the printing phase.

  2. What is the Oak tree model?

    main

    The Oak is the top-level root node of the Fantomas custom tree model. Fantomas maps the untyped AST from the F# compiler to Oak to provide a more flexible and optimized structure for code reconstruction.

    Key characteristics of the Oak model:

    • Unified Model: It does not differentiate between implementation files and signature files, allowing for code reuse in the printer.
    • Simplified Nodes: Some AST nodes are combined (e.g., a top-level attribute is linked to its sibling do expression).
    • Optimized Types: Recursive types are treated as top-level types, and certain impossible AST combinations are excluded.
    • Accurate Ranges: Node ranges are recalculated for higher precision during the mapping process.
  3. Understand how Fantomas formats code

    main
    Fantomas uses an opinionated approach to code formatting. Instead of performing incremental edits, it rewrites the entire source text from scratch according to its internal rules. This ensures complete consistency and strict adherence to the chosen style guide, but it means Fantomas makes all formatting decisions rather than just modifying your existing whitespace or indentation.
  4. Understand the Writer Event architecture (DLL + Deferred Materialization)

    main

    Fantomas uses a two-phase approach to code generation to solve issues with trailing trivia (comments) and speculative formatting (checking if an expression fits on one line).

    Core Concepts

    • Mutable Doubly-Linked List (DLL): Instead of an immutable queue, all formatting operations append WriterEvent objects to a shared, mutable EventList held on the Context. This allows for O(1) insertions, deletions, and rewinds.
    • Deferred Materialization: String building is deferred until the very end. During the formatting phase, the system only updates lightweight metadata (line count, column, indent) in the WriterModel.
    • Two-Phase Processing:
      1. Metadata Update: As events are appended, WriterModel.update tracks state (e.g., LineCount, Column, Indent) without performing string concatenation.
      2. String Materialization: The dump function performs a final pass, walking the DLL from head to tail to produce the actual output text.

    Speculative Execution via Snapshot/Restore

    To check if a piece of code fits a certain width without permanently altering the output, the system uses a Snapshot/Restore pattern:

    1. Snapshot: Save a reference to the current Tail of the DLL and the current WriterModel.
    2. Execute: Run the formatting logic (e.g., a ShortExpression check).
    3. Restore: If the result is not desired, truncate the DLL back to the saved Tail and reset the WriterModel to the saved state.
  5. Understanding WriterModel and WriterEvents

    main

    To manage formatting without the overhead of constant string manipulation, Fantomas uses two primary abstractions:

    • WriterEvents: A collection of events captured during traversal. This allows for non-destructive formatting where the engine can revert changes if layout constraints (like line length) are violated.
    • WriterModel: A record that tracks lightweight metadata about the current formatting state, such as line count, column position, and indentation level. This allows the engine to make layout decisions (e.g., whether to wrap a line) without building the actual strings first.
  6. How Fantomas version detection works in Fantomas.Client

    main

    Starting from version 4.6, Fantomas.Client uses a specific priority order to detect and use a compatible Fantomas version. It searches in this order:

    1. Local Project Version: The version of Fantomas used by your local project (as shown by running dotnet tool list inside the project folder).
    2. Global Version: Your global Fantomas installation (installed via dotnet tool install fantomas -g). You can verify this with dotnet tool list -g.
    3. PATH Executable: An executable named fantomas found in your system's PATH.
  7. Use the EventList for efficient event management

    main

    The EventList (EventList.fs) is a mutable doubly-linked list of EventNode values. It is designed for high-performance formatting operations. Because it is a hot path, EventNode uses [<AllowNullLiteral>] for Prev/Next links instead of the standard option type to reduce overhead.

    Key Operations

    OperationComplexityPurpose
    AppendO(1)Adding events during formatting
    InsertBefore / InsertAfterO(1)Splicing indent/unindent before trivia
    RemoveO(1)Removing events (e.g., trailing newline in addFinalNewline)
    CreateBackupPointO(1)Saving the tail position before speculative formatting
    RollbackToO(1)Discarding events appended after a backup point
    ToSeq / ToRevSeqO(n)Iterating forward/backward for inspection
    CurrentLineContentO(k)Walking backward to collect text on the current line
  8. Extend AST types using Type Extensions

    main

    To add functionality to existing F# syntax tree types without modifying the original definitions, Fantomas uses Type Extensions. This is often used to expose information that the original type does not provide, such as range information.

    Naming Convention: The suffix .FullRange is used to indicate that the extension provides a range that is either more complete than the original or compensates for a missing range.

    // Example of a type extension in Trivia.fs
    type CommentTrivia with
    
        member x.Range =
            match x with
            | CommentTrivia.BlockComment m
            | CommentTrivia.LineComment m -> m
  9. Understand the Fantomas modular architecture

    main

    Fantomas is organized into several distinct modules depending on your use case (library usage, CLI usage, or editor integration):

    • Fantomas.FCS: A custom fork of the F# compiler parser. It provides a single parse function to construct an untyped syntax tree. Note that while the AST looks identical to the official F# compiler, it is not binary compatible and may contain a newer version of the syntax tree.
    • Fantomas.Core: The central engine that reconstructs source code from the AST. This is the primary module if you want to use Fantomas as a library.
    • Fantomas: The command-line application (CLI). It handles high-level tasks like processing .editorconfig and .fantomasignore files.
    • Fantomas.Client: A standalone library designed specifically for editor integration. Instead of calling Fantomas.Core directly, editors use this client to communicate with a fantomas dotnet tool. This allows editors to use a specific version of Fantomas independently of the system or other tools.