Fantomas Documentation
repository·main·Indexed 21 days ago
https://github.com/fsprojects/fantomasAn 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.
What's inside Fantomas
- Fantomas is an opinionated F# source code formatter designed to automatically format F# code according to specific style guidelines. It is distributed via NuGet and can be used as a .NET tool.
Overview of Fantomas projects
mainFantomas is composed of a core library and a command line tool. The main solution file isfantomas.slnx. To run the project's unit tests, an NUnit test runner is required.What is Trivia and how is it handled?
mainIn 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
trivianode ofParsedImplFileInputorParsedSigFileInput. Blank lines are detected by scanning theISourceTextline by line.Insertion
Once collected, trivia is inserted into the
Oaktree. EveryNodein theOakmodel containsContentBeforeandContentAfterproperties, which are used to store and reconstruct these elements during the printing phase.What is the Oak tree model?
mainThe
Oakis the top-level root node of the Fantomas custom tree model. Fantomas maps the untyped AST from the F# compiler toOakto 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
doexpression). - 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.
Understand how Fantomas formats code
mainFantomas 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.Understand Ranges and Positions
mainA Range is a data structure used to model the exact location and size of a node or language construct within the source code. A range is defined by a start and an end position. Each position consists of a line number and a column number.Understand the Writer Event architecture (DLL + Deferred Materialization)
mainFantomas 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
WriterEventobjects to a shared, mutableEventListheld on theContext. 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:
- Metadata Update: As events are appended,
WriterModel.updatetracks state (e.g.,LineCount,Column,Indent) without performing string concatenation. - String Materialization: The
dumpfunction performs a final pass, walking the DLL from head to tail to produce the actual output text.
- Metadata Update: As events are appended,
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:
- Snapshot: Save a reference to the current
Tailof the DLL and the currentWriterModel. - Execute: Run the formatting logic (e.g., a
ShortExpressioncheck). - Restore: If the result is not desired, truncate the DLL back to the saved
Tailand reset theWriterModelto the saved state.
- Mutable Doubly-Linked List (DLL): Instead of an immutable queue, all formatting operations append
Understanding WriterModel and WriterEvents
mainTo 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.
How Fantomas version detection works in Fantomas.Client
mainStarting from version 4.6,
Fantomas.Clientuses a specific priority order to detect and use a compatible Fantomas version. It searches in this order:- Local Project Version: The version of Fantomas used by your local project (as shown by running
dotnet tool listinside the project folder). - Global Version: Your global Fantomas installation (installed via
dotnet tool install fantomas -g). You can verify this withdotnet tool list -g. - PATH Executable: An executable named
fantomasfound in your system'sPATH.
- Local Project Version: The version of Fantomas used by your local project (as shown by running
Use the EventList for efficient event management
mainThe
EventList(EventList.fs) is a mutable doubly-linked list ofEventNodevalues. It is designed for high-performance formatting operations. Because it is a hot path,EventNodeuses[<AllowNullLiteral>]forPrev/Nextlinks instead of the standardoptiontype to reduce overhead.Key Operations
Operation Complexity Purpose 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 Extend AST types using Type Extensions
mainTo 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
.FullRangeis 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 -> mUnderstand the Fantomas modular architecture
mainFantomas 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
.editorconfigand.fantomasignorefiles. - Fantomas.Client: A standalone library designed specifically for editor integration. Instead of calling
Fantomas.Coredirectly, editors use this client to communicate with afantomasdotnet tool. This allows editors to use a specific version of Fantomas independently of the system or other tools.