Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/Fantomas.Core.Tests/CompilerDirectivesTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3233,3 +3233,82 @@ let CombineImportedAssembliesTask

()
"""

[<Test>]
let ``should keep attributes on mutually recursive class, no defines`` () =
formatSourceStringWithDefines
[]
"""
type X = int
and
#if NET5_0_OR_GREATER
[<System.Runtime.CompilerServices.IsReadOnly>]
#endif
[<Obsolete>]
[<CustomEquality; NoComparison; Struct>] Y = int

"""
config
|> prepend newline
|> should
equal
"""
type X = int
and
#if NET5_0_OR_GREATER
#endif
[<Obsolete; CustomEquality; NoComparison; Struct>] Y = int
"""

[<Test>]
let ``should keep attributes on mutually recursive class, NET5_0_OR_GREATER`` () =
formatSourceStringWithDefines
[ "NET5_0_OR_GREATER" ]
"""
type X = int
and
#if NET5_0_OR_GREATER
[<System.Runtime.CompilerServices.IsReadOnly>]
#endif
[<Obsolete>]
[<CustomEquality; NoComparison; Struct>] Y = int

"""
config
|> prepend newline
|> should
equal
"""
type X = int
and
#if NET5_0_OR_GREATER
[<System.Runtime.CompilerServices.IsReadOnly>]
#endif
[<Obsolete; CustomEquality; NoComparison; Struct>] Y = int
"""

[<Test>]
let ``should keep attributes on mutually recursive class, 3174`` () =
formatSourceString
"""
type X = int
and
#if NET5_0_OR_GREATER
[<System.Runtime.CompilerServices.IsReadOnly>]
#endif
[<Obsolete>]
[<CustomEquality; NoComparison; Struct>] Y = int

"""
config
|> prepend newline
|> should
equal
"""
type X = int
and
#if NET5_0_OR_GREATER
[<System.Runtime.CompilerServices.IsReadOnly>]
#endif
[<Obsolete; CustomEquality; NoComparison; Struct>] Y = int
"""
2 changes: 1 addition & 1 deletion src/Fantomas.Core/ASTTransformer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2557,7 +2557,7 @@ let mkTypeDefn
else
match ats with
| [] -> leadingKeyword.Range
| firstAttr :: _ -> firstAttr.Range
| firstAttr :: _ -> unionRanges leadingKeyword.Range firstAttr.Range

let endRange =
match trivia.EqualsRange with
Expand Down
53 changes: 50 additions & 3 deletions src/Fantomas.Core/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,40 @@ let genOnelinerAttributes (n: MultipleAttributeListNode option) =

ifElse ats.IsEmpty sepNone (genAttrs +> sepSpace)

let partitionOn splitBefore splitAfter items =
let folder acc item =
match acc with
| [] -> [ [ item ] ]
| (lastItem :: _ as currentGroup) :: restGroups when splitAfter lastItem ->
[ item ] :: currentGroup :: restGroups
| currentGroup :: restGroups when splitBefore item -> [ item ] :: currentGroup :: restGroups
| currentGroup :: restGroups -> (item :: currentGroup) :: restGroups

items |> List.fold folder [] |> List.map List.rev |> List.rev

let genCompactedAttributes (n: MultipleAttributeListNode option) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a code comment on what this does?

match n with
| None -> sepNone
| Some n ->
let attributeLists =
n.AttributeLists |> partitionOn (_.HasContentBefore) (_.HasContentAfter)

col sepNone attributeLists (fun (al) ->
let ats = al |> List.collect _.Attributes

let openingToken =
List.tryHead al |> Option.map (fun (a: AttributeListNode) -> a.Opening)

let closingToken =
List.tryLast al |> Option.map (fun (a: AttributeListNode) -> a.Closing)

optSingle genSingleTextNode openingToken
+> (genAttributesCore ats)
+> optSingle genSingleTextNode closingToken
+> sepNlnWhenWriteBeforeNewlineNotEmpty
|> genNode (al |> List.head))
|> genNode n

let genAttributes (node: MultipleAttributeListNode option) =
match node with
| None -> sepNone
Expand Down Expand Up @@ -3455,6 +3489,7 @@ let hasTriviaAfterLeadingKeyword (identifier: IdentListNode) (accessibility: Sin
| _ -> false

let beforeIdentifier = identifier.HasContentBefore

beforeAccess || beforeIdentifier

let genTypeDefn (td: TypeDefn) =
Expand All @@ -3469,16 +3504,28 @@ let genTypeDefn (td: TypeDefn) =
let hasTriviaAfterLeadingKeyword =
hasTriviaAfterLeadingKeyword typeName.Identifier typeName.Accessibility

let hasTriviaInAttributes =
match typeName.Attributes with
| Some attributes ->
attributes.HasContentBefore
|| attributes.HasContentAfter
|| attributes.AttributeLists
|> List.exists (fun a -> a.HasContentBefore || a.HasContentAfter)
| None -> false

let shouldIndent =
hasTriviaAfterLeadingKeyword || (hasAndKeyword && hasTriviaInAttributes)

genXml typeName.XmlDoc
+> onlyIfNot hasAndKeyword (genAttributes typeName.Attributes)
+> genSingleTextNode typeName.LeadingKeyword
+> onlyIf hasTriviaAfterLeadingKeyword indent
+> onlyIf hasAndKeyword (sepSpace +> genOnelinerAttributes typeName.Attributes)
+> onlyIf shouldIndent indent
+> onlyIf hasAndKeyword (sepSpace +> genCompactedAttributes typeName.Attributes)
+> sepSpace
+> genAccessOpt typeName.Accessibility
+> genTypeAndParam (genIdentListNode typeName.Identifier) typeName.TypeParameters
+> onlyIfNot typeName.Constraints.IsEmpty (sepSpace +> genTypeConstraints typeName.Constraints)
+> onlyIf hasTriviaAfterLeadingKeyword unindent
+> onlyIf shouldIndent unindent
+> leadingExpressionIsMultiline
(optSingle
(fun imCtor -> sepSpaceBeforeClassConstructor +> genImplicitConstructor imCtor)
Expand Down
Loading