Skip to content

Commit

Permalink
Formatting original char from trivia. Fixes #632.
Browse files Browse the repository at this point in the history
  • Loading branch information
nojaf committed Feb 1, 2020
1 parent 3f797ed commit 16f9b36
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 15 deletions.
11 changes: 11 additions & 0 deletions src/Fantomas.Tests/SynConstTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,15 @@ namespace SomeNamespace
module SomeModule =
let backspace = '\b'
let formFeed = '\f'
"""

[<Test>]
let ``escape unicode null, 632`` () =
formatSourceString false """let nulchar = '\u0000'
let nullstr = "\u0000"
""" config
|> prepend newline
|> should equal """
let nulchar = '\u0000'
let nullstr = "\u0000"
"""
20 changes: 20 additions & 0 deletions src/Fantomas.Tests/TokenParserTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,23 @@ let ``ident between tickets `` () =
| [{ Item = IdentBetweenTicks("``/ operator combines paths``") }] ->
pass()
| _ -> fail()

[<Test>]
let ``simple char content`` () =
let source = "let someChar = \'s\'"
let (tokens,lineCount) = tokenize [] source
let trivia = getTriviaFromTokens tokens lineCount
match trivia with
| [{ Item = CharContent("\'s\'") }] ->
pass()
| _ -> fail()

[<Test>]
let ``escaped char content`` () =
let source = "let nulchar = \'\\u0000\'"
let (tokens,lineCount) = tokenize [] source
let trivia = getTriviaFromTokens tokens lineCount
match trivia with
| [{ Item = CharContent("\'\\u0000\'") }] ->
pass()
| _ -> fail()
18 changes: 16 additions & 2 deletions src/Fantomas.Tests/TriviaTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ let a = 9
failwith "Expected line comment"

[<Test>]
let ``Line comment that is alone on the single, preceded by whitespaces`` () =
let ``line comment that is alone on the single, preceded by whitespaces`` () =
let source = """ // foo
let a = 'c'
"""
Expand All @@ -52,7 +52,8 @@ let a = 'c'
|> List.head

match triviaNodes with
| [{ ContentBefore = [Comment(LineCommentOnSingleLine(lineComment))]; }] ->
| [{ ContentBefore = [Comment(LineCommentOnSingleLine(lineComment))]; }
{ ContentItself = Some(CharContent("\'c\'")) }] ->
lineComment == "// foo"
| _ ->
failwith "Expected line comment"
Expand Down Expand Up @@ -511,4 +512,17 @@ with empty lines"
| [{ ContentItself = Some(StringContent(sc))
Type = TriviaNodeType.MainNode("SynExpr.Const") }] ->
sc == sprintf "\"\"\"%s\"\"\"" multilineString
| _ -> fail()

[<Test>]
let ``char content`` () =
let source = "let nulchar = \'\\u0000\'"
let trivia =
toTrivia source
|> List.head

match trivia with
| [{ ContentItself = Some(CharContent("\'\\u0000\'"))
Type = TriviaNodeType.MainNode("SynExpr.Const") }] ->
pass()
| _ -> fail()
12 changes: 9 additions & 3 deletions src/Fantomas/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2292,8 +2292,15 @@ and genConst (c:SynConst) (r:range) =
!- (sprintf "\"%s\"" escaped)
<| ctx
| SynConst.Char(c) ->
let escapedChar = Char.escape c
!- (sprintf "\'%s\'" escapedChar)
fun (ctx: Context) ->
let charContentFromTrivia = TriviaHelpers.``get CharContent`` r ctx.Trivia
let expr =
match charContentFromTrivia with
| Some content -> !- content
| None ->
let escapedChar = Char.escape c
!- (sprintf "\'%s\'" escapedChar)
expr ctx
| SynConst.Bytes(bytes,_) -> genConstBytes bytes r
| SynConst.Measure(c, m) ->
let measure =
Expand Down Expand Up @@ -2367,4 +2374,3 @@ and infixOperatorFromTrivia range fallback (ctx: Context) =
| Some iiw -> !- iiw
| None -> !- fallback
<| ctx

1 change: 1 addition & 0 deletions src/Fantomas/Context.fs
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ let internal printTriviaContent (c: TriviaContent) (ctx: Context) =
| IdentOperatorAsWord _
| IdentBetweenTicks _
| NewlineAfter
| CharContent _
-> sepNone // don't print here but somewhere in CodePrinter
| Directive(s)
| Comment(LineCommentOnSingleLine s) ->
Expand Down
7 changes: 7 additions & 0 deletions src/Fantomas/TokenParser.fs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,13 @@ let rec private getTriviaFromTokensThemSelves (config: FormatConfig) (allTokens:
|> List.prependItem foundTrivia
getTriviaFromTokensThemSelves config allTokens rest info

| head::rest when (head.TokenInfo.TokenName = "CHAR") ->
let range = getRangeBetween head.TokenInfo.TokenName head head
let info =
Trivia.Create (CharContent(head.Content)) range
|> List.prependItem foundTrivia
getTriviaFromTokensThemSelves config allTokens rest info

| (_)::rest -> getTriviaFromTokensThemSelves config allTokens rest foundTrivia

| [] -> foundTrivia
Expand Down
4 changes: 4 additions & 0 deletions src/Fantomas/Trivia.fs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ let private addTriviaToTriviaNode (triviaNodes: TriviaNode list) trivia =
| { Item = Number(_) as number; Range = range } ->
findNodeOnLineAndColumn triviaNodes range.StartLine range.StartColumn
|> updateTriviaNode (fun tn -> { tn with ContentItself = Some number }) triviaNodes

| { Item = CharContent(_) as chNode; Range = range } ->
findNodeOnLineAndColumn triviaNodes range.StartLine range.StartColumn
|> updateTriviaNode (fun tn -> { tn with ContentItself = Some chNode }) triviaNodes

| { Item = IdentOperatorAsWord(_) as ifw; Range = range } ->
findBindingThatStartsWith triviaNodes range.StartColumn range.StartLine
Expand Down
28 changes: 18 additions & 10 deletions src/Fantomas/TriviaHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@ open FSharp.Compiler.Range
open Fantomas.TriviaTypes

[<RequireQualifiedAccess>]
module TriviaHelpers =
let internal findByRange (trivia: TriviaNode list) (range: range) =
module internal TriviaHelpers =
let findByRange (trivia: TriviaNode list) (range: range) =
trivia
|> List.tryFind (fun t -> t.Range = range)

let internal findFirstContentBeforeByRange (trivia: TriviaNode list) (range: range) =
let findFirstContentBeforeByRange (trivia: TriviaNode list) (range: range) =
findByRange trivia range
|> Option.bind (fun t -> t.ContentBefore |> List.tryHead)

let internal ``has content after after that matches`` (findTrivia: TriviaNode -> bool) (contentAfter: TriviaContent -> bool) (trivia: TriviaNode list) =
let ``has content after after that matches`` (findTrivia: TriviaNode -> bool) (contentAfter: TriviaContent -> bool) (trivia: TriviaNode list) =
List.tryFind findTrivia trivia
|> Option.map (fun t -> t.ContentAfter |> List.exists contentAfter)
|> Option.defaultValue false

let internal ``has content after that ends with`` (findTrivia: TriviaNode -> bool) (contentAfterEnd: TriviaContent -> bool) (trivia: TriviaNode list) =
let ``has content after that ends with`` (findTrivia: TriviaNode -> bool) (contentAfterEnd: TriviaContent -> bool) (trivia: TriviaNode list) =
List.tryFind findTrivia trivia
|> Option.bind (fun t -> t.ContentAfter |> List.tryLast |> Option.map contentAfterEnd)
|> Option.defaultValue false

let internal ``is token of type`` tokenName (triviaNode: TriviaNode) =
let ``is token of type`` tokenName (triviaNode: TriviaNode) =
match triviaNode.Type with
| Token({ TokenInfo = ti }) -> ti.TokenName = tokenName
| _ -> false

let internal ``keyword tokens inside range`` keywords range (trivia: TriviaNode list) =
let ``keyword tokens inside range`` keywords range (trivia: TriviaNode list) =
trivia
|> List.choose(fun t ->
match t.Type with
Expand All @@ -38,7 +38,7 @@ module TriviaHelpers =
| _ -> None
)

let internal ``has line comment after`` triviaNode =
let ``has line comment after`` triviaNode =
triviaNode.ContentAfter
|> List.filter(fun tn ->
match tn with
Expand All @@ -47,13 +47,21 @@ module TriviaHelpers =
)
|> (List.isEmpty >> not)

let internal ``has line comment before`` range triviaNodes =
let ``has line comment before`` range triviaNodes =
triviaNodes
|> List.tryFind (fun tv -> tv.Range = range)
|> Option.map (fun tv -> tv.ContentBefore |> List.exists (function | Comment(LineCommentOnSingleLine(_)) -> true | _ -> false))
|> Option.defaultValue false

let internal ``has content itself is ident between ticks`` range (triviaNodes: TriviaNode list) =
let ``get CharContent`` range triviaNodes =
triviaNodes
|> List.tryFind (fun tv -> tv.Range = range)
|> Option.bind (fun tv ->
match tv.ContentItself with
| Some(CharContent c) -> Some c
| _ -> None)

let ``has content itself is ident between ticks`` range (triviaNodes: TriviaNode list) =
triviaNodes
|> List.choose (fun tn ->
match tn.Range = range, tn.ContentItself with
Expand Down
1 change: 1 addition & 0 deletions src/Fantomas/TriviaTypes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type TriviaContent =
| Newline
| Directive of directive:string
| NewlineAfter
| CharContent of string

type Trivia =
{ Item: TriviaContent
Expand Down

0 comments on commit 16f9b36

Please sign in to comment.