Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve back ticks in enum members #662

Merged
merged 4 commits into from
Feb 1, 2020
Merged
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
57 changes: 56 additions & 1 deletion src/Fantomas.Tests/UnionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,59 @@ type DU = | Record
namespace meh

type DU = | Record
"""
"""

[<Test>]
let ``enum with back ticks, 626`` () =
formatSourceString false """type MyEnum =
| ``test-one`` = 0
""" config
|> prepend newline
|> should equal """
type MyEnum =
| ``test-one`` = 0
"""

[<Test>]
let ``enum with back ticks in signature file`` () =
formatSourceString true """namespace foo

type MyEnum =
| ``test-one`` = 0
""" config
|> prepend newline
|> should equal """
namespace foo

type MyEnum =
| ``test-one`` = 0
"""

[<Test>]
let ``discriminated union with back ticks`` () =
formatSourceString false """type MyEnum =
| ``test-one`` of int
| ``test-two`` of string
""" config
|> prepend newline
|> should equal """
type MyEnum =
| ``test-one`` of int
| ``test-two`` of string
"""

[<Test>]
let ``discriminated union with back ticks in signature file`` () =
formatSourceString true """namespace foo
type MyEnum =
| ``test-one`` of int
| ``test-two`` of string
""" config
|> prepend newline
|> should equal """
namespace foo

type MyEnum =
| ``test-one`` of int
| ``test-two`` of string
"""
58 changes: 28 additions & 30 deletions src/Fantomas/AstTransformer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ module private Ast =
Properties = p []
FsAstNode = ast
Childs = [visitSynModuleOrNamespace moduleOrNamespace]}

and visitSynExpr(synExpr: SynExpr): Node =
match synExpr with
| SynExpr.Paren(expr,leftParenRange,rightParenRange,range) ->
Expand Down Expand Up @@ -354,7 +354,7 @@ module private Ast =
| Sequentials xs -> // use tail-rec active pattern to avoid stack overflow
let rec cons xs =
match xs with
| [] -> failwith "should not happen" // expr2Opt is always Some in last item
| [] -> failwith "should not happen" // expr2Opt is always Some in last item
| ((isTrueSeq,expr1,expr2Opt,range)::rest) ->
{Type = "SynExpr.Sequential"
Range = r range
Expand Down Expand Up @@ -398,13 +398,14 @@ module private Ast =
FsAstNode = synExpr
Childs = []}
| SynExpr.LongIdent(isOptional,longDotId,_,range) ->
let ids = visitLongIdentWithDots longDotId
{Type = "SynExpr.LongIdent"
Range = r range
Properties =
p ["isOptional" ==> isOptional
"longDotId" ==> lid longDotId]
FsAstNode = synExpr
Childs = []}
Childs = ids}
| SynExpr.LongIdentSet(longDotId,expr,range) ->
{Type = "SynExpr.LongIdentSet"
Range = r range
Expand All @@ -413,17 +414,7 @@ module private Ast =
Childs = [yield visitSynExpr expr]}
| SynExpr.DotGet(expr,rangeOfDot,longDotId,range) ->
// Idents are collected as childs here to deal with unit test ``Fluent api with comments should remain on same lines``
let ids =
match longDotId with
| LongIdentWithDots(ids,_) ->
ids
|> List.map (fun (ident) -> {
Type = "Ident"
Range = Some ident.idRange
Properties = Map.empty
FsAstNode = ident
Childs = []
})
let ids = visitLongIdentWithDots longDotId
{Type = "SynExpr.DotGet"
Range = r range
Properties =
Expand Down Expand Up @@ -772,7 +763,7 @@ module private Ast =
[yield visitSynComponentInfo sci
yield visitSynTypeDefnSigRepr synTypeDefnSigReprs
yield! (memberSig |> List.map visitSynMemberSig)]}

and visitSynTypeDefnSigRepr(stdr: SynTypeDefnSigRepr): Node =
match stdr with
| SynTypeDefnSigRepr.ObjectModel(kind,members,range) ->
Expand Down Expand Up @@ -1367,7 +1358,7 @@ module private Ast =
yield "typeName" ==> lid attr.TypeName]
FsAstNode = attr
Childs = [visitSynExpr attr.ArgExpr]}

and visitSynAttributeList(attrs: SynAttributeList): Node =
{Type = "SynAttributeList"
Range = r attrs.Range
Expand Down Expand Up @@ -1411,9 +1402,9 @@ module private Ast =
| EnumCase(attrs,ident,_,_,range) ->
{Type = "EnumCase"
Range = r range
Properties = p ["ident" ==> i ident]
Properties = p []
FsAstNode = sec
Childs = [yield! attrs |> List.map visitSynAttributeList]}
Childs = [yield! attrs |> List.map visitSynAttributeList; yield visitIdent ident]}

