Git Product home page Git Product logo

fantomas's People

Contributors

alanlomeli avatar anthonylloyd avatar auduchinok avatar baronfel avatar bobface avatar btzo avatar dawedawe avatar dependabot[bot] avatar deviousasti avatar dsyme avatar dungpa avatar edgarfgp avatar enricosada avatar erikschierboom avatar forki avatar jackmatusiewicz avatar jaredpar avatar jindraivanek avatar josh-degraw avatar kentcb avatar knocte avatar ly29 avatar nojaf avatar okayx6 avatar pbiggar avatar s-trooper avatar sasmithjr avatar smoothdeveloper avatar theimowski avatar vasily-kirichenko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fantomas's Issues

Signature formatting is cramped

Signature formatting needs more whitelines, e.g. before "module" declarations, before "type" declarations, and indeed before each "val" and "member", at least the ones with comments:

open System
open System.Linq.Expressions
module ExtraHashCompare =
/// An intrinsic for compiling <@ x <> y @> to expression trees
val GenericNotEqualIntrinsic: 'T -> 'T -> bool
[]
type QuotationEvaluator =

VS2013 support

I tried upgrading the project to VS2013 SDK, and just adding a new target for VS12, but after installing the built vsix, the fantomas commands are not availabe inside VS.
Can someone give a hand with this?

Verbatim strings lose their "@"

Formatting

#r @"C:\foo\bar.dll"

gives

#r "C:\foo\bar.dll"

which is not valid. Tricky to fix because verbatim-ness is not recorded in the compiler AST. Suggest either a modified compiler or a heuristic that at least uses @ on strings that contain "".

Wrong desugaring of single match

Formatting the following code fragment

