diff --git a/src/Fantomas.Tests/ClassTests.fs b/src/Fantomas.Tests/ClassTests.fs index 79919a7214..1a403c272f 100644 --- a/src/Fantomas.Tests/ClassTests.fs +++ b/src/Fantomas.Tests/ClassTests.fs @@ -345,7 +345,7 @@ System.String.Concat ("a", "b" + (longNamedFunlongNamedFunlongNamedFunlongNamedFunlongNamedFun - (longNamedClasslongNamedClasslongNamedClasslongNamedClasslongNamedClasslongNamedClass)).Property) + (longNamedClasslongNamedClasslongNamedClasslongNamedClasslongNamedClasslongNamedClass)).Property) """ [] @@ -360,5 +360,5 @@ type Exception with type Exception with member inline __.FirstLine = (__.Message.Split - ([| Environment.NewLine |], StringSplitOptions.RemoveEmptyEntries)).[0] + ([| Environment.NewLine |], StringSplitOptions.RemoveEmptyEntries)).[0] """ \ No newline at end of file diff --git a/src/Fantomas.Tests/ControlStructureTests.fs b/src/Fantomas.Tests/ControlStructureTests.fs index ccec9c636a..cfc2628cb9 100644 --- a/src/Fantomas.Tests/ControlStructureTests.fs +++ b/src/Fantomas.Tests/ControlStructureTests.fs @@ -222,7 +222,7 @@ let x = |> should equal """ let x = if try - true + true with Failure _ -> false then () else () diff --git a/src/Fantomas.Tests/DataStructureTests.fs b/src/Fantomas.Tests/DataStructureTests.fs index c564c6344b..ac3aab03ea 100644 --- a/src/Fantomas.Tests/DataStructureTests.fs +++ b/src/Fantomas.Tests/DataStructureTests.fs @@ -108,8 +108,8 @@ let ``should keep -> notation``() = |> should equal """ let environVars target = [ for e in Environment.GetEnvironmentVariables target -> - let e1 = e :?> Collections.DictionaryEntry - e1.Key, e1.Value ] + let e1 = e :?> Collections.DictionaryEntry + e1.Key, e1.Value ] """ [] @@ -140,7 +140,7 @@ let a2 = [| 0..99 |] let a3 = [| for n in 1..100 do - if isPrime n then yield n |] + if isPrime n then yield n |] """ [] diff --git a/src/Fantomas.Tests/FormattingSelectionTests.fs b/src/Fantomas.Tests/FormattingSelectionTests.fs index 3359df5bc1..fa539adfd6 100644 --- a/src/Fantomas.Tests/FormattingSelectionTests.fs +++ b/src/Fantomas.Tests/FormattingSelectionTests.fs @@ -223,7 +223,7 @@ let a3 = |> should equal """ let a3 = [| for n in 1..100 do - if isPrime n then yield n |]""" + if isPrime n then yield n |]""" [] let ``should format around the cursor inside an object expression``() = @@ -233,7 +233,7 @@ let ``should format around the cursor inside an object expression``() = |> should equal """ let obj1 = { new System.Object() with - member x.ToString() = "F#" }""" + member x.ToString() = "F#" }""" [] let ``should format around the cursor inside a computation expression``() = diff --git a/src/Fantomas.Tests/InterfaceTests.fs b/src/Fantomas.Tests/InterfaceTests.fs index 88b116443c..20a2fa9351 100644 --- a/src/Fantomas.Tests/InterfaceTests.fs +++ b/src/Fantomas.Tests/InterfaceTests.fs @@ -55,7 +55,7 @@ let ``object expressions``() = |> should equal """ let obj1 = { new System.Object() with - member x.ToString() = "F#" } + member x.ToString() = "F#" } """ [] @@ -72,8 +72,8 @@ let ``object expressions and interfaces``() = |> should equal """ let implementer() = { new ISecond with - member this.H() = () - member this.J() = () + member this.H() = () + member this.J() = () interface IFirst with member this.F() = () member this.G() = () } @@ -92,7 +92,7 @@ let f () = |> should equal """ let f() = { new obj() with - member x.ToString() = "INotifyEnumerableInternal" + member x.ToString() = "INotifyEnumerableInternal" interface INotifyEnumerableInternal<'T> interface IEnumerable<_> with member x.GetEnumerator() = null } diff --git a/src/Fantomas.Tests/LetBindingTests.fs b/src/Fantomas.Tests/LetBindingTests.fs index 7fc38fa44a..ec7e7db1a0 100644 --- a/src/Fantomas.Tests/LetBindingTests.fs +++ b/src/Fantomas.Tests/LetBindingTests.fs @@ -76,7 +76,7 @@ let f () = |> should equal """let f() = let x = 1 (while true do - () + () x) """ @@ -110,8 +110,8 @@ let x = let x = [| 1..2 |] |> Array.mapi (fun _ _ -> - let num = - "" - .PadLeft(9) - num) + let num = + "" + .PadLeft(9) + num) """ \ No newline at end of file diff --git a/src/Fantomas.Tests/PatternMatchingTests.fs b/src/Fantomas.Tests/PatternMatchingTests.fs index 2197249fa2..648f913f04 100644 --- a/src/Fantomas.Tests/PatternMatchingTests.fs +++ b/src/Fantomas.Tests/PatternMatchingTests.fs @@ -203,8 +203,8 @@ with try fst (find - (fun (s, (s', ty) : int * int) -> - s' = s0 && can (type_match ty ty0) []) (!the_interface)) + (fun (s, (s', ty) : int * int) -> + s' = s0 && can (type_match ty ty0) []) (!the_interface)) with Failure _ -> s0 """ diff --git a/src/Fantomas.Tests/PipingTests.fs b/src/Fantomas.Tests/PipingTests.fs index 4f8ae39066..e5e7115e53 100644 --- a/src/Fantomas.Tests/PipingTests.fs +++ b/src/Fantomas.Tests/PipingTests.fs @@ -73,6 +73,6 @@ let prefetchImages = let prefetchImages = [ playerOImage; playerXImage ] |> List.map (fun img -> - link [ Rel "prefetch" - Href img ]) + link [ Rel "prefetch" + Href img ]) """ diff --git a/src/Fantomas.Tests/TypeDeclarationTests.fs b/src/Fantomas.Tests/TypeDeclarationTests.fs index 0e515d4770..96395bfee1 100644 --- a/src/Fantomas.Tests/TypeDeclarationTests.fs +++ b/src/Fantomas.Tests/TypeDeclarationTests.fs @@ -619,7 +619,7 @@ type BlobHelper(Account : CloudStorageAccount) = configSettingPublisher.Invoke(connectionString) |> ignore) BlobHelper (CloudStorageAccount.FromConfigurationSetting - (configurationSettingName)) + (configurationSettingName)) """ [] diff --git a/src/Fantomas/CodeFormatterImpl.fs b/src/Fantomas/CodeFormatterImpl.fs index 3106a14dde..e135064646 100644 --- a/src/Fantomas/CodeFormatterImpl.fs +++ b/src/Fantomas/CodeFormatterImpl.fs @@ -541,7 +541,7 @@ let formatRange returnFormattedContentOnly (range : range) (lines : _ []) config // Mono version of indent text writer behaves differently from .NET one, // So we add an empty string first to regularize it |> if returnFormattedContentOnly then Context.str String.Empty else Context.str pre - |> Context.atIndentLevel startCol (Context.col Context.sepNln formatteds Context.str) + |> Context.atIndentLevel true startCol (Context.col Context.sepNln formatteds Context.str) |> if returnFormattedContentOnly then Context.str String.Empty else Context.str post |> Context.dump diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index 0db712f575..6eb34365de 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -518,7 +518,7 @@ and genExpr astContext synExpr = (sepOpenL +> atCurrentColumn (colAutoNlnSkip0 sepWithPreserveEndOfLine xs (genExpr astContext)) +> sepCloseL) | Record(inheritOpt, xs, eo) -> - let recordExpr = opt (!- " with ") eo (genExpr astContext) +> atCurrentColumn (col sepSemiNln xs (genRecordFieldName astContext)) + let recordExpr = opt (!- " with ") eo (genExpr astContext) +> atCurrentColumnIndent (col sepSemiNln xs (genRecordFieldName astContext)) sepOpenS +> atCurrentColumn (opt (if xs.IsEmpty then sepNone else ifElseCtx (futureNlnCheck recordExpr sepNone) sepNln sepSemi) inheritOpt (fun (typ, expr) -> !- "inherit " +> genType astContext false typ +> genExpr astContext expr)) diff --git a/src/Fantomas/Context.fs b/src/Fantomas/Context.fs index c691278946..03ba0735b4 100644 --- a/src/Fantomas/Context.fs +++ b/src/Fantomas/Context.fs @@ -13,13 +13,26 @@ type ColumnIndentedTextWriter(tw : TextWriter) = let indentWriter = new IndentedTextWriter(tw, " ") let mutable col = indentWriter.Indent + // on newline, bigger from Indent and atColumn is selected + // that way we avoid bigger than indentSpace indentation when indent is used after atCurrentColumn + let mutable atColumn = 0 + + let applyAtColumn f = + let newIndent = f atColumn + indentWriter.Indent <- newIndent + + member __.ApplyAtColumn f = applyAtColumn f + member __.Write(s : string) = match s.LastIndexOf('\n') with | -1 -> col <- col + s.Length - | i -> col <- s.Length - i - 1 + | i -> + applyAtColumn (fun x -> max indentWriter.Indent x) + col <- s.Length - i - 1 indentWriter.Write(s) member __.WriteLine(s : string) = + applyAtColumn (fun x -> max indentWriter.Indent x) col <- indentWriter.Indent indentWriter.WriteLine(s) @@ -32,6 +45,10 @@ type ColumnIndentedTextWriter(tw : TextWriter) = with get() = indentWriter.Indent and set i = indentWriter.Indent <- i + member __.AtColumn + with get() = atColumn + and set i = atColumn <- i + member __.InnerWriter = indentWriter.InnerWriter interface IDisposable with @@ -74,6 +91,7 @@ type internal Context = member x.With(writer : ColumnIndentedTextWriter) = writer.Indent <- x.Writer.Indent writer.Column <- x.Writer.Column + writer.AtColumn <- x.Writer.AtColumn // Use infinite column width to encounter worst-case scenario let config = { x.Config with PageWidth = Int32.MaxValue } { x with Writer = writer; Config = config } @@ -86,11 +104,13 @@ let internal dump (ctx: Context) = /// Indent one more level based on configuration let internal indent (ctx : Context) = ctx.Writer.Indent <- ctx.Writer.Indent + ctx.Config.IndentSpaceNum + // if atColumn is bigger then after indent, then we use atColumn as base for indent + ctx.Writer.ApplyAtColumn (fun x -> if x >= ctx.Writer.Indent then x + ctx.Config.IndentSpaceNum else ctx.Writer.Indent) ctx /// Unindent one more level based on configuration let internal unindent (ctx : Context) = - ctx.Writer.Indent <- max 0 (ctx.Writer.Indent - ctx.Config.IndentSpaceNum) + ctx.Writer.Indent <- max ctx.Writer.AtColumn (ctx.Writer.Indent - ctx.Config.IndentSpaceNum) ctx /// Increase indent by i spaces @@ -104,18 +124,37 @@ let internal decrIndent i (ctx : Context) = ctx /// Apply function f at an absolute indent level (use with care) -let internal atIndentLevel level (f : Context -> Context) ctx = +let internal atIndentLevel alsoSetIndent level (f : Context -> Context) ctx = if level < 0 then invalidArg "level" "The indent level cannot be negative." let oldLevel = ctx.Writer.Indent - ctx.Writer.Indent <- level + let oldColumn = ctx.Writer.AtColumn + if alsoSetIndent then ctx.Writer.Indent <- level + ctx.Writer.AtColumn <- level let result = f ctx + ctx.Writer.AtColumn <- oldColumn ctx.Writer.Indent <- oldLevel result -/// Write everything at current column indentation +/// Set minimal indentation (`atColumn`) at current column position - next newline will be indented on `max indent atColumn` +/// Example: +/// { X = // indent=0, atColumn=2 +/// "some long string" // indent=4, atColumn=2 +/// Y = 1 // indent=0, atColumn=2 +/// } +/// `atCurrentColumn` was called on `X`, then `indent` was called, but "some long string" have indent only 4, because it is bigger than `atColumn` (2). let internal atCurrentColumn (f : _ -> Context) (ctx : Context) = - atIndentLevel ctx.Writer.Column f ctx + atIndentLevel false ctx.Writer.Column f ctx + +/// Write everything at current column indentation, set `indent` and `atColumn` on current column position +/// /// Example (same as above): +/// { X = // indent=2, atColumn=2 +/// "some long string" // indent=6, atColumn=2 +/// Y = 1 // indent=2, atColumn=2 +/// } +/// `atCurrentColumn` was called on `X`, then `indent` was called, "some long string" have indent 6, because it is indented from `atCurrentColumn` pos (2). +let internal atCurrentColumnIndent (f : _ -> Context) (ctx : Context) = + atIndentLevel true ctx.Writer.Column f ctx /// Function composition operator let internal (+>) (ctx : Context -> Context) (f : _ -> Context) x =