and visitSynField(sfield: SynField): Node =
match sfield with
Expand Down Expand Up @@ -1601,18 +1592,10 @@ module private Ast =
"longIdent" ==> longIdent]
FsAstNode = hash
Childs = []}

and visitSynModuleOrNamespaceSig(modOrNs: SynModuleOrNamespaceSig): Node =
match modOrNs with
| SynModuleOrNamespaceSig(longIdent,isRecursive,isModule,decls,_,attrs,access,range) ->
let collectIdents (idents: LongIdent) =
idents
|> List.map (fun ident ->
{ Type = "Ident"
Range = r ident.idRange
Properties = Map.empty
FsAstNode = ident
Childs = [] })
{Type = sprintf "SynModuleOrNamespaceSig.%A" isModule
Range = r range
Properties =
Expand All @@ -1622,10 +1605,10 @@ module private Ast =
if access.IsSome then yield "access" ==> (access.Value |> visitSynAccess)]
FsAstNode = modOrNs
Childs =
[yield! (if isModule = SynModuleOrNamespaceKind.DeclaredNamespace then collectIdents longIdent else [])
[yield! (if isModule = SynModuleOrNamespaceKind.DeclaredNamespace then visitLongIdent longIdent else [])
yield! attrs |> List.map visitSynAttributeList
yield! (decls |> List.map visitSynModuleSigDecl)]}

and visitSynModuleSigDecl(ast: SynModuleSigDecl) : Node =
match ast with
| SynModuleSigDecl.ModuleAbbrev(ident,longIdent,range) ->
Expand Down Expand Up @@ -1676,7 +1659,7 @@ module private Ast =
Properties = p []
FsAstNode = ast
Childs = [visitSynExceptionSig synExceptionSig]}

and visitSynExceptionSig(exceptionDef: SynExceptionSig): Node =
match exceptionDef with
| SynExceptionSig(sedr,members,range) ->
Expand All @@ -1688,6 +1671,21 @@ module private Ast =
[yield visitSynExceptionDefnRepr sedr
yield! (members |> List.map visitSynMemberSig)]}

and visitLongIdentWithDots (lid: LongIdentWithDots): Node list =
match lid with
| LongIdentWithDots(ids,_) ->
List.map visitIdent ids

and visitLongIdent (li: LongIdent) : Node list =
List.map visitIdent li

and visitIdent (ident: Ident) : Node =
{ Type = "Ident"
Range = r ident.idRange
Properties = Map.empty
FsAstNode = ident
Childs = [] }

