diff --git a/src/Fantomas.Tests/LetBindingTests.fs b/src/Fantomas.Tests/LetBindingTests.fs index 36041d882d..04cc830aca 100644 --- a/src/Fantomas.Tests/LetBindingTests.fs +++ b/src/Fantomas.Tests/LetBindingTests.fs @@ -124,4 +124,10 @@ let b = "meh" let b = "meh" +""" + +[] +let ``Raw method names with `/` `` () = + formatSourceString false "let ``/ operator combines paths`` = x" config + |> should equal """let ``/ operator combines paths`` = x """ \ No newline at end of file diff --git a/src/Fantomas.Tests/TokenParserTests.fs b/src/Fantomas.Tests/TokenParserTests.fs index 7aa7f925f5..6f71819c6b 100644 --- a/src/Fantomas.Tests/TokenParserTests.fs +++ b/src/Fantomas.Tests/TokenParserTests.fs @@ -381,3 +381,13 @@ let ``infix operator in full words inside an ident`` () = |> List.filter (fun { Item = item } -> match item with | IdentOperatorAsWord "op_LessThan" -> true | _ -> false) List.length triviaNodes == 1 + +[] +let ``ident between tickets `` () = + let source = "let ``/ operator combines paths`` = ()" + let (tokens,lineCount) = tokenize [] source + let triviaNodes = getTriviaFromTokens tokens lineCount + match triviaNodes with + | [{ Item = IdentBetweenTicks("``/ operator combines paths``") }] -> + pass() + | _ -> fail() \ No newline at end of file diff --git a/src/Fantomas.Tests/TriviaTests.fs b/src/Fantomas.Tests/TriviaTests.fs index 651a1e3e3e..50f4ab0bcc 100644 --- a/src/Fantomas.Tests/TriviaTests.fs +++ b/src/Fantomas.Tests/TriviaTests.fs @@ -37,7 +37,7 @@ let a = 9 match triviaNodes with | [{ ContentBefore = [Comment(LineCommentOnSingleLine(lineComment))]; } - {ContentBefore = [Number("9")]}] -> + { ContentItself = Some(Number("9"))}] -> lineComment == "// meh" | _ -> failwith "Expected line comment" @@ -66,7 +66,7 @@ let ``Line comment on same line, is after last AST item`` () = |> List.head match triviaNodes with - | [{Type = MainNode("SynModuleOrNamespace.AnonModule") ;ContentAfter = [Comment(LineCommentAfterSourceCode(lineComment))]}; {Type = MainNode("SynExpr.Const"); ContentBefore =[Number("7")]}] -> + | [{Type = MainNode("SynModuleOrNamespace.AnonModule") ;ContentAfter = [Comment(LineCommentAfterSourceCode(lineComment))]}; {Type = MainNode("SynExpr.Const"); ContentItself =Some(Number("7"))}] -> lineComment == "// should be 8" | _ -> fail() @@ -81,9 +81,9 @@ let b = 9""" |> List.head match triviaNodes with - | [{ContentBefore = [Number("7")]} + | [{ContentItself = Some(Number("7"))} {ContentBefore = [Newline]} - {ContentBefore = [Number("9")]}] -> + {ContentItself = Some(Number("9"))}] -> pass() | _ -> fail() @@ -101,7 +101,7 @@ let a = 7 match triviaNodes with | [{ContentBefore = [Comment(LineCommentOnSingleLine(fooComment));Comment(LineCommentOnSingleLine(barComment))]} - {ContentBefore = [Number("7")]}] -> + {ContentItself = Some(Number("7"))}] -> fooComment == "// foo" barComment == "// bar" | _ -> @@ -120,7 +120,7 @@ let ``Comments inside record`` () = match triviaNodes with | [{ Type = TriviaNodeType.Token(t); ContentAfter = [Comment(LineCommentAfterSourceCode("// foo"))] } - { ContentBefore = [Number("7")] }] -> + { ContentItself = Some(Number("7")) }] -> t.Content == "{" | _ -> fail() @@ -138,7 +138,7 @@ let ``Comment after all source code`` () = match triviaNodes with | [ { Type = MainNode(mn); ContentAfter = [Comment(LineCommentOnSingleLine(lineComment))] } - { ContentBefore = [Number("123")] } ] -> + { ContentItself = Some(Number("123")) } ] -> mn == "SynModuleDecl.Types" lineComment == (sprintf "%s// override private x.ToString() = \"\"" Environment.NewLine) pass() @@ -155,7 +155,7 @@ let ``Block comment added to trivia`` () = match triviaNodes with | [{ ContentAfter = [Comment(BlockComment(comment))] - Type = Token { Content = "=" } }; {ContentBefore = [Number("9")]}] -> + Type = Token { Content = "=" } }; {ContentItself = Some(Number("9"))}] -> comment == "(* meh *)" | _ -> failwith "Expected block comment" @@ -234,7 +234,7 @@ let a = 9 match triviaNodes with | [{ ContentBefore = [Comment(BlockComment(comment)); Newline] } - { ContentBefore = [Number("9")] }] -> + { ContentItself = Some(Number("9")) }] -> comment == "(* // meh *)" | _ -> failwith "Expected block comment" diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index 4ea8f1699b..5f101518d4 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -1808,12 +1808,9 @@ and genConst (c:SynConst) (r:range) = let triviaStringContent = trivia |> Option.bind(fun tv -> - tv.ContentBefore - |> List.choose (fun tv -> - match tv with - | StringContent(sc) -> Some sc - | _ -> None ) - |> List.tryExactlyOne + match tv.ContentItself with + | Some(StringContent(sc)) -> Some sc + | _ -> None ) match triviaStringContent, trivia with @@ -1843,9 +1840,7 @@ and genConstNumber (c:SynConst) (r: range) = ctx.Trivia |> List.tryFind (fun t -> t.Range = r) |> Option.bind(fun tn -> - tn.ContentBefore - |> List.choose (fun tv -> match tv with | Number(n) -> Some n | _ -> None) - |> List.tryExactlyOne + match tn.ContentItself with | Some(Number(n)) -> Some n | _ -> None ) |> fun n -> match n with @@ -1876,12 +1871,10 @@ and genConstBytes (bytes: byte []) (r: range) = ctx.Trivia |> List.tryFind(fun t -> t.Range = r) |> Option.bind (fun tv -> - tv.ContentBefore - |> List.choose (fun sc -> - match sc with - | StringContent(content) -> Some content - | _ -> None) - |> List.tryExactlyOne) + match tv.ContentItself with + | Some(StringContent(content)) -> Some content + | _ -> None + ) match trivia with | Some t -> !- t @@ -1899,9 +1892,10 @@ and infixOperatorFromTrivia range fallback (ctx: Context) = |> List.choose(fun t -> match t.Range = range with | true -> - t.ContentBefore - |> List.choose (fun tc -> match tc with | IdentOperatorAsWord(iiw) -> Some iiw | _ -> None) - |> List.tryHead + match t.ContentItself with + | Some(IdentOperatorAsWord(iiw)) -> Some iiw + | Some(IdentBetweenTicks(iiw)) -> Some iiw // Used when value between ``...`` + | _ -> None | _ -> None) |> List.tryHead |> fun iiw -> diff --git a/src/Fantomas/Context.fs b/src/Fantomas/Context.fs index 65b0f95fac..1120ce6232 100644 --- a/src/Fantomas/Context.fs +++ b/src/Fantomas/Context.fs @@ -450,6 +450,7 @@ let internal printTriviaContent (c: TriviaContent) = | Number _ | StringContent _ | IdentOperatorAsWord _ + | IdentBetweenTicks _ -> sepNone // don't print here but somewhere in CodePrinter | Directive(content, addNewline) -> (ifElse addNewline sepNln sepNone) +> !- content +> sepNln diff --git a/src/Fantomas/SourceParser.fs b/src/Fantomas/SourceParser.fs index 0e44779de5..4734b4efe6 100644 --- a/src/Fantomas/SourceParser.fs +++ b/src/Fantomas/SourceParser.fs @@ -1255,7 +1255,7 @@ let getRangesFromAttributes (mdl: SynModuleDecl) = types |> Seq.collect(fun t -> match t with - | SynTypeDefn.TypeDefn((SynComponentInfo.ComponentInfo(attrs, _,_,_,_,_,_,_)),stdr,_,_) -> collectAttributesRanges attrs + | SynTypeDefn.TypeDefn((SynComponentInfo.ComponentInfo(attrs, _,_,_,_,_,_,_)),_,_,_) -> collectAttributesRanges attrs ) | _ -> Seq.empty |> Seq.toList \ No newline at end of file diff --git a/src/Fantomas/TokenParser.fs b/src/Fantomas/TokenParser.fs index 9ac1f56be4..045d3e5e8c 100644 --- a/src/Fantomas/TokenParser.fs +++ b/src/Fantomas/TokenParser.fs @@ -343,6 +343,13 @@ let rec private getTriviaFromTokensThemSelves (allTokens: Token list) (tokens: T |> List.prependItem foundTrivia getTriviaFromTokensThemSelves allTokens rest info + | head::rest when (head.TokenInfo.TokenName = "IDENT" && head.Content.StartsWith("``") && head.Content.EndsWith("``")) -> + let range = getRangeBetween "ident between ``" head head + let info = + Trivia.Create(IdentBetweenTicks(head.Content)) range + |> List.prependItem foundTrivia + getTriviaFromTokensThemSelves allTokens rest info + | (_)::rest -> getTriviaFromTokensThemSelves allTokens rest foundTrivia | [] -> foundTrivia @@ -391,6 +398,7 @@ let getTriviaNodesFromTokens (tokens: Token list) : TriviaNode list = |> List.map (fun t -> { Type = TriviaNodeType.Token(t) ContentBefore = [] + ContentItself = None ContentAfter = [] Range = getRangeBetween t.TokenInfo.TokenName t t } ) \ No newline at end of file diff --git a/src/Fantomas/Trivia.fs b/src/Fantomas/Trivia.fs index be27ce1063..bc58a39ac9 100644 --- a/src/Fantomas/Trivia.fs +++ b/src/Fantomas/Trivia.fs @@ -118,6 +118,7 @@ let private mapNodeToTriviaNode (node: Node) = |> Option.map (fun range -> { Type = MainNode(node.Type) ContentAfter = [] + ContentItself = None ContentBefore = [] Range = range } ) @@ -259,21 +260,33 @@ let private addTriviaToTriviaNode (triviaNodes: TriviaNode list) trivia = | { Item = StringContent(_) as siNode; Range = range } -> findNodeOnLineAndColumn triviaNodes range.StartLine range.StartColumn - |> updateTriviaNode (fun tn -> { tn with ContentBefore = List.appendItem tn.ContentBefore (siNode) }) triviaNodes + |> updateTriviaNode (fun tn -> { tn with ContentItself = Some siNode }) triviaNodes | { Item = Number(_) as number; Range = range } -> findNodeOnLineAndColumn triviaNodes range.StartLine range.StartColumn - |> updateTriviaNode (fun tn -> { tn with ContentBefore = List.appendItem tn.ContentBefore number }) triviaNodes + |> updateTriviaNode (fun tn -> { tn with ContentItself = Some number }) triviaNodes | { Item = IdentOperatorAsWord(_) as ifw; Range = range } -> findBindingThatStartsWith triviaNodes range.StartColumn range.StartLine - |> updateTriviaNode (fun tn -> { tn with ContentBefore = List.appendItem tn.ContentBefore ifw }) triviaNodes + |> updateTriviaNode (fun tn -> { tn with ContentItself = Some ifw }) triviaNodes + + | { Item = IdentBetweenTicks(_) as iNode; Range = range } -> + triviaNodes + |> List.tryFind (fun t -> + let isIdent = + match t.Type with + | MainNode("SynExpr.Ident") + | MainNode("SynPat.Named") -> true + | _ -> false + isIdent && (t.Range.StartColumn = range.StartColumn || t.Range.StartColumn = range.StartColumn + 1) && t.Range.StartLine = range.StartLine + ) + |> updateTriviaNode (fun tn -> { tn with ContentItself = Some iNode }) triviaNodes | _ -> triviaNodes let private triviaNodeIsNotEmpty triviaNode = - not(List.isEmpty triviaNode.ContentAfter) || not(List.isEmpty triviaNode.ContentBefore) + not(List.isEmpty triviaNode.ContentAfter) || not(List.isEmpty triviaNode.ContentBefore) || Option.isSome triviaNode.ContentItself (* 1. Collect TriviaNode from tokens and AST diff --git a/src/Fantomas/TriviaTypes.fs b/src/Fantomas/TriviaTypes.fs index b4ba4cfda0..e9c08bf895 100644 --- a/src/Fantomas/TriviaTypes.fs +++ b/src/Fantomas/TriviaTypes.fs @@ -31,6 +31,7 @@ type TriviaContent = | Number of string | StringContent of string | IdentOperatorAsWord of string + | IdentBetweenTicks of string | Comment of Comment | Newline | Directive of directive:string * appendNewline:bool @@ -51,5 +52,6 @@ type TriviaNodeType = type TriviaNode = { Type: TriviaNodeType ContentBefore: TriviaContent list + ContentItself: TriviaContent option ContentAfter: TriviaContent list Range: range } \ No newline at end of file