Create a .NET tool manifest to install tools locally
dotnet new tool-manifest
Install the command line tool with:
dotnet tool install fantomas-tool
or install the tool globally with
dotnet tool install -g fantomas-tool
For the overview how to use the tool, you can type the command
dotnet fantomas --help
USAGE: dotnet fantomas [--help] [--recurse] [--force] [--profile] [--fsi <string>] [--stdin] [--stdout] [--out <string>] [--check] [--daemon] [--version] [<string>...]
INPUT:
<string>... Input paths: can be multiple folders or files with *.fs,*.fsi,*.fsx,*.ml,*.mli extension.
OPTIONS:
--recurse, -r Process the input folder recursively.
--force Print the source unchanged if it cannot be parsed correctly.
--profile Print performance profiling information.
--fsi <string> Read F# source from stdin as F# signatures.
--stdin Read F# source from standard input.
--stdout Write the formatted source code to standard output.
--out <string> Give a valid path for files/folders. Files should have .fs, .fsx, .fsi, .ml or .mli extension only.
--check Don't format files, just check if they have changed. Exits with 0 if it's formatted correctly, with 1 if some files need formatting and 99 if there was an internal error
--daemon Daemon mode, launches an LSP-like server to can be used by editor tooling.
--version, -v Displays the version of Fantomas
--help display this list of options.
You have to specify an input path and optionally an output path.
The output path is prompted by --out
e.g.
dotnet fantomas ../../../../tests/stackexchange/array.fs --out ../../../../tests/stackexchange_output/array.fs
Both paths have to be files or folders at the same time.
If they are folders, the structure of input folder will be reflected in the output one.
The tool will explore the input folder recursively if you set --recurse
option.
If you omit the output path, Fantomas will overwrite the input files.
starting version 3.3
Verify that a single file or folder was formatted correctly.
dotnet fantomas --check Source.fs
This will verify if the file Source.fs
still needs formatting.
If it does, the process will return exit code 99.
In the case that the file does not require any formatting, exit code 0 is returned.
Unexpected errors will return exit code 1.
This scenario is meant to be executed in a continuous integration environment, to enforce that the newly added code was formatted correctly.
starting version 4.5
Multiple paths can be passed as last argument, these can be both files and folders.
This cannot be combined with the --out
and --stdout
flags.
When combined with the --recurse
flag, all passed folders will be processed recursively.
One interesting use-case of passing down multiple paths is that you can easily control the selection and filtering of paths from the current shell.
Consider the following PowerShell scripts:
# Create an array with paths
$files =
Get-ChildItem src/*.fs -Recurse # Find all *.fs files in src,
| Where-Object { $_.FullName -notlike "*obj*" } # ignore files in the `obj` folder
| ForEach-Object { $_.FullName } # and select the full path name.
& "dotnet" "fantomas" $files
# Filter all added and modified files in git
$files = git status --porcelain | Where-Object { $_ -match "^\s?A?M(.*)\.fs(x|i)?$" } | ForEach-Object { $_.TrimStart("AM").TrimStart(" ", "M") }
& "dotnet" "fantomas" $files
Or usage with find
on unix:
find my-project/ -type f -name "*.fs" -not -path "*obj*" | xargs dotnet fantomas --check
--daemon
should not be used directly by end-users. Learn more about this feature in the Daemon mode documentation
Fantomas ships with a series of format options. These can be stored in an .editorconfig file and will be picked up automatically by the commandline tool.
A default .editorconfig file would look like
[*.{fs,fsx}]
indent_size=4
max_line_length=120
end_of_line=crlf
insert_final_newline=true
fsharp_semicolon_at_end_of_line=false
fsharp_space_before_parameter=true
fsharp_space_before_lowercase_invocation=true
fsharp_space_before_uppercase_invocation=false
fsharp_space_before_class_constructor=false
fsharp_space_before_member=false
fsharp_space_before_colon=false
fsharp_space_after_comma=true
fsharp_space_before_semicolon=false
fsharp_space_after_semicolon=true
fsharp_indent_on_try_with=false
fsharp_space_around_delimiter=true
fsharp_max_if_then_else_short_width=40
fsharp_max_infix_operator_expression=50
fsharp_max_record_width=40
fsharp_max_record_number_of_items=1
fsharp_record_multiline_formatter=character_width
fsharp_max_array_or_list_width=40
fsharp_max_array_or_list_number_of_items=1
fsharp_array_or_list_multiline_formatter=character_width
fsharp_max_value_binding_width=80
fsharp_max_function_binding_width=40
fsharp_max_dot_get_expression_width=50
fsharp_multiline_block_brackets_on_same_column=false
fsharp_newline_between_type_definition_and_members=false
fsharp_keep_if_then_in_same_line=false
fsharp_max_elmish_width=40
fsharp_single_argument_web_mode=false
fsharp_align_function_signature_to_indentation=false
fsharp_alternative_long_member_definitions=false
fsharp_multi_line_lambda_closing_newline=false
fsharp_disable_elmish_syntax=false
fsharp_keep_indent_in_branch=false
fsharp_blank_lines_around_nested_multiline_expressions=true
fsharp_bar_before_discriminated_union_declaration=false
fsharp_strict_mode=false
Please note that you should only add settings to the .editorconfig
file when you want to deviate from the default settings.
Copying the entire list above is unnecessary.
indent_size
has to be between 1 and 10.
This preference sets the indentation The common values are 2 and 4. The same indentation is ensured to be consistent in a source file. Default = 4.
defaultConfig
let inline selectRandom (f: _ []) =
let r = random 1.0
let rec find =
function
| 0 -> fst f.[0]
| n when r < snd f.[n] -> fst f.[n]
| n -> find (n - 1)
find <| f.Length - 1
{ defaultConfig with IdentSize = 2 }
let inline selectRandom (f: _ []) =
let r = random 1.0
let rec find =
function
| 0 -> fst f.[0]
| n when r < snd f.[n] -> fst f.[n]
| n -> find (n - 1)
find <| f.Length - 1
max_line_length
has to be an integer greater or equal to 60.
This preference sets the column where we break F# constructs into new lines.
Default = 120.
defaultConfig
match myValue with
| Some foo -> someLongFunctionNameThatWillTakeFooAndReturnsUnit foo
| None -> printfn "nothing"
{ defaultConfig with MaxLineLength = 60 }
match myValue with
| Some foo ->
someLongFunctionNameThatWillTakeFooAndReturnsUnit foo
| None -> printfn "nothing"
end_of_line
determines the newline character, lf
will add \n
where crlf
will add \r\n
.
cr
is not supported by the F# language spec.
If not set by the user, the default value is determined by System.Environment.NewLine
.
Adds a final newline character at the end of the file. Default = true
defaultConfig
let a = 42
{ default with InsertFinalNewline = false }
let a = 42
Add semicolons at the end of lines. Default = false.
defaultConfig
let saturn =
{ X = 8.343366718
Y = 4.124798564
Z = -0.4035234171
VX = -0.002767425107 * daysPerYear
VY = 0.004998528012 * daysPerYear
VZ = 2.304172976e-05 * daysPerYear
Mass = 0.0002858859807 * solarMass }
{ defaultConfig with SemicolonAtEndOfLine = true }
let saturn =
{ X = 8.343366718;
Y = 4.124798564;
Z = -0.4035234171;
VX = -0.002767425107 * daysPerYear;
VY = 0.004998528012 * daysPerYear;
VZ = 2.304172976e-05 * daysPerYear;
Mass = 0.0002858859807 * solarMass }
Add a space after the name of a function and before the opening parenthesis of the first parameter. This setting influences function definitions. Default = true.
defaultConfig
let value (a: int) = x
let DumpTrace () = ()
{ defaultConfig with SpaceBeforeParameter = false }
let value(a: int) = x
let DumpTrace() = ()
Add a space after the name of a lowercased function and before the opening parenthesis of the first argument. This setting influences function invocation. Default = true.
defaultConfig
value (a, b)
startTimer ()
{ defaultConfig with SpaceBeforeLowercaseInvocation = false }
value(a, b)
startTimer()
Add a space after the name of a uppercased function and before the opening parenthesis of the first argument. This setting influences function invocation. Default = false.
defaultConfig
Value(a, b)
person.ToString()
{ defaultConfig with SpaceBeforeUppercaseInvocation = true }
Value (a, b)
person.ToString ()
Add a space after a type name and before the class constructor. Default = false.
defaultConfig
type Person() =
class
end
{ defaultConfig with SpaceBeforeClassConstructor = true }
type Person () =
class
end
Add a space after a member name and before the opening parenthesis of the first parameter. Default = false.
defaultConfig
type Person() =
member this.Walk(distance: int) = ()
member this.Sleep() = ignore
member __.singAlong() = ()
member __.swim(duration: TimeSpan) = ()
{ defaultConfig with SpaceBeforeMember = true }
type Person() =
member this.Walk (distance: int) = ()
member this.Sleep () = ignore
member __.singAlong () = ()
member __.swim (duration: TimeSpan) = ()
Add a space before :
. Please note that not every :
is controlled by this setting.
Default = false.
defaultConfig
type Point = { x: int; y: int }
let myValue: int = 42 // See https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#right-pad-value-and-function-argument-type-annotations
let update (msg: Msg) (model: Model) : Model = model // See https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#surround-return-type-annotations-with-white-space
{ defaultConfig with SpaceBeforeColon = true }
type Point = { x : int; y : int }
let myValue : int = 42
let update (msg : Msg) (model : Model) : Model = model
Adds a space after ,
in tuples.
Default = true.
defaultConfig
myValue.SomeFunction(foo, bar, somethingElse)
(a, b, c)
{ defaultConfig with SpaceAfterComma = false }
myValue.SomeFunction(foo,bar,somethingElse)
(a,b,c)
Adds a space before ;
in records, arrays, lists, etc.
Default = false.
defaultConfig
let a = [ 1; 2; 3 ]
let b = [| foo; bar |]
type C = { X: int; Y: int }
{ defaultConfig with SpaceBeforeSemicolon = true }
let a = [ 1 ; 2 ; 3 ]
let b = [| foo ; bar |]
type C = { X: int ; Y: int }
Adds a space after ;
in records, arrays, lists, etc.
Default = true.
defaultConfig
let a = [ 1; 2; 3 ]
let b = [| foo; bar |]
type C = { X: int; Y: int }
{ defaultConfig with SpaceAfterSemicolon = false }
let a = [ 1;2;3 ]
let b = [| foo;bar |]
type C = { X: int;Y: int }
Adds an extra indent to the with
block of a try/with expression.
Default = false.
defaultConfig
try
if System.DateTime.Now.Second % 3 = 0
then raise (new System.Exception())
else raise (new System.ApplicationException())
with
| :? System.ApplicationException -> printfn "A second that was not a multiple of 3"
| _ -> printfn "A second that was a multiple of 3"
{ defaultConfig with IndentOnTryWith = true }
try
if System.DateTime.Now.Second % 3 = 0
then raise (new System.Exception())
else raise (new System.ApplicationException())
with
| :? System.ApplicationException -> printfn "A second that was not a multiple of 3"
| _ -> printfn "A second that was a multiple of 3"
Adds a space around delimiters like [
,[|
,{
.
Default = true.
defaultConfig
let a = [ 1;2;3 ]
let b = [| 4;5;6 |]
{ defaultConfig with SpaceAroundDelimiter = false }
let a = [1;2;3]
let b = [|4;5;6|]
Fantomas by default follows the if/then/else conventions listed in the Microsoft F# style guide. There is stated:
Indentation of conditionals depends on the size and complexity of the expressions that make them up. Write them on one line when: cond, e1, and e2 are short e1 and e2 are not if/then/else expressions themselves.
This setting facilitates this by determining the maximum character width where the if/then/else expression stays in one line. Default = 40.
defaultConfig
if myCheck then truth else bogus
{ defaultConfig with MaxIfThenElseShortWidth = 10 }
if myCheck then
truth
else
bogus
Control the maximum length for which infix expression can be on one line. Default = 50.
defaultConfig
let WebApp =
route "/ping" >=> authorized >=> text "pong"
{ defaultConfig with MaxInfixOperatorExpression = 20 }
let WebApp =
route "/ping"
>=> authorized
>=> text "pong"
Control the maximum width for which records should be in one line. Default = 40.
Requires fsharp_record_multiline_formatter
to be character_width
to take
effect.
defaultConfig
type MyRecord = { X: int; Y: int; Length: int }
let myInstance = { X = 10; Y = 20; Length = 90 }
{ defaultConfig with MaxRecordWidth = 20 }
type MyRecord =
{ X: int
Y: int
Length: int }
let myInstance =
{ X = 10
Y = 20
Length = 90 }
Control the maximum number of fields for which records should be in one line.
Default = 1. Requires fsharp_record_multiline_formatter
to be
number_of_items
to take effect.
defaultConfig
type R = { x: int }
type S = { x: int; y: string }
type T = { x: int; y: string; z: float }
let myRecord = { r = 3 }
let myRecord' = { r with x = 3 }
let myRecord'' = { r with x = 3; y = "hello" }
let myRecord''' = { r with x = 3; y = "hello"; z = 0.0 }
{ defaultConfig with MaxRecordSize = 2; RecordMultilineFormatter = MultilineFormatterType.NumberOfItems }
type R = { x: int }
type S = { x: int; y: string }
type T =
{ x: int
y: string
z: float }
let myRecord = { r = 3 }
let myRecord' = { r with x = 3 }
let myRecord'' = { r with x = 3; y = "hello" }
let myRecord''' =
{ r with
x = 3
y = "hello"
z = 0.0 }
Split records expressions/statements into multiple lines based on the given
condition. character_width
uses character count of the expression, controlled
by fsharp_max_record_width
. number_of_items
uses the number of fields in the
record, controlled by fsharp_max_record_number_of_items
. Default =
character_width
. Note that in either case, record expressions/statements are
still governed by max_line_length
.
defaultConfig
type R = { x: int }
type S = { x: int; y: string }
let myRecord = { r = 3 }
let myRecord' = { r with x = 3 }
let myRecord'' = { r with x = 3; y = "hello" }
{ defaultConfig with RecordMultilineFormatter = MultilineFormatterType.NumberOfItems }
type R = { x: int }
type S =
{ x: int
y: string }
let myRecord = { x = 3 }
let myRecord' = { r with x = 3 }
let myRecord'' =
{ r with
x = 3
y = "hello" }
Control the maximum width for which lists and arrays can be in one line. Default
= 40. Requires fsharp_array_or_list_multiline_formatter
to be
character_width
to take effect.
defaultConfig
let myArray = [| one; two; three |]
{ defaultConfig with MaxArrayOrListWidth = 20 }
let myArray =
[| one
two
three |]
Control the maximum number of elements for which lists and arrays can be in
one line. Default = 1. Requires fsharp_array_or_list_multiline_formatter
to be
number_of_items
to take effect.
defaultConfig
let myList = [ one; two ]
let myArray = [| one; two; three |]
{ defaultConfig with MaxArrayOrListNumberOfItems = 2; ArrayOrListMultilineFormatter = MultilineFormatterType.NumberOfItems }
let myList = [ one; two ]
let myArray =
[| one
two
three |]
Split arrays and lists into multiple lines based on the given condition.
character_width
uses character count of the expression, controlled by
fsharp_max_array_or_list_width
. number_of_items
uses the number of elements
in the array or list, controlled by fsharp_max_array_or_list_number_of_items
.
Default = character_width
. Note that in either case, list expressions are
still governed by max_line_length
.
defaultConfig
let myArray = [| one; two; three |]
{ defaultConfig with ArrayOrListMultilineFormatter = MultilineFormatterType.NumberOfItems }
let myArray =
[| one
two
three |]
Control the maximum expression width for which let and member value/property bindings should be in one line. The width is that of the pattern for the binding plus the implementating expression but not the keywords (e.g. "let"). Default = 80.
defaultConfig
let title = "Great title of project"
type MyType() =
member this.HelpText = "Some help text"
{ defaultConfig with MaxValueBindingWidth = 10 }
let title =
"Great title of project"
type MyType() =
member this.HelpText =
"Some help text"
Control the maximum width for which function and member bindings should be in one line. Default = 40.
defaultConfig
let printScore score total = printfn "%i / %i" score total
type Triangle() =
member this.CalculateSurface(width: int, height: int) = width * height / 2
{ defaultConfig with MaxFunctionBindingWidth = 10 }
let printScore score total =
printfn "%i / %i" score total
type Triangle() =
member this.CalculateSurface(width: int, height: int) =
width * height / 2
Control the maximum width for which (nested) SynExpr.DotGet expressions should be in one line. Default = 50.
defaultConfig
let job =
JobBuilder
.UsingJobData(jobDataMap)
.Create<WrapperJob>()
.Build()
{ defaultConfig with MaxDotGetExpressionWidth = 100 }
let job =
JobBuilder.UsingJobData(jobDataMap).Create<WrapperJob>().Build()
Alternative way of formatting records, arrays and lists. This will align the braces at the same column level. Default = false.
defaultConfig
let myRecord =
{ Level = 1
Progress = "foo"
Bar = "bar"
Street = "Bakerstreet"
Number = 42 }
type Range =
{ From: float
To: float }
let a =
[| (1, 2, 3)
(4, 5, 6)
(7, 8, 9) |]
{ defaultConfig with MultilineBlockBracketsOnSameColumn = true }
let myRecord =
{
Level = 1
Progress = "foo"
Bar = "bar"
Street = "Bakerstreet"
Number = 42
}
type Range =
{
From: float
To: float
}
let a =
[|
(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
|]
Adds a new line between a type definition and its first member. Default = false.
defaultConfig
type Range =
{ From: float
To: float }
member this.Length = this.To - this.From
{ defaultConfig with NewlineBetweenTypeDefinitionAndMembers = true }
type Range =
{ From: float
To: float }
member this.Length = this.To - this.From
Deprecated setting!
This setting will be removed in the next major version of Fantomas. As of 4.4, it has no effect anymore due to a change in the MS F# style guide.
Bypasses the situation where if
,then
and else
are placed underneath each other.
This will ensure if
and then
are kept in the same line.
Default = false.
defaultConfig
if System.Char.IsUpper(c)
then sprintf "________%s" (c.ToString().ToLower())
else c.ToString()
{ defaultConfig with KeepIfThenInSameLine = true }
if System.Char.IsUpper(c) then
sprintf "________%s" (c.ToString().ToLower())
else
c.ToString()
Control the maximum width for which an elmish expression should be in one line. See Formatting Elmish style guide for more information. Default = 40.
defaultConfig
let d = div [] [ p [] [ str "meh" ] ]
{ defaultConfig with MaxElmishWidth = 10 }
let d =
div [] [
p [] [
str "meh"
]
]
Applies similar behavior when the elmish expression only contains a single argument. Default = false.
defaultConfig
let a =
Html.button [ prop.style [ style.marginLeft 5 ]
prop.onClick (fun _ -> setCount (count - 1))
prop.text "Decrement" ]
{ defaultConfig with SingleArgumentWebMode = true}
let a =
Html.button [
prop.style [ style.marginLeft 5 ]
prop.onClick (fun _ -> setCount (count - 1))
prop.text "Decrement"
]
When a function signature exceeds the max_line_length
, Fantomas will put all parameters on separate lines.
This setting also places the equals sign and return type on a new line.
Default = false.
defaultConfig
[<FunctionName("FormatCode")>]
let run
([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{*any}")>] req: HttpRequest)
(log: ILogger)
: HttpResponse =
Http.main CodeFormatter.GetVersion format FormatConfig.FormatConfig.Default log req
{ defaultConfig with AlignFunctionSignatureToIndentation = true }
[<FunctionName("FormatCode")>]
let run
([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{*any}")>] req: HttpRequest)
(log: ILogger)
: HttpResponse
=
Http.main CodeFormatter.GetVersion format FormatConfig.FormatConfig.Default log req
Provides an alternative way of formatting long member and constructor definitions, where the difference is mainly in the equal sign and returned type placement. Default = false.
defaultConfig
type C
(
aVeryLongType: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongType: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongType: AVeryLongTypeThatYouNeedToUse
) =
class
end
type D() =
member _.LongMethodWithLotsOfParameters
(
aVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse
) : ReturnType =
42
{ defaultConfig with AlternativeLongMemberDefinitions = true }
type C
(
aVeryLongType: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongType: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongType: AVeryLongTypeThatYouNeedToUse
)
=
class
end
type D() =
member _.LongMethodWithLotsOfParameters
(
aVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse
)
: ReturnType
=
42
Places the closing parenthesis of a multiline lambda argument on the next line. Default = false.
defaultConfig
let printListWithOffset a list1 =
List.iter
(fun { ItemOne = a } ->
// print
printfn "%s" a)
list1
let printListWithOffset a list1 =
list1
|> List.iter
(fun elem ->
// print stuff
printfn "%d" (a + elem))
{ defaultConfig with MultiLineLambdaClosingNewline = true }
let printListWithOffset a list1 =
List.iter
(fun { ItemOne = a } ->
// print
printfn "%s" a
)
list1
let printListWithOffset a list1 =
list1
|> List.iter (fun elem ->
// print stuff
printfn "%d" (a + elem)
)
Disable the default Elmish formatting style. When fsharp_disable_elmish_syntax
is enabled fsharp_max_elmish_width
and fsharp_single_argument_web_mode
will have no effect anymore.
defaultConfig
let encodeInput (input: Input) =
Encode.object [ "sourceCode", Encode.string input.SourceCode
"defines",
(Array.map Encode.string input.Defines
|> Encode.array)
"isFsi", Encode.bool input.IsFsi ]
{ defaultConfig with DisableElmishSyntax = true }
let encodeUrlModel code model: JsonValue =
Encode.object
[ "defines", Encode.string model.Defines
"isFsi", Encode.bool model.IsFsi
"code", Encode.string code ]
Breaks the normal indentation flow for the last branch of a pattern match or if/then/else expression. Only when the pattern match or if/then/else is the return value of a function or member.
This feature is considered experimental and is subject to change
defaultConfig
let main argv =
let args = parse argv
let instructions = Library.foo args
if args.DryRun = RunMode.Dry then
printfn "Would execute actions, but --dry-run was supplied: %+A" instructions
0
else
// proceed with main method
let output = Library.execute instructions
// do more stuff
0
{ defaultConfig with KeepIndentInBranch = true }
let main argv =
let args = parse argv
let instructions = Library.foo args
if args.DryRun = RunMode.Dry then
printfn "Would execute actions, but --dry-run was supplied: %+A" instructions
0
else
// proceed with main method
let output = Library.execute instructions
// do more stuff
0
Surround nested multi-line expressions with blank lines.
Existing blank lines are always preserved (via trivia).
Top level expressions will always follow the 2020 blank lines revision principle.
Default = true.
defaultConfig
let topLevelFunction () =
printfn "Something to print"
try
nothing ()
with
| ex ->
splash ()
()
let secondTopLevelFunction () =
// ...
()
{ defaultConfig with BlankLinesAroundNestedMultilineExpressions = false }
let topLevelFunction () =
printfn "Something to print"
try
nothing ()
with
| ex ->
splash ()
()
let secondTopLevelFunction () =
// ...
()
Always use a |
before every case in the declaration of a discriminated union. If false
, a |
character is used only in multiple-case discriminated unions, and is omitted in short single-case DUs.
Default = false.
type MyDU = Short of int
{ defaultConfig with BarBeforeDiscriminatedUnionDeclaration = true }
type MyDU = | Short of int
If being set, pretty printing is only done via ASTs. Compiler directives, inline comments and block comments will be ignored. There are numerous situations when the information in the AST alone cannot restore the original code. Please do not use this setting for formatting hand written code! Valid use-case of this settings is code generation in projects like FsAst and Myriad. Default = false.
hand written code
// some great comment
let add a b =
#if INTERACTIVE
42
#else
a + b
#endif
{ defaultConfig with StrictMode = true }
let add a b = a + b
starting version 4.1
To exclude files from formatting, create a .fantomasignore
file in the root of your project.
.fantomasignore
uses gitignore syntax (via MAB.DotIgnore).
Ignored files will be picked up when the Fantomas cli tool or the FAKE helpers (in Fantomas.Extras) are used.
Exclusion applies both to formatting and the format checking.
# Ignore Fable files
.fable/
# Ignore script files
*.fsx
Note that Fantomas only searches for a .fantomasignore
file in or above its current working directory, if one exists; unlike Git, it does not traverse the filesystem for each input file to find an appropriate ignore file.
(This is not true of the Fantomas daemon. The daemon can't rely on being invoked from the right place, and indeed there may not even be a well-defined notion of "right place" for the formatting tasks the daemon is required to perform, so it does search the filesystem for every file individually.)
See CodeFormatter.fsi to view the public API of Fantomas.
A very elegant and transparent way to use Fantomas is including it in a pre-commit git hook, by creating a .git/hooks/pre-commit
file with:
#!/bin/sh
git diff --cached --name-only --diff-filter=ACM -z | xargs -0 $HOME/.dotnet/tools/fantomas
git diff --cached --name-only --diff-filter=ACM -z | xargs -0 git add
This script assumes you have installed Fantomas globally as a dotnet tool
Please use with caution as Fantomas is not without bugs.
Fantomas also exposes some less official helper functions in Fantomas.Extras, these will be deprecated in the next major version.
It is advised to run the fantomas-tool
instead when using FAKE, see example.
By default, Fantomas adheres to the Microsoft F# code formatting guidelines. If these change, Fantomas will follow accordingly. Due to this reason, the output cannot be guaranteed to remain the same when upgrading to a new minor version.
If you are using Git for your source control, it is recommended to ignore commits where fantomas-tool
was updated using a .git-blame-ignore-revs file.
Check out this blogpost for more details.