Configuration
Fantomas ships with a limited series of options.
These can be stored in an .editorconfig file and will be picked up automatically by the
commandline.
Your IDE should respect your settings, however the implementation of that is editor specific. Setting the configuration via
UI might be available depending on the IDE.
|
Usage
Inside .editorconfig you can specify the file extension and code location to be use per config:
[*.fs]
fsharp_space_before_uppercase_invocation = true
# Write a comment by starting the line with a '#'
[*.{fs,fsx,fsi}]
fsharp_bar_before_discriminated_union_declaration = true
# Apply specific settings for a targeted subfolder
[src/Elmish/View.fs]
fsharp_multiline_bracket_style = stroustrup
Trying your settings via the online tool
You can quickly try your settings via the online tool.
Settings recommendations
Fantomas ships with a series of settings that you can use freely depending on your case.
However, there are settings that we do not recommend and generally should not be used.
.editorconfig
setting text you need to change the default. ⚠️ The copied text will not contain the default value.
Auxiliary settings
indent_size
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.
|
formatCode
"""
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
"""
"""
indent_size = 2
"""
|
max_line_length
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.
|
formatCode
"""
match myValue with
| Some foo -> someLongFunctionNameThatWillTakeFooAndReturnsUnit foo
| None -> printfn "nothing"
"""
"""
max_line_length = 60
"""
|
end_of_line
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
.
insert_final_newline
Adds a final newline character at the end of the file.
Why should text files end with a newline?
|
formatCode
"""
let a = 42
"""
"""
insert_final_newline = false
"""
|
fsharp_space_before_parameter
Add a space after the name of a function and before the opening parenthesis of the first parameter.
This setting influences function definitions.
|
formatCode
"""
let value (a: int) = x
let DumpTrace() = ()
"""
"""
fsharp_space_before_parameter = false
"""
|
fsharp_space_before_lowercase_invocation
Add a space after the name of a lowercased function and before the opening parenthesis of the first argument.
This setting influences function invocation in expressions and patterns.
|
formatCode
"""
value (a, b)
startTimer ()
match x with
| value (a, b) -> ()
"""
"""
fsharp_space_before_lowercase_invocation = false
"""
|
fsharp_space_before_uppercase_invocation
Add a space after the name of a uppercase function and before the opening parenthesis of the first argument.
This setting influences function invocation in expressions and patterns.
|
formatCode
"""
Value(a, b)
person.ToString()
match x with
| Value(a, b) -> ()
"""
"""
fsharp_space_before_uppercase_invocation = true
"""
|
fsharp_space_before_class_constructor
Add a space after a type name and before the class constructor.
|
formatCode
"""
type Person() =
class
end
"""
"""
fsharp_space_before_class_constructor = true
"""
|
fsharp_space_before_member
Add a space after a member name and before the opening parenthesis of the first parameter.
|
formatCode
"""
type Person() =
member this.Walk(distance: int) = ()
member this.Sleep() = ignore
member __.singAlong() = ()
member __.swim(duration: TimeSpan) = ()
"""
"""
fsharp_space_before_member = true
"""
|
fsharp_space_before_colon
Add a space before :
. Please note that not every :
is controlled by this setting.
|
formatCode
"""
type Point = { x: int; y: int }
let myValue: int = 42
let update (msg: Msg) (model: Model) : Model = model
"""
"""
fsharp_space_before_colon = true
"""
|
fsharp_space_after_comma
Adds a space after ,
in tuples.
|
formatCode
"""
myValue.SomeFunction(foo, bar, somethingElse)
(a, b, c)
"""
"""
fsharp_space_after_comma = false
"""
|
fsharp_space_before_semicolon
Adds a space before ;
in records, arrays, lists, etc.
|
formatCode
"""
let a = [ 1 ; 2 ; 3 ]
let b = [| foo ; bar |]
type C = { X: int ; Y: int }
"""
"""
fsharp_space_before_semicolon = true
"""
|
fsharp_space_after_semicolon
Adds a space after ;
in records, arrays, lists, etc.
|
formatCode
"""
let a = [ 1; 2; 3 ]
let b = [| foo; bar |]
type C = { X: int; Y: int }
"""
"""
fsharp_space_after_semicolon = false
"""
|
fsharp_space_around_delimiter
Adds a space around delimiters like [
,[|
,{`.
|
formatCode
"""
let a = [ 1;2;3 ]
let b = [| 4;5;6 |]
"""
"""
fsharp_space_around_delimiter = false
"""
|
Maximum width constraints
Settings that control the max width of certain expressions.
fsharp_max_if_then_short_width
Control the maximum length for which if/then expression without an else expression can be on one line.
The Microsoft F# style guide recommends to never write such an expression in one line.
If the else expression is absent, it is recommended to never to write the entire expression in one line.
|
formatCode
"""
if a then
()
"""
"""
fsharp_max_if_then_short_width = 15
"""
|
fsharp_max_if_then_else_short_width
Fantomas by default follows the if/then/else conventions listed in the Microsoft F# style guide.
This setting facilitates this by determining the maximum character width where the if/then/else expression stays in one line.
|
formatCode
"""
if myCheck then truth else bogus
"""
"""
fsharp_max_if_then_else_short_width = 10
"""
|
fsharp_max_infix_operator_expression
Control the maximum length for which infix expression can be on one line.
|
formatCode
"""
let WebApp =
route "/ping" >=> authorized >=> text "pong"
"""
"""
fsharp_max_infix_operator_expression = 20
"""
|
fsharp_max_record_width
Control the maximum width for which records should be in one line.
Requires fsharp_record_multiline_formatter
to be character_width
to take effect.
|
formatCode
"""
type MyRecord = { X: int; Y: int; Length: int }
let myInstance = { X = 10; Y = 20; Length = 90 }
"""
"""
fsharp_max_record_width = 20
"""
|
fsharp_max_record_number_of_items
Control the maximum number of fields for which records should be in one line.
Requires fsharp_record_multiline_formatter
to be
number_of_items
to take effect.
|
formatCode
"""
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 }
"""
"""
fsharp_record_multiline_formatter = number_of_items
fsharp_max_record_number_of_items = 2
"""
|
fsharp_record_multiline_formatter
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
.
Note that in either case, record expressions/statements are still governed by max_line_length
.
|
formatCode
"""
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" }
"""
"""
fsharp_record_multiline_formatter = number_of_items
"""
|
fsharp_max_array_or_list_width
Control the maximum width for which lists and arrays can be in one line.
Requires fsharp_array_or_list_multiline_formatter
to be character_width
to take effect
|
formatCode
"""
let myArray = [| one; two; three |]
"""
"""
fsharp_max_array_or_list_width = 20
"""
|
fsharp_max_array_or_list_number_of_items
Control the maximum number of elements for which lists and arrays can be in one line.
Requires fsharp_array_or_list_multiline_formatter
to be number_of_items
to take effect.
|
formatCode
"""
let myList = [ one; two ]
let myArray = [| one; two; three |]
"""
"""
fsharp_array_or_list_multiline_formatter = number_of_items
fsharp_max_array_or_list_number_of_items = 2
"""
|
fsharp_array_or_list_multiline_formatter
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
.
Note that in either case, list expressions are still governed by max_line_length
.
|
formatCode
"""
let myArray = [| one; two; three |]
"""
"""
fsharp_array_or_list_multiline_formatter = number_of_items
"""
|
fsharp_max_value_binding_width
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 right-hand expression but not the keywords (e.g. "let").
|
formatCode
"""
let title = "Great title of project"
type MyType() =
member this.HelpText = "Some help text"
"""
"""
fsharp_max_value_binding_width = 10
"""
|
fsharp_max_function_binding_width
Control the maximum width for which function and member bindings should be in one line.
In contrast to fsharp_max_value_binding_width
, only the right-hand side expression of the binding is measured.
|
formatCode
"""
let title = "Great title of project"
type MyType() =
member this.HelpText = "Some help text"
"""
"""
fsharp_max_function_binding_width = 10
"""
|
fsharp_max_dot_get_expression_width
Control the maximum width for which (nested) SynExpr.DotGet expressions should be in one line.
|
formatCode
"""
let job = JobBuilder.UsingJobData(jobDataMap).Create<WrapperJob>().Build()
"""
"""
fsharp_max_dot_get_expression_width = 60
"""
|
fsharp_multiline_bracket_style
Cramped
The default way in F# to format brackets.
Aligned
Alternative way of formatting records, arrays and lists. This will align the braces at the same column level.
Stroustrup
Allow for easier reordering of members and keeping the code succinct.
|
formatCode
"""
let myRecord =
{ Level = 1
Progress = "foo"
Bar = "bar"
Street = "Bakerstreet"
Number = 42 }
type Range =
{ From: float
To: float
FileName: string }
let a =
[| (1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10, 11, 12)
(13, 14, 15)
(16, 17,18)
(19, 20, 21) |]
"""
"""
fsharp_multiline_bracket_style = aligned
"""
|
formatCode
"""
let myRecord =
{ Level = 1
Progress = "foo"
Bar = "bar"
Street = "Bakerstreet"
Number = 42 }
type Range =
{ From: float
To: float
FileName: string }
let a =
[| (1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10, 11, 12)
(13, 14, 15)
(16, 17,18)
(19, 20, 21) |]
"""
"""
fsharp_multiline_bracket_style = stroustrup
"""
|
fsharp_newline_before_multiline_computation_expression
Insert a newline before a computation expression that spans multiple lines
|
formatCode
"""
let something =
task {
let! thing = otherThing ()
return 5
}
"""
"""
fsharp_newline_before_multiline_computation_expression = false
"""
|
G-Research style
A series of settings requicolor="red" to conform with the G-Research style guide.
From a consistency point of view, it is recommend to enable all these settings instead of cherry-picking a few.
fsharp_newline_between_type_definition_and_members
Adds a new line between a type definition and its first member.
|
formatCode
"""
type Range =
{ From: float
To: float }
member this.Length = this.To - this.From
"""
"""
fsharp_newline_between_type_definition_and_members = false
"""
|
fsharp_align_function_signature_to_indentation
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.
|
formatCode
"""
[<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
"""
"""
fsharp_align_function_signature_to_indentation = true
"""
|
fsharp_alternative_long_member_definitions
Provides an alternative way of formatting long member and constructor definitions, where the difference is mainly in the equal sign and returned type placement.
|
formatCode
"""
type C
(
aVeryLongType: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongType: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongType: AVeryLongTypeThatYouNeedToUse
) =
class
end
type D() =
member _.LongMethodWithLotsOfParameters
(
aVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse
) : ReturnType =
42
type E() =
new
(
aVeryLongType: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongType: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongType: AVeryLongTypeThatYouNeedToUse
) = E()
"""
"""
fsharp_alternative_long_member_definitions = true
"""
|
fsharp_multi_line_lambda_closing_newline
Places the closing parenthesis of a multiline lambda argument on the next line.
|
formatCode
"""
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))
"""
"""
fsharp_multi_line_lambda_closing_newline = true
"""
|
fsharp_experimental_keep_indent_in_branch
Breaks the normal indentation flow for the last branch of a pattern match or if/then/else expression.
Only when the last pattern match or else branch was already at the same level of the entire match or if expression.
This feature is experimental and is subject to change.
|
formatCode
"""
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
"""
"""
fsharp_experimental_keep_indent_in_branch = true
"""
|
fsharp_bar_before_discriminated_union_declaration
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.
|
formatCode
"""
type MyDU = Short of int
"""
"""
fsharp_bar_before_discriminated_union_declaration = true
"""
|
Other
Some additional settings that don't fit into any style guide.
fsharp_blank_lines_around_nested_multiline_expressions
Surround nested multi-line expressions with blank lines.
Existing blank lines are always preserved (via trivia), with exception when fsharp_keep_max_number_of_blank_lines is used.
Top level expressions will always follow the 2020 blank lines revision principle.
|
formatCode
"""
let topLevelFunction () =
printfn "Something to print"
try
nothing ()
with
| ex ->
splash ()
()
let secondTopLevelFunction () =
// ...
()
"""
"""
fsharp_blank_lines_around_nested_multiline_expressions = false
"""
|
fsharp_keep_max_number_of_blank_lines
Set maximal number of consecutive blank lines to keep from original source. It doesn't change number of new blank lines generated by Fantomas.
|
formatCode
"""
open Foo
let x = 42
"""
"""
fsharp_keep_max_number_of_blank_lines = 1
"""
|
fsharp_experimental_elmish
Applies the Stroustrup style to the final (two) array or list argument(s) in a function application.
Note that this behaviour is also active when fsharp_multiline_bracket_style = stroustrup
.
|
formatCode
"""
let dualList =
div
[]
[
h1 [] [ str "Some title" ]
ul
[]
[
for p in model.Points do
li [] [ str $"%i{p.X}, %i{p.Y}" ]
]
hr []
]
let singleList =
Html.div
[
Html.h1 [ str "Some title" ]
Html.ul
[
for p in model.Points do
Html.li [ str $"%i{p.X}, %i{p.Y}" ]
]
]
"""
"""
fsharp_experimental_elmish = true
"""
|
val string: value: 'T -> string
--------------------
type string = String
String.Split(separator: string array, options: StringSplitOptions) : string array
String.Split(separator: string, ?options: StringSplitOptions) : string array
String.Split(separator: char array, options: StringSplitOptions) : string array
String.Split(separator: char array, count: int) : string array
String.Split(separator: char, ?options: StringSplitOptions) : string array
String.Split(separator: string array, count: int, options: StringSplitOptions) : string array
String.Split(separator: string, count: int, ?options: StringSplitOptions) : string array
String.Split(separator: char array, count: int, options: StringSplitOptions) : string array
String.Split(separator: char, count: int, ?options: StringSplitOptions) : string array
<summary>Specifies options for applicable <see cref="Overload:System.String.Split" /> method overloads, such as whether to omit empty substrings from the returned array or trim whitespace from substrings.</summary>
<summary>Provides methods for creating, manipulating, searching, and sorting arrays, thereby serving as the base class for all arrays in the common language runtime.</summary>
<summary>Gets the total number of elements in all the dimensions of the <see cref="T:System.Array" />.</summary>
<exception cref="T:System.OverflowException">The array is multidimensional and contains more than <see cref="F:System.Int32.MaxValue">Int32.MaxValue</see> elements.</exception>
<returns>The total number of elements in all the dimensions of the <see cref="T:System.Array" />; zero if there are no elements in the array.</returns>
static member CodeFormatter.FormatDocumentAsync: isSignature: bool * source: string * config: FormatConfig -> Async<FormatResult>
static member CodeFormatter.FormatDocumentAsync: isSignature: bool * source: string * config: FormatConfig * cursor: Fantomas.FCS.Text.pos -> Async<FormatResult>
<summary> Formatted code </summary>
module Async from Fantomas.Core
--------------------
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * obj -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...
--------------------
type Async<'T>
Boolean.ToString(provider: IFormatProvider) : string
active recognizer MultilineFormatterType: string -> MultilineFormatterType option
--------------------
type MultilineFormatterType = | CharacterWidth | NumberOfItems member Equals: MultilineFormatterType * IEqualityComparer -> bool static member OfConfigString: cfgString: string -> MultilineFormatterType option static member ToConfigString: cfg: MultilineFormatterType -> string