diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b78b2047e..979f30120f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [6.0.0-alpha-008] - 2023-03-27 + +### Fixed +* Nested multiline record with indent_size = 2. [#2801](https://github.com/fsprojects/fantomas/issues/2801) +* Idempotency problem when comment after opening brace in inherit record. [#2803](https://github.com/fsprojects/fantomas/issues/2803) + ## [6.0.0-alpha-007] - 2023-03-27 ### Changed diff --git a/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs b/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs index 8752786b19..1aed588631 100644 --- a/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs +++ b/src/Fantomas.Core.Tests/AlignedMultilineBracketStyleTests.fs @@ -1593,3 +1593,42 @@ struct // 1 // 5 |} // 6 """ + +[] +let ``multiline field body expression where indent_size = 2`` () = + formatSourceString + false + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + + [| { Range = range; NewText = formatted } |] +""" + { config with IndentSize = 2 } + |> prepend newline + |> should + equal + """ +let handlerFormattedRangeDoc (lines : NamedText, formatted : string, range : FormatSelectionRange) = + let range = + { + Start = + { + Line = range.StartLine - 1 + Character = range.StartColumn + } + End = + { + Line = range.EndLine - 1 + Character = range.EndColumn + } + } + + [| { Range = range ; NewText = formatted } |] +""" diff --git a/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs b/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs index 4d22cff4e6..0e4ef81157 100644 --- a/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs +++ b/src/Fantomas.Core.Tests/CrampedMultilineBracketStyleTests.fs @@ -1742,8 +1742,8 @@ let ``record with comments above field, indent 2`` () = equal """ { Foo = - // bar - someValue } + // bar + someValue } """ [] @@ -1799,8 +1799,8 @@ let ``anonymous record with multiline field, indent 2`` () = equal """ {| Foo = - // meh - someValue |} + // meh + someValue |} """ [] @@ -2280,3 +2280,174 @@ let a = // test2 |} """ + +[] +let ``multiline field body expression where indent_size = 2, 2801`` () = + formatSourceString + false + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + + [| { Range = range; NewText = formatted } |] +""" + { config with IndentSize = 2 } + |> prepend newline + |> should + equal + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + + [| { Range = range; NewText = formatted } |] +""" + +[] +let ``multiline field body expression where indent_size = 2, anonymous record`` () = + formatSourceString + false + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + {| Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } |} + + [| { Range = range; NewText = formatted } |] +""" + { config with IndentSize = 2 } + |> prepend newline + |> should + equal + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + {| Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } |} + + [| { Range = range; NewText = formatted } |] +""" + +[] +let ``multiline field body expression where indent_size = 2, update record`` () = + formatSourceString + false + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { x with + Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + + [| { Range = range; NewText = formatted } |] +""" + { config with IndentSize = 2 } + |> prepend newline + |> should + equal + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { x with + Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + + [| { Range = range; NewText = formatted } |] +""" + +let ``multiline field body expression where indent_size = 2, inherit record`` () = + formatSourceString + false + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { inherit Foo() + Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + [| { Range = range; NewText = formatted } |] +""" + { config with IndentSize = 2 } + |> prepend newline + |> should + equal + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { inherit Foo() + Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + + [| { Range = range; NewText = formatted } |] +""" + +[] +let ``trivia after opening brace in inherit expression, 2803`` () = + formatSourceString + false + """ +let range = + { // foo + // bar + inherit // reason + Foo() + X = y + Z = + someReallyLongExpressionThatIsLongerThanTheLineLength + aLongArgument + // + anotherLongArgument + fooBar } +""" + config + |> prepend newline + |> should + equal + """ +let range = + { // foo + // bar + inherit // reason + Foo() + X = y + Z = + someReallyLongExpressionThatIsLongerThanTheLineLength + aLongArgument + // + anotherLongArgument + fooBar } +""" diff --git a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj index b0cb713268..f025909343 100644 --- a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj +++ b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj @@ -110,6 +110,7 @@ + diff --git a/src/Fantomas.Core.Tests/Stroustrup/RecordInstanceTests.fs b/src/Fantomas.Core.Tests/Stroustrup/RecordInstanceTests.fs new file mode 100644 index 0000000000..7d59783b58 --- /dev/null +++ b/src/Fantomas.Core.Tests/Stroustrup/RecordInstanceTests.fs @@ -0,0 +1,46 @@ +module Fantomas.Core.Tests.Stroustrup.RecordInstanceTests + +open NUnit.Framework +open FsUnit +open Fantomas.Core.Tests.TestHelper +open Fantomas.Core + +let config = + { config with + MultilineBracketStyle = Stroustrup } + +[] +let ``multiline field body expression where indent_size = 2`` () = + formatSourceString + false + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = + { Start = + { Line = range.StartLine - 1 + Character = range.StartColumn } + End = + { Line = range.EndLine - 1 + Character = range.EndColumn } } + + [| { Range = range; NewText = formatted } |] +""" + { config with IndentSize = 2 } + |> prepend newline + |> should + equal + """ +let handlerFormattedRangeDoc (lines: NamedText, formatted: string, range: FormatSelectionRange) = + let range = { + Start = { + Line = range.StartLine - 1 + Character = range.StartColumn + } + End = { + Line = range.EndLine - 1 + Character = range.EndColumn + } + } + + [| { Range = range; NewText = formatted } |] +""" diff --git a/src/Fantomas.Core/ASTTransformer.fs b/src/Fantomas.Core/ASTTransformer.fs index 5d0bcb954a..2ef8dc517a 100644 --- a/src/Fantomas.Core/ASTTransformer.fs +++ b/src/Fantomas.Core/ASTTransformer.fs @@ -222,6 +222,7 @@ let mkOpenAndCloseForArrayOrList isArray range = let mkInheritConstructor (creationAide: CreationAide) (t: SynType) (e: SynExpr) (mInherit: range) (m: range) = let inheritNode = stn "inherit" mInherit + let m = unionRanges mInherit m match e with | SynExpr.Const(constant = SynConst.Unit; range = StartEndRange 1 (mOpen, unitRange, mClose)) -> @@ -962,7 +963,7 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr = match baseInfo, copyInfo with | Some _, Some _ -> failwith "Unexpected that both baseInfo and copyInfo are present in SynExpr.Record" - | Some(t, e, mInherit, _, m), None -> + | Some(t, e, m, _, mInherit), None -> let inheritCtor = mkInheritConstructor creationAide t e mInherit m ExprInheritRecordNode(stn "{" mOpen, inheritCtor, fieldNodes, stn "}" mClose, exprRange) diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 1e9711bf94..1ec6ffee3c 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -420,7 +420,8 @@ let genExpr (e: Expr) = node let genMultilineInheritRecordExpr = - let fieldsExpr = genMultilineRecordFieldsExpr node + let fieldsExpr genRecordField = + genMultilineRecordFieldsExpr genRecordField node let genInheritInfo = (genSingleTextNode node.InheritConstructor.InheritKeyword @@ -430,19 +431,31 @@ let genExpr (e: Expr) = let genMultilineAlignBrackets = genSingleTextNode node.OpeningBrace - +> indentSepNlnUnindent (genInheritInfo +> fieldsExpr) + +> indentSepNlnUnindent (genInheritInfo +> fieldsExpr genRecordFieldNameAligned) +> sepNln +> genSingleTextNode node.ClosingBrace - let genMultilineCramped = - genSingleTextNode node.OpeningBrace - +> addSpaceIfSpaceAroundDelimiter - +> atCurrentColumn ( - genInheritInfo - +> fieldsExpr - +> addSpaceIfSpaceAroundDelimiter - +> genSingleTextNode node.ClosingBrace - ) + let genMultilineCramped (ctx: Context) = + // This is the column each field should start on. + let targetColumn = + let openBraceLength = node.OpeningBrace.Text.Length + + ctx.Column + + (if ctx.Config.SpaceAroundDelimiter then + openBraceLength + 1 + else + openBraceLength) + + (genSingleTextNodeSuffixDelimiter node.OpeningBrace + +> atCurrentColumn genInheritInfo + +> col sepNln node.Fields (fun e -> + // Add spaces to ensure the record field (incl trivia) starts at the right column. + addFixedSpaces targetColumn + // Potential indentations will be in relation to the opening curly brace. + +> genRecordFieldNameCramped false e) + +> addSpaceIfSpaceAroundDelimiter + +> genSingleTextNode node.ClosingBrace) + ctx ifAlignOrStroustrupBrackets genMultilineAlignBrackets genMultilineCramped @@ -1567,7 +1580,29 @@ let genMultilineRecordCopyExpr (addAdditionalIndent: bool) fieldsExpr copyExpr = +> onlyIf addAdditionalIndent unindent +> unindent -let genRecordFieldName (node: RecordFieldNode) = +/// Special case for record fields in Cramped mode. +/// The caller should have already verified that the settings do indeed specify Cramped. +let genRecordFieldNameCramped (alreadyIndentedFurther: bool) (node: RecordFieldNode) = + atCurrentColumn ( + enterNode node + +> genIdentListNode node.FieldName + +> sepSpace + +> genSingleTextNode node.Equals + ) + +> (fun ctx -> + // See: https://github.com/fsprojects/fantomas/issues/2801 + let addAdditionIndent = not alreadyIndentedFurther && ctx.Config.IndentSize < 3 + + let genBodyExpr e = + if addAdditionIndent then + sepSpaceOrDoubleIndentAndNlnIfExpressionExceedsPageWidth (genExpr e) + else + sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidth (genExpr e) + + genBodyExpr node.Expr ctx) + +> leaveNode node + +let genRecordFieldNameAligned (node: RecordFieldNode) = atCurrentColumn ( enterNode node +> genIdentListNode node.FieldName @@ -1577,8 +1612,11 @@ let genRecordFieldName (node: RecordFieldNode) = +> sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidthUnlessStroustrup genExpr node.Expr +> leaveNode node -let genMultilineRecordFieldsExpr (node: ExprRecordBaseNode) = - col sepNln node.Fields genRecordFieldName +let genMultilineRecordFieldsExpr + (genRecordField: RecordFieldNode -> Context -> Context) + (node: ExprRecordBaseNode) + : Context -> Context = + col sepNln node.Fields genRecordField /// /// Print a (anonymous) record with additional information as a single line. @@ -1589,7 +1627,13 @@ let genSmallRecordBaseExpr genExtra (node: ExprRecordBaseNode) = genSingleTextNode node.OpeningBrace +> addSpaceIfSpaceAroundDelimiter +> genExtra - +> col sepSemi node.Fields genRecordFieldName + +> col sepSemi node.Fields (fun rf -> + genIdentListNode rf.FieldName + +> sepSpace + +> genSingleTextNode rf.Equals + +> sepSpace + +> genExpr rf.Expr + |> genNode rf) //genRecordFieldNameAligned +> addSpaceIfSpaceAroundDelimiter +> genSingleTextNode node.ClosingBrace @@ -1622,7 +1666,7 @@ let genMultilineRecord (node: ExprRecordNode) (ctx: Context) = openBraceLength) let genMultilineAlignBrackets = - let fieldsExpr = genMultilineRecordFieldsExpr node + let fieldsExpr = genMultilineRecordFieldsExpr genRecordFieldNameAligned node match node.CopyInfo with | Some ci -> @@ -1653,7 +1697,10 @@ let genMultilineRecord (node: ExprRecordNode) (ctx: Context) = // Regular record || ctx.Config.IndentSize < 3 - genMultilineRecordCopyExpr additionalIndent (genMultilineRecordFieldsExpr node) we + genMultilineRecordCopyExpr + additionalIndent + (genMultilineRecordFieldsExpr (genRecordFieldNameCramped additionalIndent) node) + we | None -> fun (ctx: Context) -> col @@ -1663,7 +1710,7 @@ let genMultilineRecord (node: ExprRecordNode) (ctx: Context) = // Add spaces to ensure the record field (incl trivia) starts at the right column. addFixedSpaces targetColumn // Potential indentations will be in relation to the opening curly brace. - +> genRecordFieldName e) + +> genRecordFieldNameCramped false e) ctx match node.CopyInfo with