try 
    fst(find (fun (s, (s', ty)) -> 
                s' = s0 && can (type_match ty ty0) []) (!the_interface))
with
| Failure _ -> s0

gives

try 
    fst(find (fun (s, _) -> 
                match _arg1 with
                | s', ty -> s' = s0 && can (type_match ty ty0) []) (!the_interface))
with
| Failure _ -> s0

This happens because desugaring patterns can't capture the tuple without parentheses.

Wrong locations of attributes on recursive declarations

Formatting this

let rec [<Test>] a () = 10
and [<Test>] b () = 10

gives non-compilable result as

[<Test>] 
let rec a () = 10
[<Test>] 
and b () = 10

The second attribute should be placed between and and the identifier.

Similar approach applies for type declarations

[<TestFixture>]
type A() = class end

and [<TestFixture>] B() = class end

See this question http://stackoverflow.com/questions/16484463/attributes-and-let-statements-in-type-members for more information.

Lost parentheses while formatting type signatures

When converting NHol code base, I notice wrong formatting of type signatures e.g. formatting

let user_printers = ref([] : (string * (term -> unit)) list)
let the_interface = ref([] : (string * (string * hol_type)) list)

gives

let user_printers = ref([] : (string * term -> unit) list)
let the_interface = ref([] : (string * string * hol_type) list)

This kind of bug happens for quite some time but it hasn't been resolved completely since there is not enough information in the AST. As a last resort, we could query the token stream and print the signature as is.

style question: single function parameters, parenthesis reduction.

Just regarding https://github.com/dungpa/fantomas/blob/master/FormattingConventions.md , I would vote in favour of enforcing single parameters to drop their parenthesis. This is already allowed in F# compiler and I feel it 'lightens up' code and is promotes clean code, and is more consistent with idiomatic F#. Is this the write place to post these requests?

Example bad vs good:
stderr.WriteLine("Test") vs. stderr.WriteLine "Test"
Some("Test") vs. Some "Test"

Indentation of multi line functions used in higher order operations

It's common to have code like this:

data
|> Seq.something (fun x -> small function)
|> Seq.map (fun x -> 
    bigger function
    spanning multiple lines)
|> Seq.somethingElse

Fantomas is turning it into this:

data
|> Seq.something (fun x -> small function)
|> Seq.map (fun x -> 
            bigger function
            spanning multiple lines)
|> Seq.somethingElse

Is this intentional?

^a needs spaces when used as a type parameter

The rarely used inline-type-variables need a space around them when used as type parameters. This code:

let inline tryAverage(seq: seq< ^a >): ^a option =  1

currently becomes:

let inline tryAverage(seq: seq<^a>): ^a option = 1

which is not valid code.

Named arguments

Not so sure this fails only for attributes or constructors in general but it broke the following line:

[<AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)>]

Like so:

[<AspNetCompatibilityRequirements(RequirementsMode 
                                  = AspNetCompatibilityRequirementsMode.Allowed)>]

Which doesn't compile.

Breaking of parameters of function calls between multiple lines

This is minor, but here it goes anyway.

I had this line:

sprintf "https://datafeeds.networkrail.co.uk/ntrod/CifFileAuthenticate?type=CIF_%s_%s_DAILY&day=%s" 
        (toc.ToUrl()) 
        (schedule.ToUrl()) 
        (schedule.ToUrl(day))    

And it was changed into this:

sprintf "https://datafeeds.networkrail.co.uk/ntrod/CifFileAuthenticate?type=CIF_%s_%s_DAILY&day=%s" 
    (toc.ToUrl()) (schedule.ToUrl()) (schedule.ToUrl(day))

I think the first one was more readable.
Is it possible to add an option to align the parameters that fall to another line below the first one instead of just indenting by the default?

Actually I think the best formatting for this case would be this one:

sprintf "https://datafeeds.networkrail.co.uk/ntrod/CifFileAuthenticate?type=CIF_%s_%s_DAILY&day=%s" 
        (toc.ToUrl()) (schedule.ToUrl()) (schedule.ToUrl(day))    

Whitespace at start and end of file not preserved - should it be?

When a file contains

let x = 1

with a few empty lines before the first "let" and a few empty lines at the end of the file, the whitespace is not preserved and the last line ends up with no end-of-line.

The last line should definitely at least get an end-of-line character.

It also feels like the initial whitespace should probably be preserved.

Spaces around the dynamic (?) operator

I got this:

{ Code = code
  Name = station?StationName.Replace(" Rail Station", null)

converted to this:

{ Code = code
  Name = station ? StationName.Replace(" Rail Station", null)

which is not very common.
Unlike th other infix operators, the ? operator doesn't usually have spaces around

The global keyword is not supported

If you use the global keyword it is stripped out by fantoms.

Consider:

namespace global
type Startup() =
    let addSeven v =  v + 7
    member x.Invoke(input:obj) =
        let v = input :?> int
        async.Return (addSeven v :> obj) |> Async.StartAsTask

becomes:

namespace 

type Startup() = 
    let addSeven v = v + 7
    member x.Invoke(input : obj) = 
        let v = input :?> int
        async.Return(addSeven v :> obj) |> Async.StartAsTask

"as this" dropped on secondary constructors

There is a rarely used declaration form that permits naming the "this" parameter for a secondary constructor and using it in the "then" clause.

new(fileName, makeAsync, initState) as secondCtor = 
    new StateMachine(makeAsync)
    then
        secondCtor.Init(fileName, initState)

Fantomas is dropping the "as secondCtor", resulting in invalid code.

Semicolon at the End of Line option

Is this comon? I don't remember seeing any code base were I found semicolons at the end of line on records, so I think that the default for this option should be false instead of true

Ensure consistent behaviours of SemicolonEndOfLine

This option is true by default. It works as expected on records

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 }

but not on lists and arrays

let xs =
  ["abc"
   "def"
   "ghi"
   "jkl"]

The behaviour on lists and arrays should be consistent as the one on records.

Unit literal is sometimes erased

This is a line of a test written using Foq mock library:

let logger = Mock<ILogger>().Setup(fun log -> <@ log.Log(error) @>).Returns(()).Create()
let logger = Mock<ILogger>().Setup(fun log -> <@ log.Log(error) @>).Returns().Create()

Returns(()) becomes Returns() after formatting which is wrong.

In a signature, a comment gets inserted in the wrong place

This code:

type C =

    member P1 : int * string

    /// def
    member P2 : int

gets formatted like this:

type C = 
    member P1: (int * string/// def
                            )
    member P2: int

where the "def" comment has been inserted in the wrong place. One fix is to avoid inserting the extra parens at all, though it looks like the problem is more general.

keep empty lines

please keep empty lines:

i.e for nested let's:

let f1 = 
    let f2 x = 
        x + 1
    let f3 y = 
        y + 1
    x + y

vs

let f1 = 
    let f2 x = 
        x + 1

    let f3 y = 
        y + 1

    x + y

or 2 empty lines for top level let's like python

2D arrays reformatted to jagged array

Array2D is getting changed to jagged arrays ( 'a[,] is changed to 'a[][] )

original
module Array2D =
let cast<'a> (A:obj[,]):'a[,] = A |> Array2D.map unbox
let flatten (A:'a[,]) = A |> Seq.cast<'a>
let getColumn c (A:_[,]) = flatten A.[*,c..c] |> Seq.toArray

formatted
module Array2D =
let cast<'a>(A : obj[][]) : 'a[][] = A |> Array2D.map unbox
let flatten(A : 'a[][]) = A |> Seq.cast<'a>
let getColumn c (A : _[][]) = flatten A.[.., c..c] |> Seq.toArray

Keep inline and multi-line comments

This tool is great!

One comment - I see that the README says inline and multi-line comments get dropped. It would be great to have this addressed!

Some relevant facts

  • The F# lexer can already produce token streams that include all skipped tokens: // comments, (* *) comments, #if, #else etc., with ranges attached.
  • This augmented token stream cannot be used as input the parser, because it wouldd make the grammar ambiguous. Nor is there any meaningful way to store this information into the AST: comments and directives can occur at too many places. This means the comments simply can't be propagated through to the untyped AST in the same way as we do for XML doc comments.
  • However, it should be possible to match up the non-augmented and augmented token streams in a post-processing step. That is, as text is produced in the output (based on formatting the AST from the non-augmented token steam), it should be possible to look in the augmented token stream for corresponding comments and other skipped text to emit. This means running two token streams: one non-augmented (to produce the AST) and one augmented.
  • Some formatting transformations have the property that they never add or remove non-whitespace text from the token stream. However it looks like this transformation does add/remove tokens.

In any case, I believe the correct technique is to use a combination of the the non-augmented formatted text (from the AST) and the augmented token stream to restore comments into the output. From talking to people in the Visual Studio team, this is (roughly speaking) how other compilers do it (though it may be worth checking with the C# commpiler in mono develop, for example).

This would likely need a private build of FSharp.Compiler.dll (or a private copy of the lexer) but you may have that already.

Add code formatting support to MonoDevelop

Having this feature in MonoDevelop and XamarinStudio via the F# language binding is just as important as Visual Studio IMHO. A reflective soft binding would have to be used to access the compiler unless the compiler is packaged with the F# language binding in the future.

Trailing with after interface implementation declaration

Adds a trailing with to interface implementation declaration although since the type already implements the interface from the inherited class.

The following gets translated:

type WebServices() =
    inherit ExchangeRateService()
    interface IWebServices

To:

type WebServices() = 
    inherit ExchangeRateService()
    interface IWebServices with

Which fails to compile.

'Format selection' reports negative indent level on top-level let form

I believe the following test should pass, and reformat the "let y = 1+2" line:

[<Test>]
let ``should format a top-level let correctly``() =
    formatSelectionFromString false (makeRange 3 0 3 11) """
let x = 2 + 3
let y = 1+2
let z = x + y""" config
    |> should equal """
let x = 2 + 3
let y = 1 + 2
let z = x + y"""

However, when I run it, it fails with the error "The indent level cannot be negative." I've tried using 1 as the start column number but it doesn't help.

Reorder open statements

It would be nice if fantomas reordered the open statements, similar to the reorder usings functionality in C#

Excessive line breaking

I have line breaks set at 120, and I had this line:

              let details = cells.[4] |> element "a" |> attr "href"

which ended at line 79, but it was still broke down like this:

              let details = 
                  cells.[4]
                  |> element "a"
                  |> attr "href"

I actually don't mind in this case, but just posted the issue to make sure this was intentional and not a bug, as it doesn't seem consistent because fantomas does the opposite for functions. I had this:

    let radians2Dist radians radius = 
        radians * radius
    let degrees2Dist degrees radius = 
        radians2Dist (toRadians degrees) radius

and it was converted to this:

    let radians2Dist radians radius = radians * radius
    let degrees2Dist degrees radius = radians2Dist (toRadians degrees) radius

VS support

It would be great to have a Visual Studio extension with this so you could you Ctrl+K Ctrl+D in F# source files like you can in C# source files

Formatting introduces error when using type provider and verbatim string

Consider the following code fragment (using FSharpx.TypeProviders):

open FSharpx.TypeProviders.Regex
type IntegerRegex = FSharpx.Regex< @"(?<value>\d+)" >

Formatting removes the spaces from the second line, resulting in:

type IntegerRegex = FSharpx.Regex<@"(?<value>\d+)">

which gives the error "Unexpected quotation operator '<@' in type definition". The space before the @ should be preserved.

Missing ":" when formatting type function

A type function like this:

let empty<'T> : LazyList<'T> = EmptyValue<'T>.Value

gets formatted without the ":", resulting in invalid code

let empty<'T> LazyList<'T> = EmptyValue<'T>.Value

Add an option to remove unneeded parenthesis

Example:

let (a, b) = 1,2

match (a, b) with
| Some(c) -> ()

let manualTree = 
    Choice ("Sci-Fi", 
            [|("No", 
               Choice ("Action", 
                       [|("Yes", Conclusion "Stallone")
                         ("No", Conclusion "Schwarzenegger")|]))
              ("Yes", Conclusion "Schwarzenegger")|])

let test = [| ("Action", "Yes"); ("Sci-Fi", "Yes") |]

Could be turned into:

let a, b = 1, 2

match a, b with
| Some c -> ()

let manualTree = 
    Choice ("Sci-Fi", 
            [|"No", 
              Choice ("Action", 
                      [|"Yes", Conclusion "Stallone"
                        "No", Conclusion "Schwarzenegger"|])
              "Yes", Conclusion "Schwarzenegger"|])

let test = [| "Action", "Yes"; "Sci-Fi", "Yes" |]

What should be the default?

Unit Tests fail in windows

Environment.Newline returns "\r\n", but the triple quoted string literals only have "\n" on them, causing a lot of tests to fail

Use 4 spaces indentation

Hi,

I think most F# users are using 4 spaces now.
Please use this as a default in the formatter.

Cheers,
Steffen

Weird indentation of fun body

The contrived example code

[]
|> List.map (fun x -> 
    let y = x * x
    y > 0)

becomes

[] |> List.filter (fun x -> 
              let y = x * x
              y > 0)

There seems to be some alignment going on too -- adding items to the list changes the indentation/alignment. As an aside, an option to disable alignment of code (except multi-line lists, records etc) would be fantastic :).

Big regression in comments handling

If you try to dogfood fantomas on CodePrinter.fs, with the current version published in the VS gallery it works reasonably, but with the current master branch I got this:

module internal Fantomas.CodePrinter

open Fantomas.FormatConfig
open Fantomas.SourceParser
open Fantomas.SourceTransformer
open System.Collections.Generic

let // print strings with quotes
    // Use verbatim string to escape '\' correctly
    // Checking for Tmp is a bit fragile
    sortAndDedup // Add a new line after module-level let bindings
                 // There is no nested types and they are recursive if there are more than one definition
                 // Special treatment for function application on attributes
                 /// Preserve a break even if the expression is a one-liner
                 /// Break but doesn't indent the expression
                 /// Value inter indicates printing in a interface definition. 
                 /// Each member is separated by a new line.
                 // Too tedious in handling property get and set
                 // Handle special "then" block in constructors
                 // Not sure about the role of e1
                 // Check the role of the second part of eio
                 // Handle the form 'for i in e1 -> e2'
                 // Separate two prefix ops by spaces
                 // Handle spaces of infix application based on which category it belongs to
                 // Only put |> on the same line in a very trivial expression
                 // This filters a few long examples of App
                 // Unlike infix app, function application needs a level of indentation
                 // Always spacing in multiple arguments
                 // and is applicable for use binding
                 // Could customize a bit if e is single line
                 // It seems too annoying to use sepSemiNln
                 // A generalization of IfThenElse
                 // At this stage, all symbolic operators have been handled.
                 /// Use in indexed set and get only
                 // Add newline after un-indent to be spacing-correct
                 // Remember that we use MemberDefn of parent node
                 // Add newline after un-indent to be spacing-correct
                 // Remember that we use MemberSig of parent node
                 // Being protective on union case declaration
                 // Drop bracket around tuples before an arrow
                 // Do similar for tuples after an arrow
                 // Some patterns without spaces could cause a parsing error
                 // Fun is grouped by brackets inside 'genType true t'
                 // There is a potential parser bug with "<^T..."
                 /// Each multiline member definition has a pre and post new line. 
                 // What is the role of so
                 // Remove parentheses on an extremely simple pattern
                 // Override escaped new keyword
                 // This pattern is potentially long
                 // Quotes will be printed by inner expression
                 by l = 
    l
    |> Seq.distinctBy by
    |> Seq.sortBy by
    |> List.ofSeq

let rec genParsedInput = 
    function 
    | ImplFile im -> genImpFile im
    | SigFile si -> genSigFile si

and genImpFile (ParsedImplFileInput (hs, mns)) = 
    col sepNln hs genParsedHashDirective +> col sepNln mns genModuleOrNamespace

and genSigFile (ParsedSigFileInput (hs, mns)) = 
    col sepNln hs genParsedHashDirective +> col sepNln mns genSigModuleOrNamespace

and genParsedHashDirective (ParsedHashDirective (h, s)) = 
    let gs = 
        match s with
        | "" -> sepNone
        | _ when s.Contains ("\\") -> !-(sprintf "@\"%O\"" s)
        | _ -> !-(sprintf "\"%O\"" s)
    !-"#" -- h +> sepSpace +> gs

and genModuleOrNamespace (ModuleOrNamespace (ats, px, ao, s, mds, isModule)) = 
    genPreXmlDoc px +> colPost sepNln sepNln ats genAttribute 
    +> ifElse (s = "Tmp") sepNone 
           (ifElse isModule (!-"module ") (!-"namespace ") +> opt sepSpace ao genAccess 
            +> ifElse (s = "") (!-"global") (!-s) +> rep 2 sepNln) +> genModuleDeclList mds

and genSigModuleOrNamespace (SigModuleOrNamespace (ats, px, ao, s, mds, isModule)) = 
    genPreXmlDoc px +> colPost sepNln sepNln ats genAttribute 
    +> ifElse (s = "Tmp") sepNone 
           (ifElse isModule (!-"module ") (!-"namespace ") +> opt sepSpace ao genAccess -- s +> rep 2 sepNln) 
    +> genSigModuleDeclList mds

and genModuleDeclList = 
    function 
    | [x] -> genModuleDecl x
    | OpenL (xs, ys) -> 
        let xs = xs |> sortAndDedup ((|Open|_|) >> Option.get)
        match ys with
        | [] -> col sepNln xs genModuleDecl
        | _ -> col sepNln xs genModuleDecl +> rep 2 sepNln +> genModuleDeclList ys
    | DoExprAttributesL (xs, ys) | HashDirectiveL (xs, ys) | ModuleAbbrevL (xs, ys) | OneLinerLetL (xs, ys) -> 
        match ys with
        | [] -> col sepNln xs genModuleDecl
        | _ -> col sepNln xs genModuleDecl +> rep 2 sepNln +> genModuleDeclList ys
    | MultilineModuleDeclL (xs, ys) -> 
        match ys with
        | [] -> col (rep 2 sepNln) xs genModuleDecl
        | _ -> col (rep 2 sepNln) xs genModuleDecl +> rep 2 sepNln +> genModuleDeclList ys
    | _ -> sepNone

and genSigModuleDeclList = 
    function 
    | [x] -> genSigModuleDecl x
    | SigOpenL (xs, ys) -> 
        let xs = xs |> sortAndDedup ((|SigOpen|_|) >> Option.get)
        match ys with
        | [] -> col sepNln xs genSigModuleDecl
        | _ -> col sepNln xs genSigModuleDecl +> rep 2 sepNln +> genSigModuleDeclList ys
    | SigHashDirectiveL (xs, ys) | SigModuleAbbrevL (xs, ys) | SigValL (xs, ys) -> 
        match ys with
        | [] -> col sepNln xs genSigModuleDecl
        | _ -> col sepNln xs genSigModuleDecl +> rep 2 sepNln +> genSigModuleDeclList ys
    | SigMultilineModuleDeclL (xs, ys) -> 
        match ys with
        | [] -> col (rep 2 sepNln) xs genSigModuleDecl
        | _ -> col (rep 2 sepNln) xs genSigModuleDecl +> rep 2 sepNln +> genSigModuleDeclList ys
    | _ -> sepNone

and genModuleDecl = 
    function 
    | Attributes (ats) -> col sepNln ats genAttribute
    | DoExpr (e) -> genExpr e
    | Exception (ex) -> genException ex
    | HashDirective (p) -> genParsedHashDirective p
    | Let (b) -> genLetBinding true "let " b
    | LetRec (b :: bs) -> 
        genLetBinding true "let rec " b +> colPre (rep 2 sepNln) (rep 2 sepNln) bs (genLetBinding false "and ")
    | ModuleAbbrev (s1, s2) -> !-"module " -- s1 +> sepEq -- s2
    | NamespaceFragment (m) -> failwithf "NamespaceFragment hasn't been implemented yet: %O" m
    | NestedModule (ats, px, ao, s, mds) -> 
        genPreXmlDoc px +> colPost sepNln sepNln ats genAttribute -- "module " +> opt sepSpace ao genAccess -- s 
        +> sepEq +> indent +> sepNln +> genModuleDeclList mds +> unindent
    | Open (s) -> !-(sprintf "open %s" s)
    | Types (t :: ts) -> genTypeDefn true t +> colPre (rep 2 sepNln) (rep 2 sepNln) ts (genTypeDefn false)
    | md -> failwithf "Unexpected module declaration: %O" md

and genSigModuleDecl = 
    function 
    | SigException (ex) -> genSigException ex
    | SigHashDirective (p) -> genParsedHashDirective p
    | SigVal (v) -> genVal v
    | SigModuleAbbrev (s1, s2) -> !-"module " -- s1 +> sepEq -- s2
    | SigNamespaceFragment (m) -> failwithf "NamespaceFragment is not supported yet: %O" m
    | SigNestedModule (ats, px, ao, s, mds) -> 
        genPreXmlDoc px +> colPost sepNln sepNln ats genAttribute -- "module " +> opt sepSpace ao genAccess -- s 
        +> sepEq +> indent +> sepNln +> genSigModuleDeclList mds +> unindent
    | SigOpen (s) -> !-(sprintf "open %s" s)
    | SigTypes (t :: ts) -> genSigTypeDefn true t +> colPre (rep 2 sepNln) (rep 2 sepNln) ts (genSigTypeDefn false)
    | md -> failwithf "Unexpected module signature declaration: %O" md

and genAccess (Access s) = !-s

and genAttribute (Attribute (s, e, _)) = 
    match e with
    | ConstExpr (Const "()") -> !-(sprintf "[<%s>]" s)
    | e -> !-"[<" -- s +> genExpr e -- ">]"

and genOneLinerAttributes ats = colPost sepSpace sepNone ats genAttribute

and genAttributes ats = colPost sepNln sepNone ats genAttribute

and genPreXmlDoc (PreXmlDoc _) = sepNone

and breakNln brk e = ifElse brk (indent +> sepNln +> genExpr e +> unindent) (indent +> autoNln (genExpr e) +> unindent)

and preserveBreakNln e ctx = breakNln (checkPreserveBreakForExpr e ctx) e ctx

and noIndentBreakNln e ctx = ifElse (checkPreserveBreakForExpr e ctx) (sepNln +> genExpr e) (autoNln (genExpr e)) ctx

and genTyparList tps = 
    ifElse (List.atMostOne tps) (col wordOr tps genTypar) (sepOpenT +> col wordOr tps genTypar +> sepCloseT)

and genTypeParam tds tcs = 
    ifElse (List.isEmpty tds) sepNone 
        (!-"<" +> col sepComma tds genTyparDecl +> colPre (!-" when ") wordAnd tcs genTypeConstraint -- ">")

and genLetBinding isFirst pref b = 
    match b with
    | LetBinding (ats, px, ao, isInline, isMutable, p, e) -> 
        let prefix = 
            genPreXmlDoc px +> ifElse isFirst (genAttributes ats -- pref) (!-pref +> genOneLinerAttributes ats) 
            +> opt sepSpace ao genAccess +> ifElse isMutable (!-"mutable ") sepNone 
            +> ifElse isInline (!-"inline ") sepNone +> genPat p
        match e with
        | TypedExpr (Typed, e, t) -> prefix +> sepColon +> genType false t +> sepEq +> preserveBreakNln e
        | e -> prefix +> sepEq +> preserveBreakNln e
    | DoBinding (ats, px, e) -> 
        let prefix = 
            if pref.Contains ("let") then pref.Replace ("let", "do")
            else "do "
        genPreXmlDoc px +> genAttributes ats -- prefix +> preserveBreakNln e
    | b -> failwithf "%O isn't a let binding" b

and genProperty prefix ps e = 
    let tuplerize ps = 
        let rec loop acc = 
            function 
            | [p] -> (List.rev acc, p)
            | p1 :: ps -> loop (p1 :: acc) ps
            | [] -> invalidArg "p" "Patterns should not be empty"
        loop [] ps
    match ps with
    | [PatSeq (PatTuple, ps)] -> 
        let (ps, p) = tuplerize ps
        !-prefix 
        +> ifElse (List.atMostOne ps) (col sepComma ps genPat +> sepSpace) 
               (sepOpenT +> col sepComma ps genPat +> sepCloseT +> sepSpace) +> genPat p +> sepEq +> preserveBreakNln e
    | ps -> !-prefix +> col sepSpace ps genPat +> sepEq +> preserveBreakNln e

and genPropertyWithGetSet inter (b1, b2) = 
    match b1, b2 with
    | PropertyBinding (ats, px, ao, isInline, mf1, PatLongIdent (_, s1, ps1, _), e1), 
      PropertyBinding (_, _, _, _, _, PatLongIdent (_, _, ps2, _), e2) -> 
        let prefix = 
            genPreXmlDoc px +> genAttributes ats +> genMemberFlags inter mf1 +> ifElse isInline (!-"inline ") sepNone 
            +> opt sepSpace ao genAccess
        prefix -- s1 +> sepSpace +> indent +> sepNln +> genProperty "with get " ps1 e1 +> sepNln 
        +> genProperty "and set " ps2 e2 +> unindent
    | _ -> sepNone

and genMemberBindingList inter = 
    function 
    | [x] -> genMemberBinding inter x
    | MultilineBindingL (xs, ys) -> 
        let prefix = 
            sepNln +> col (rep 2 sepNln) xs (function 
                                             | Pair (x1, x2) -> genPropertyWithGetSet inter (x1, x2)
                                             | Single x -> genMemberBinding inter x)
        match ys with
        | [] -> prefix
        | _ -> prefix +> rep 2 sepNln +> genMemberBindingList inter ys
    | OneLinerBindingL (xs, ys) -> 
        match ys with
        | [] -> col sepNln xs (genMemberBinding inter)
        | _ -> col sepNln xs (genMemberBinding inter) +> sepNln +> genMemberBindingList inter ys
    | _ -> sepNone

and genMemberBinding inter b = 
    match b with
    | PropertyBinding (ats, px, ao, isInline, mf, p, e) -> 
        let prefix = 
            genPreXmlDoc px +> genAttributes ats +> genMemberFlags inter mf +> ifElse isInline (!-"inline ") sepNone 
            +> opt sepSpace ao genAccess
        let propertyPref = 
            match mf with
            | MFProperty PropertyGet -> " with get "
            | MFProperty PropertySet -> " with set "
            | mf -> failwithf "Unexpected member flags: %O" mf
        match p with
        | PatLongIdent (_, s, ps, _) -> prefix -- s +> genProperty propertyPref ps e
        | p -> failwithf "Unexpected pattern: %O" p
    | MemberBinding (ats, px, ao, isInline, mf, p, e) -> 
        let prefix = 
            genPreXmlDoc px +> genAttributes ats +> genMemberFlags inter mf +> ifElse isInline (!-"inline ") sepNone 
            +> opt sepSpace ao genAccess +> genPat p
        match e with
        | TypedExpr (Typed, e, t) -> prefix +> sepColon +> genType false t +> sepEq +> preserveBreakNln e
        | e -> prefix +> sepEq +> preserveBreakNln e
    | ExplicitCtor (ats, px, ao, p, e, so) -> 
        let prefix = 
            genPreXmlDoc px +> genAttributes ats +> opt sepSpace ao genAccess +> genPat p 
            +> opt sepNone so (sprintf " as %s" >> (!-))
        match e with
        | Sequentials [e1; e2] -> 
            prefix +> sepEq +> indent +> sepNln +> genExpr e1 ++ "then " +> preserveBreakNln e2 +> unindent
        | e -> prefix +> sepEq +> preserveBreakNln e
    | b -> failwithf "%O isn't a member binding" b

and genMemberFlags inter = 
    function 
    | MFMember _ -> !-"member "
    | MFStaticMember _ -> !-"static member "
    | MFConstructor _ -> sepNone
    | MFOverride _ -> ifElse inter (!-"member ") (!-"override ")

and genVal (Val (ats, px, ao, s, t, vi, _)) = 
    let (FunType ts) = (t, vi)
    genPreXmlDoc px +> genAttributes ats 
    +> atCurrentColumn (indent -- "val " +> opt sepSpace ao genAccess -- s +> sepColon +> genTypeList ts +> unindent)

and genRecordFieldName (RecordFieldName (s, eo)) = opt sepNone eo (fun e -> !-s +> sepEq +> preserveBreakNln e)

and genExpr = 
    function 
    | Paren e -> sepOpenT +> genExpr e +> sepCloseT
    | SingleExpr (kind, e) -> str kind +> genExpr e
    | ConstExpr (c) -> genConst c
    | NullExpr -> !-"null"
    | Quote (_, e2, isRaw) -> 
        let e = genExpr e2
        ifElse isRaw (!-"<@@ " +> e -- " @@>") (!-"<@ " +> e -- " @>")
    | TypedExpr (TypeTest, e, t) -> genExpr e -- " :? " +> genType false t
    | TypedExpr (New, e, t) -> 
        !-"new " +> genType false t +> ifElse (hasParenthesis e) sepBeforeArg sepSpace +> genExpr e
    | TypedExpr (Downcast, e, t) -> genExpr e -- " :?> " +> genType false t
    | TypedExpr (Upcast, e, t) -> genExpr e -- " :> " +> genType false t
    | TypedExpr (Typed, e, t) -> genExpr e +> sepColon +> genType false t
    | Tuple es -> atCurrentColumn (colAutoNlnSkip0 sepComma es noIndentBreakNln)
    | ArrayOrList (isArray, xs, isSimple) -> 
        let sep = ifElse isSimple sepSemi sepSemiNln
        ifElse isArray (sepOpenA +> atCurrentColumn (colAutoNlnSkip0 sep xs genExpr) +> sepCloseA) 
            (sepOpenL +> atCurrentColumn (colAutoNlnSkip0 sep xs genExpr) +> sepCloseL)
    | Record (xs, eo) -> 
        sepOpenS +> opt (!-" with ") eo genExpr +> atCurrentColumn (col sepSemiNln xs genRecordFieldName) +> sepCloseS
    | ObjExpr (t, eio, bd, ims) -> 
        let param = opt sepNone (Option.map fst eio) genExpr
        sepOpenS 
        +> atCurrentColumn 
               (!-"new " +> genType false t +> param -- " with" +> indent +> sepNln +> genMemberBindingList true bd 
                +> unindent +> colPre sepNln sepNln ims genInterfaceImpl) +> sepCloseS
    | While (e1, e2) -> 
        atCurrentColumn (!-"while " +> genExpr e1 -- " do" +> indent +> sepNln +> genExpr e2 +> unindent)
    | For (s, e1, e2, e3, isUp) -> 
        atCurrentColumn 
            (!-(sprintf "for %s = " s) +> genExpr e1 +> ifElse isUp (!-" to ") (!-" downto ") +> genExpr e2 -- " do" 
             +> indent +> sepNln +> genExpr e3 +> unindent)
    | ForEach (p, e1, e2, isArrow) -> 
        atCurrentColumn 
            (!-"for " +> genPat p -- " in " +> genExpr e1 
             +> ifElse isArrow (sepArrow +> preserveBreakNln e2) (!-" do" +> indent +> sepNln +> genExpr e2 +> unindent))
    | CompExpr (isArrayOrList, e) -> ifElse isArrayOrList (genExpr e) (preserveBreakNln e)
    | ArrayOrListOfSeqExpr (isArray, e) -> 
        ifElse isArray (sepOpenA +> genExpr e +> sepCloseA) (sepOpenL +> genExpr e +> sepCloseL)
    | JoinIn (e1, e2) -> genExpr e1 -- " in " +> genExpr e2
    | DesugaredLambda (cps, e) -> !-"fun " +> col sepSpace cps genComplexPats +> sepArrow +> preserveBreakNln e
    | Lambda (e, sps) -> !-"fun " +> col sepSpace sps genSimplePats +> sepArrow +> preserveBreakNln e
    | MatchLambda (sp, _) -> atCurrentColumn (!-"function " +> colPre sepNln sepNln sp genClause)
    | Match (e, cs) -> atCurrentColumn (!-"match " +> genExpr e -- " with" +> colPre sepNln sepNln cs genClause)
    | CompApp (s, e) -> !-s +> sepSpace +> sepOpenS +> genExpr e +> sepCloseS
    | App (Var ".. ..", [e1; e2; e3]) -> genExpr e1 -- ".." +> genExpr e2 -- ".." +> genExpr e3
    | PrefixApp (s1, PrefixApp (s2, e)) -> !-(sprintf "%s %s" s1 s2) +> genExpr e
    | PrefixApp (s, e) -> !-s +> genExpr e
    | InfixApps (e, es) -> 
        let hasNewLine = multiline e || not (List.atMostOne es)
        atCurrentColumn (genExpr e +> genInfixApps hasNewLine es)
    | TernaryApp (e1, e2, e3) -> 
        atCurrentColumn (genExpr e1 +> !-"?" +> genExpr e2 +> sepSpace +> !-"<-" +> sepSpace +> genExpr e3)
    | DotGetAppSpecial (s, es) -> 
        !-s 
        +> atCurrentColumn 
               (colAutoNlnSkip0 sepNone es 
                    (fun (s, e) -> (!-(sprintf ".%s" s) +> ifElse (hasParenthesis e) sepBeforeArg sepSpace +> genExpr e)))
    | DotGetApp (e, es) -> 
        noNln (genExpr e) +> indent 
        +> (col sepNone es 
                (fun (s, e) -> 
                    autoNln (!-(sprintf ".%s" s) +> ifElse (hasParenthesis e) sepBeforeArg sepSpace +> genExpr e))) 
        +> unindent
    | App (e1, [e2]) -> 
        atCurrentColumn 
            (genExpr e1 +> ifElse (hasParenthesis e2) sepBeforeArg sepSpace +> indent +> autoNln (genExpr e2) 
             +> unindent)
    | App (e, es) -> 
        atCurrentColumn (genExpr e +> colPre sepSpace sepSpace es (fun e -> indent +> autoNln (genExpr e) +> unindent))
    | TypeApp (e, ts) -> genExpr e -- "<" +> col sepComma ts (genType false) -- ">"
    | LetOrUse (isRec, isUse, bs, e) -> 
        let prefix = 
            if isUse then "use "
            elif isRec then "let rec "
            else "let "
        match bs with
        | b :: bs -> 
            atCurrentColumn 
                (genLetBinding true prefix b +> colPre sepNln sepNln bs (genLetBinding false "and ") +> sepNln 
                 +> genExpr e)
        | _ -> atCurrentColumn (col sepNln bs (genLetBinding true prefix) +> sepNln +> genExpr e)
    | TryWith (e, cs) -> 
        atCurrentColumn 
            (!-"try " +> indent +> sepNln +> genExpr e +> unindent ++ "with" +> indentOnWith +> sepNln 
             +> col sepNln cs genClause +> unindentOnWith)
    | TryFinally (e1, e2) -> 
        atCurrentColumn 
            (!-"try " +> indent +> sepNln +> genExpr e1 +> unindent ++ "finally" +> indent +> sepNln +> genExpr e2 
             +> unindent)
    | SequentialSimple es -> atCurrentColumn (colAutoNlnSkip0 sepSemi es genExpr)
    | Sequentials es -> atCurrentColumn (col sepNln es genExpr)
    | ElIf ((e1, e2, _) :: es, en) -> 
        atCurrentColumn 
            (!-"if " +> ifElse (checkBreakForExpr e1) (genExpr e1 ++ "then") (genExpr e1 +- "then") -- " " 
             +> preserveBreakNln e2 
             +> fun ctx -> 
                 col sepNone es 
                     (fun (e1, e2, r) -> 
                         ifElse (startWith "elif" r ctx) (!+"elif ") (!+"else if ") 
                         +> ifElse (checkBreakForExpr e1) (genExpr e1 ++ "then") (genExpr e1 +- "then") -- " " 
                         +> preserveBreakNln e2) ctx ++ "else " +> preserveBreakNln en)
    | IfThenElse (e1, e2, None) -> 
        atCurrentColumn 
            (!-"if " +> ifElse (checkBreakForExpr e1) (genExpr e1 ++ "then") (genExpr e1 +- "then") -- " " 
             +> preserveBreakNln e2)
    | OptVar (s, isOpt) -> ifElse isOpt (!-"?") sepNone -- s
    | LongIdentSet (s, e) -> !-(sprintf "%s <- " s) +> genExpr e
    | DotIndexedGet (e, es) -> genExpr e -- "." +> sepOpenL +> genIndexedVars es +> sepCloseL
    | DotIndexedSet (e1, es, e2) -> genExpr e1 -- ".[" +> genIndexedVars es -- "] <- " +> genExpr e2
    | DotGet (e, s) -> genExpr e -- sprintf ".%s" s
    | DotSet (e1, s, e2) -> genExpr e1 -- sprintf ".%s <- " s +> genExpr e2
    | TraitCall (tps, msg, e) -> 
        sepOpenT +> genTyparList tps +> sepColon +> sepOpenT +> genMemberSig msg +> sepCloseT +> sepSpace +> genExpr e 
        +> sepCloseT
    | LetOrUseBang (isUse, p, e1, e2) -> 
        atCurrentColumn 
            (ifElse isUse (!-"use! ") (!-"let! ") +> genPat p -- " = " +> genExpr e1 +> sepNln +> genExpr e2)
    | e -> failwithf "Unexpected expression: %O" e

and genInfixApps newline = 
    function 
    | (s, e) :: es -> 
        (ifElse (newline && NewLineInfixOps.Contains s) (sepNln -- s +> sepSpace +> genExpr e) 
             (ifElse (NoSpaceInfixOps.Contains s) (!-s +> autoNln (genExpr e)) 
                  (ifElse (NoBreakInfixOps.Contains s) (sepSpace -- s +> sepSpace +> genExpr e) 
                       (sepSpace +> autoNln (!-s +> sepSpace +> genExpr e))))) +> genInfixApps newline es
    | [] -> sepNone

and genIndexedVars es = 
    match es with
    | IndexedVar eo1 :: es -> 
        match es with
        | IndexedVar eo2 :: es -> 
            ifElse (eo1.IsNone && eo2.IsNone) (!-"*") (opt sepNone eo1 genExpr -- ".." +> opt sepNone eo2 genExpr) 
            +> ifElse es.IsEmpty sepNone (sepComma +> genIndexedVars es)
        | _ -> opt sepNone eo1 genExpr +> ifElse es.IsEmpty sepNone (sepComma +> genIndexedVars es)
    | [e] -> genExpr e
    | e :: es -> genExpr e +> sepComma +> genIndexedVars es
    | [] -> sepNone

and genTypeDefn isFirst (TypeDef (ats, px, ao, tds, tcs, tdr, ms, s)) = 
    let typeName = 
        genPreXmlDoc px 
        +> ifElse isFirst (colPost sepNln sepNln ats genAttribute -- "type ") (!-"and " +> genOneLinerAttributes ats) 
        +> opt sepSpace ao genAccess -- s +> genTypeParam tds tcs
    match tdr with
    | Simple (TDSREnum ecs) -> 
        typeName +> sepEq +> indent +> sepNln +> col sepNln ecs (genEnumCase true) +> genMemberDefnList false ms 
        +> unindent
    | Simple (TDSRUnion (ao', xs)) -> 
        typeName +> sepEq +> indent +> sepNln +> opt sepNln ao' genAccess +> col sepNln xs (genUnionCase true) 
        +> genMemberDefnList false ms +> unindent
    | Simple (TDSRRecord (ao', fs)) -> 
        typeName +> sepEq +> indent +> sepNln +> opt sepNln ao' genAccess +> sepOpenS 
        +> atCurrentColumn (col sepSemiNln fs (genField false "")) +> sepCloseS +> genMemberDefnList false ms 
        +> unindent
    | Simple TDSRNone -> typeName
    | Simple (TDSRTypeAbbrev t) -> typeName +> sepEq +> genType false t
    | ObjectModel (TCSimple (TCStruct | TCInterface | TCClass) as tdk, MemberDefnList (impCtor, others)) -> 
        let inter = 
            match tdk with
            | TCSimple TCInterface -> true
            | _ -> false
        typeName +> optPre sepBeforeArg sepNone impCtor (genMemberDefn inter) +> sepEq +> indent +> sepNln 
        +> genTypeDefKind tdk +> indent +> genMemberDefnList inter others +> unindent ++ "end" +> unindent
    | ObjectModel (TCSimple TCAugmentation, _) -> 
        typeName -- " with" +> indent +> genMemberDefnList false ms +> unindent
    | ObjectModel (TCDelegate (FunType ts), _) -> typeName +> sepEq -- "delegate of " +> genTypeList ts
    | ObjectModel (_, MemberDefnList (impCtor, others)) -> 
        typeName +> optPre sepBeforeArg sepNone impCtor (genMemberDefn false) +> sepEq +> indent 
        +> genMemberDefnList false others +> unindent

and genSigTypeDefn isFirst (SigTypeDef (ats, px, ao, tds, tcs, tdr, ms, s)) = 
    let typeName = 
        genPreXmlDoc px 
        +> ifElse isFirst (colPost sepNln sepNln ats genAttribute -- "type ") (!-"and " +> genOneLinerAttributes ats) 
        +> opt sepSpace ao genAccess -- s +> genTypeParam tds tcs
    match tdr with
    | SigSimple (TDSREnum ecs) -> 
        typeName +> sepEq +> indent +> sepNln +> col sepNln ecs (genEnumCase true) 
        +> colPre sepNln sepNln ms genMemberSig +> unindent
    | SigSimple (TDSRUnion (ao', xs)) -> 
        typeName +> sepEq +> indent +> sepNln +> opt sepNln ao' genAccess +> col sepNln xs (genUnionCase true) 
        +> colPre sepNln sepNln ms genMemberSig +> unindent
    | SigSimple (TDSRRecord (ao', fs)) -> 
        typeName +> sepEq +> indent +> sepNln +> opt sepNln ao' genAccess +> sepOpenS 
        +> atCurrentColumn (col sepSemiNln fs (genField false "")) +> sepCloseS +> colPre sepNln sepNln ms genMemberSig 
        +> unindent
    | SigSimple TDSRNone -> typeName
    | SigSimple (TDSRTypeAbbrev t) -> typeName +> sepEq +> genType false t
    | SigObjectModel (TCSimple (TCStruct | TCInterface | TCClass) as tdk, mds) -> 
        typeName +> sepEq +> indent +> sepNln +> genTypeDefKind tdk +> indent +> colPre sepNln sepNln mds genMemberSig 
        +> unindent ++ "end" +> unindent
    | SigObjectModel (TCSimple TCAugmentation, _) -> 
        typeName -- " with" +> indent +> sepNln +> col sepNln ms genMemberSig +> unindent
    | SigObjectModel (TCDelegate (FunType ts), _) -> typeName +> sepEq -- "delegate of " +> genTypeList ts
    | SigObjectModel (_, mds) -> typeName +> sepEq +> indent +> sepNln +> col sepNln mds genMemberSig +> unindent

and genMemberSig = 
    function 
    | MSMember (Val (ats, px, ao, s, t, vi, _), mf) -> 
        let (FunType ts) = (t, vi)
        genPreXmlDoc px +> genOneLinerAttributes ats 
        +> atCurrentColumn 
               (indent +> genMemberFlags false mf +> opt sepNone ao genAccess +> ifElse (s = "``new``") (!-"new") (!-s) 
                +> sepColon +> genTypeList ts +> unindent)
    | MSInterface t -> !-"interface " +> genType false t
    | MSInherit t -> !-"inherit " +> genType false t
    | MSValField f -> genField false "val " f
    | MSNestedType _ -> invalidArg "md" "This is not implemented in F# compiler"

and genTyparDecl (TyparDecl (ats, tp)) = genOneLinerAttributes ats +> genTypar tp

and genTypeDefKind = 
    function 
    | TCSimple TCUnspecified -> sepNone
    | TCSimple TCClass -> !-"class"
    | TCSimple TCInterface -> !-"interface"
    | TCSimple TCStruct -> !-"struct"
    | TCSimple TCRecord -> sepNone
    | TCSimple TCUnion -> sepNone
    | TCSimple TCAbbrev -> sepNone
    | TCSimple TCHiddenRepr -> sepNone
    | TCSimple TCAugmentation -> sepNone
    | TCSimple TCILAssemblyCode -> sepNone
    | TCDelegate _ -> sepNone

and genException (ExceptionDef (ats, px, ao, uc, ms)) = 
    genPreXmlDoc px +> genAttributes ats -- "exception " +> opt sepSpace ao genAccess +> genUnionCase false uc 
    +> genMemberDefnList false ms

and genSigException (SigExceptionDef (ats, px, ao, uc, ms)) = 
    genPreXmlDoc px +> genAttributes ats -- "exception " +> opt sepSpace ao genAccess +> genUnionCase false uc 
    +> colPre sepNln sepNln ms genMemberSig

and genUnionCase hasBar (UnionCase (ats, px, _, s, UnionCaseType fs)) = 
    genPreXmlDoc px +> ifElse hasBar sepBar sepNone +> genOneLinerAttributes ats -- s 
    +> colPre wordOf sepStar fs (genField true "")

and genEnumCase hasBar (EnumCase (ats, px, s, c)) = 
    genPreXmlDoc px +> ifElse hasBar sepBar sepNone +> genOneLinerAttributes ats -- s +> sepEq +> genConst c

and genField isUnion prefix (Field (ats, px, ao, isStatic, isMutable, t, so)) = 
    let t = genType isUnion t
    genPreXmlDoc px +> genOneLinerAttributes ats -- prefix +> opt sepSpace ao genAccess 
    +> ifElse isStatic (!-"static ") sepNone +> ifElse isMutable (!-"mutable ") sepNone +> opt sepColon so (!-) +> t

and genType outerBracket t = 
    let rec loop = 
        function 
        | THashConstraint t -> !-"#" +> loop t
        | TMeasurePower (t, n) -> loop t -- "^" +> str n
        | TMeasureDivide (t1, t2) -> loop t1 -- " / " +> loop t2
        | TStaticConstant (c) -> genConst c
        | TStaticConstantExpr (e) -> genExpr e
        | TStaticConstantNamed (t1, t2) -> loop t1 -- "=" +> loop t2
        | TArray (t, n) -> loop t -- " [" +> rep (n - 1) (!-",") -- "]"
        | TAnon -> sepWild
        | TVar tp -> genTypar tp
        | TFun (TTuple ts, t) -> sepOpenT +> col sepStar ts loop +> sepArrow +> loop t +> sepCloseT
        | TFun (t, TTuple ts) -> sepOpenT +> loop t +> sepArrow +> col sepStar ts loop +> sepCloseT
        | TFuns ts -> sepOpenT +> col sepArrow ts loop +> sepCloseT
        | TApp (t, ts, isPostfix) -> 
            let postForm = 
                match ts with
                | [] -> loop t
                | [t'] -> loop t' +> sepSpace +> loop t
                | ts -> sepOpenT +> col sepComma ts loop +> sepCloseT +> loop t
            ifElse isPostfix postForm (loop t +> genPrefixTypes ts)
        | TLongIdentApp (t, s, ts) -> loop t -- sprintf ".%s" s +> genPrefixTypes ts
        | TTuple ts -> sepOpenT +> col sepStar ts loop +> sepCloseT
        | TWithGlobalConstraints (t, tcs) -> loop t +> colPre (!-" when ") wordAnd tcs genTypeConstraint
        | TLongIdent s -> !-s
        | t -> failwithf "Unexpected type: %O" t
    match t with
    | TFun (TTuple ts, t) -> 
        ifElse outerBracket (sepOpenT +> col sepStar ts loop +> sepArrow +> loop t +> sepCloseT) 
            (col sepStar ts loop +> sepArrow +> loop t)
    | TFuns ts -> ifElse outerBracket (sepOpenT +> col sepArrow ts loop +> sepCloseT) (col sepArrow ts loop)
    | TTuple ts -> ifElse outerBracket (sepOpenT +> col sepStar ts loop +> sepCloseT) (col sepStar ts loop)
    | _ -> loop t

and genPrefixTypes = 
    function 
    | [] -> sepNone
    | (TStaticConstant _ | TStaticConstantExpr _ | TStaticConstantNamed _ as t) :: ts -> 
        !-"< " +> col sepComma (t :: ts) (genType false) -- " >"
    | ts -> !-"<" +> col sepComma ts (genType false) -- ">"

and genTypeList = 
    function 
    | [] -> sepNone
    | (t, [ArgInfo (so, isOpt)]) :: ts -> 
        let gt = 
            match t with
            | TTuple _ -> 
                opt sepColonFixed so (if isOpt then (sprintf "?%s" >> (!-))
                                      else (!-)) +> genType (not ts.IsEmpty) t
            | TFun _ -> 
                opt sepColonFixed so (if isOpt then (sprintf "?%s" >> (!-))
                                      else (!-)) +> genType true t
            | _ -> opt sepColonFixed so (!-) +> genType false t
        gt +> ifElse ts.IsEmpty sepNone (autoNln (sepArrow +> genTypeList ts))
    | (TTuple ts', ais) :: ts -> 
        let gt = 
            col sepStar (Seq.zip ais ts') (fun ((ArgInfo (so, isOpt)), t) -> 
                    opt sepColonFixed so (if isOpt then (sprintf "?%s" >> (!-))
                                          else (!-)) +> genType (not ts.IsEmpty) t)
        gt +> ifElse ts.IsEmpty sepNone (autoNln (sepArrow +> genTypeList ts))
    | (t, _) :: ts -> 
        let gt = genType false t
        gt +> ifElse ts.IsEmpty sepNone (autoNln (sepArrow +> genTypeList ts))

and genTypar (Typar (s, isHead)) = 
    ifElse isHead (!-"^") (!-"'") -- s

and genTypeConstraint = 
    function 
    | TyparSingle (kind, tp) -> genTypar tp +> sepColon -- sprintf "%O" kind
    | TyparDefaultsToType (tp, t) -> !-"default " +> genTypar tp +> sepColon +> genType false t
    | TyparSubtypeOfType (tp, t) -> genTypar tp -- " :> " +> genType false t
    | TyparSupportsMember (tps, msg) -> genTyparList tps +> sepColon +> sepOpenT +> genMemberSig msg +> sepCloseT
    | TyparIsEnum (tp, ts) -> genTypar tp +> sepColon -- "enum<" +> col sepComma ts (genType false) -- ">"
    | TyparIsDelegate (tp, ts) -> genTypar tp +> sepColon -- "delegate<" +> col sepComma ts (genType false) -- ">"

and genInterfaceImpl (InterfaceImpl (t, bs)) = 
    !-"interface " +> genType false t -- " with" +> indent +> sepNln +> genMemberBindingList true bs +> unindent

and genClause (Clause (p, e, eo)) = 
    sepBar +> genPat p +> optPre (!-" when ") sepNone eo genExpr +> sepArrow +> preserveBreakNln e

and genMemberDefnList inter = 
    function 
    | [x] -> sepNln +> genMemberDefn inter x
    | MDOpenL (xs, ys) -> 
        let xs = xs |> sortAndDedup ((|MDOpen|_|) >> Option.get)
        match ys with
        | [] -> col sepNln xs (genMemberDefn inter)
        | _ -> col sepNln xs (genMemberDefn inter) +> rep 2 sepNln +> genMemberDefnList inter ys
    | MultilineMemberDefnL (xs, []) -> 
        rep 2 sepNln +> col (rep 2 sepNln) xs (function 
                                               | Pair (x1, x2) -> genPropertyWithGetSet inter (x1, x2)
                                               | Single x -> genMemberDefn inter x)
    | MultilineMemberDefnL (xs, ys) -> 
        rep 2 sepNln +> col (rep 2 sepNln) xs (function 
                                               | Pair (x1, x2) -> genPropertyWithGetSet inter (x1, x2)
                                               | Single x -> genMemberDefn inter x) +> sepNln 
        +> genMemberDefnList inter ys
    | OneLinerMemberDefnL (xs, ys) -> sepNln +> col sepNln xs (genMemberDefn inter) +> genMemberDefnList inter ys
    | _ -> sepNone

and genMemberDefn inter = 
    function 
    | MDNestedType _ -> invalidArg "md" "This is not implemented in F# compiler"
    | MDOpen (s) -> !-s
    | MDImplicitInherit (t, e, _) -> !-"inherit " +> genType false t +> genExpr e
    | MDInherit (t, _) -> !-"inherit " +> genType false t
    | MDValField f -> genField false "val " f
    | MDImplicitCtor (ats, ao, ps, so) -> 
        optPre sepSpace sepSpace ao genAccess +> sepOpenT +> genOneLinerAttributes ats +> col sepComma ps genSimplePat 
        +> sepCloseT +> optPre (!-" as ") sepNone so (!-)
    | MDMember (b) -> genMemberBinding inter b
    | MDLetBindings (isStatic, isRec, b :: bs) -> 
        let prefix = 
            if isStatic && isRec then "static let rec "
            elif isStatic then "static let "
            elif isRec then "let rec "
            else "let "
        genLetBinding true prefix b +> colPre sepNln sepNln bs (genLetBinding false "and")
    | MDInterface (t, mdo) -> 
        !-"interface " +> genType false t 
        +> opt sepNone mdo (fun mds -> !-" with" +> indent +> genMemberDefnList true mds +> unindent)
    | MDAutoProperty (ats, px, ao, mk, e, s) -> 
        genPreXmlDoc px +> genOneLinerAttributes ats -- "member val " +> opt sepSpace ao genAccess -- s +> sepEq 
        +> genExpr e -- propertyKind mk
    | MDAbstractSlot (ats, px, ao, s, t, ValTyparDecls (tds, _, tcs), MFMemberFlags mk) -> 
        genPreXmlDoc px +> genOneLinerAttributes ats +> opt sepSpace ao genAccess -- sprintf "abstract %s" s 
        +> genTypeParam tds tcs +> sepColon +> genType false t -- propertyKind mk
    | md -> failwithf "Unexpected member definition: %O" md

and propertyKind = 
    function 
    | PropertyGet -> " with get"
    | PropertySet -> " with set"
    | PropertyGetSet -> " with get, set"
    | _ -> ""

and genSimplePat = 
    function 
    | SPId (s, isOptArg, _) -> ifElse isOptArg (!-(sprintf "?%s" s)) (!-s)
    | SPTyped (sp, t) -> genSimplePat sp +> sepColon +> genType false t
    | SPAttrib (ats, sp) -> genOneLinerAttributes ats +> genSimplePat sp

and genSimplePats = 
    function 
    | SimplePats [SPId _ as sp] -> genSimplePat sp
    | SimplePats ps -> sepOpenT +> col sepComma ps genSimplePat +> sepCloseT
    | SPSTyped (ps, t) -> genSimplePats ps +> sepColon +> genType false t

and genComplexPat = 
    function 
    | CPId p -> genPat p
    | CPSimpleId (s, isOptArg, _) -> ifElse isOptArg (!-(sprintf "?%s" s)) (!-s)
    | CPTyped (sp, t) -> genComplexPat sp +> sepColon +> genType false t
    | CPAttrib (ats, sp) -> colPost sepSpace sepNone ats genAttribute +> genComplexPat sp

and genComplexPats = 
    function 
    | ComplexPats [c] -> genComplexPat c
    | ComplexPats ps -> sepOpenT +> col sepComma ps genComplexPat +> sepCloseT
    | ComplexTyped (ps, t) -> genComplexPats ps +> sepColon +> genType false t

and genPatRecordFieldName (PatRecordFieldName (s1, s2, p)) = 
    ifElse (s1 = "") (!-(sprintf "%s = " s2)) (!-(sprintf "%s.%s = " s1 s2)) +> genPat p

and genPat = 
    function 
    | PatOptionalVal (s) -> !-(sprintf "?%s" s)
    | PatAttrib (p, ats) -> genOneLinerAttributes ats +> genPat p
    | PatOr (p1, p2) -> genPat p1 -- " | " +> genPat p2
    | PatAnds (ps) -> col (!-" & ") ps genPat
    | PatNullary PatNull -> !-"null"
    | PatNullary PatWild -> sepWild
    | PatTyped (p, t) -> genPat p +> sepColon +> genType false t
    | PatNamed (ao, PatNullary PatWild, s) -> opt sepSpace ao genAccess -- s
    | PatNamed (ao, p, s) -> opt sepSpace ao genAccess +> genPat p -- sprintf " as %s" s
    | PatLongIdent (ao, s, ps, tpso) -> 
        let aoc = opt sepSpace ao genAccess
        let tpsoc = opt sepNone tpso (fun (ValTyparDecls (tds, _, tcs)) -> genTypeParam tds tcs)
        let s = 
            if s = "``new``" then "new"
            else s
        match ps with
        | [] -> aoc -- s +> tpsoc
        | [PatSeq (PatTuple, [p1; p2])] when s = "(::)" -> aoc +> genPat p1 -- " :: " +> genPat p2
        | [p] -> aoc -- s +> tpsoc +> ifElse (hasParenInPat p) sepBeforeArg sepSpace +> genPat p
        | ps -> atCurrentColumn (aoc -- s +> tpsoc +> sepSpace +> colAutoNlnSkip0 sepSpace ps genPat)
    | PatParen (PatConst (c)) -> genConst c
    | PatParen (p) -> sepOpenT +> genPat p +> sepCloseT
    | PatSeq (PatTuple, ps) -> atCurrentColumn (colAutoNlnSkip0 sepComma ps genPat)
    | PatSeq (PatList, ps) -> sepOpenL +> atCurrentColumn (colAutoNlnSkip0 sepSemi ps genPat) +> sepCloseL
    | PatSeq (PatArray, ps) -> sepOpenA +> atCurrentColumn (colAutoNlnSkip0 sepSemi ps genPat) +> sepCloseA
    | PatRecord (xs) -> sepOpenS +> atCurrentColumn (colAutoNlnSkip0 sepSemi xs genPatRecordFieldName) +> sepCloseS
    | PatConst (c) -> genConst c
    | PatIsInst (t) -> !-":? " +> genType false t
    | PatQuoteExpr e -> genExpr e
    | p -> failwithf "Unexpected pattern: %O" p

Auto format when finishing block

It would be nice if fantomas would trigger a reformating of the block where the caret is when we close a }, ), or ], like C# does on } and ;

Slightly incorrect indentation when comment is present

Formatting this code

let f x = 
  a || // other case
        match n with
        | 17 -> false
        | _ -> true

gives the ever-so-slightly incorrect

let f x = 
  a || // other case
        match n with
       | 17 -> false
       | _ -> true

where the "match" is indented one space too far.

Space before argument option is too general

When set to false, we get this:

x.ToLower()
Seq.map(fun x -> x * 2)
Console.Writeline("something")
Some(a, b) -> ()

But when it's false we get this:

x.ToLower ()
Seq.map (fun x -> x * 2)
Console.Writeline ("something")
| Some (a, b) -> ()

What I would like to get is this:

x.ToLower()
Seq.map (fun x -> x * 2)
Console.Writeline("something")
| Some (a, b) -> ()

My suggestion is to remove the option and:

  1. If it's the unit () argument, don't add a space before the parens
  2. If it's a function with curried arguments like Seq.map etc., add a space before the parens
  3. If it's a function with tupled arguments, don't add a space before the parens
  4. If it's a DU constructor, add the space before the parens

What do you think?

Parenthesis in signatures are not preserved

// before
type IA =
    abstract F: (unit -> Option<'T>) -> Option<'T>

type A () =
    interface IA with
        member x.F (f: unit -> _) = f ()

// after
type IA = 
    abstract F: unit -> Option<'T> -> Option<'T>

type A () = 
    interface IA with
        // error: This override takes a different number of arguments to the corresponding abstract member
        member x.F (f: unit -> _) = f () 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.