let astToNode (hds: ParsedHashDirective list) (mdls: SynModuleOrNamespace list): Node =
let children =
[ yield! List.map Ast.visit mdls
Expand Down
19 changes: 14 additions & 5 deletions src/Fantomas/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,10 @@ and genExpr astContext synExpr =
(atCurrentColumn formatIfElseExpr) ctx

// At this stage, all symbolic operators have been handled.
| OptVar(s, isOpt) -> ifElse isOpt (!- "?") sepNone -- s
| OptVar(s, isOpt, ranges) ->
// In case s is f.ex `onStrongDiscard.IsNone`, last range is the range of `IsNone`
let lastRange = List.tryLast ranges
ifElse isOpt (!- "?") sepNone -- s +> opt id lastRange (fun r ctx -> leaveNode r ctx)
| LongIdentSet(s, e, _) ->
!- (sprintf "%s <- " s) +> autoIndentNlnByFuture (genExpr astContext e)
| DotIndexedGet(e, es) -> addParenIfAutoNln e (genExpr astContext) -- "." +> sepOpenLFixed +> genIndexers astContext es +> sepCloseLFixed
Expand Down Expand Up @@ -1811,10 +1814,16 @@ and genUnionCase astContext (UnionCase(ats, px, _, s, UnionCaseType fs) as node)
|> genTrivia node.Range

and genEnumCase astContext (EnumCase(ats, px, _, (_,r)) as node) =
let genCase =
match node with
| SynEnumCase.EnumCase(_, ident, c,_,_) ->
!- ident.idText +> !- " = " +> genConst c r
let genCase (ctx: Context) =
let expr =
match node with
| EnumCase(_, _, identInAST, (c,r)) ->
let ident =
match TriviaHelpers.``has content itself is ident between ticks`` r ctx.Trivia with
| Some identBetweenTicks -> identBetweenTicks
| None -> identInAST
!- ident +> !- " = " +> genConst c r
expr ctx

genPreXmlDoc px
+> genTriviaBeforeClausePipe node.Range
Expand Down
20 changes: 13 additions & 7 deletions src/Fantomas/SourceParser.fs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ type Identifier =
xs
|> Seq.map (fun x -> if x.idText = MangledGlobalName then "global" else x.idText)
|> String.concat "."
member x.Ranges =
match x with
| Id x -> List.singleton x.idRange
| LongId xs -> List.map (fun (x:Ident) -> x.idRange) xs

/// Different from (|Ident|), this pattern also accepts keywords
let inline (|IdentOrKeyword|) (s : Ident) = Id s
Expand All @@ -96,6 +100,7 @@ let (|OpName|) (x : Identifier) =

/// Operators in their declaration form
let (|OpNameFull|) (x : Identifier) =
let r = x.Ranges
let s = x.Text
let s' = DecompileOpName s
if IsActivePatternName s || IsInfixOperator s || IsPrefixOperator s || IsTernaryOperator s || s = "op_Dynamic" then
Expand All @@ -107,6 +112,7 @@ let (|OpNameFull|) (x : Identifier) =
match x with
| Id(Ident s) | LongId(LongIdent s) ->
DecompileOpName s
|> fun s -> (s, r)

// Type params

Expand Down Expand Up @@ -619,10 +625,10 @@ let (|Indexer|) = function
| SynIndexerArg.One e -> Single e

let (|OptVar|_|) = function
| SynExpr.Ident(IdentOrKeyword(OpNameFull s)) ->
Some(s, false)
| SynExpr.LongIdent(isOpt, LongIdentWithDots.LongIdentWithDots(LongIdentOrKeyword(OpNameFull s), _), _, _) ->
Some(s, isOpt)
| SynExpr.Ident(IdentOrKeyword(OpNameFull (s,r))) ->
Some(s, false, r)
| SynExpr.LongIdent(isOpt, LongIdentWithDots.LongIdentWithDots(LongIdentOrKeyword(OpNameFull (s,r)), _), _, _) ->
Some(s, isOpt, r)
| _ -> None

/// This pattern is escaped by using OpName
Expand Down Expand Up @@ -912,12 +918,12 @@ let (|PatTyped|_|) = function
| _ -> None

let (|PatNamed|_|) = function
| SynPat.Named(p, IdentOrKeyword(OpNameFull s), _, ao, _) ->
| SynPat.Named(p, IdentOrKeyword(OpNameFull (s,_)), _, ao, _) ->
Some(ao, p, s)
| _ -> None

let (|PatLongIdent|_|) = function
| SynPat.LongIdent(LongIdentWithDots.LongIdentWithDots(LongIdentOrKeyword(OpNameFull s), _), _, tpso, xs, ao, _) ->
| SynPat.LongIdent(LongIdentWithDots.LongIdentWithDots(LongIdentOrKeyword(OpNameFull (s,_)), _), _, tpso, xs, ao, _) ->
match xs with
| SynConstructorArgs.Pats ps ->
Some(ao, s, List.map (fun p -> (None, p)) ps, tpso)
Expand Down Expand Up @@ -1214,7 +1220,7 @@ let (|MSMember|MSInterface|MSInherit|MSValField|MSNestedType|) = function
| SynMemberSig.ValField(f, _) -> MSValField f
| SynMemberSig.NestedType(tds, _) -> MSNestedType tds

let (|Val|) (ValSpfn(ats, IdentOrKeyword(OpNameFull s), tds, t, vi, _, _, px, ao, _, _)) =
let (|Val|) (ValSpfn(ats, IdentOrKeyword(OpNameFull (s,_)), tds, t, vi, _, _, px, ao, _, _)) =
(ats, px, ao, s, t, vi, tds)

// Misc
Expand Down
3 changes: 2 additions & 1 deletion src/Fantomas/Trivia.fs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ let private addTriviaToTriviaNode (triviaNodes: TriviaNode list) trivia =
let isIdent =
match t.Type with
| MainNode("SynExpr.Ident")
| MainNode("SynPat.Named") -> true
| MainNode("SynPat.Named")
| MainNode("Ident") -> true
| _ -> false
isIdent && (t.Range.StartColumn = range.StartColumn || t.Range.StartColumn = range.StartColumn + 1) && t.Range.StartLine = range.StartLine
)
Expand Down
10 changes: 9 additions & 1 deletion src/Fantomas/TriviaHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,12 @@ module TriviaHelpers =
triviaNodes
|> List.tryFind (fun tv -> tv.Range = range)
|> Option.map (fun tv -> tv.ContentBefore |> List.exists (function | Comment(LineCommentOnSingleLine(_)) -> true | _ -> false))
|> Option.defaultValue false
|> Option.defaultValue false

let internal ``has content itself is ident between ticks`` range (triviaNodes: TriviaNode list) =
triviaNodes
|> List.choose (fun tn ->
match tn.Range = range, tn.ContentItself with
| true, Some(IdentBetweenTicks(ident)) -> Some ident
| _ -> None)
|> List.tryHead