Repository: domluna/JuliaFormatter.jl
Pull request #66 (2019-09-14 21:27:11)

Begin to attempt to add trailing zeros to Float literals
So I wanted to implement adding trailing zeros to Float literals (https://github.com/domluna/JuliaFormatter.jl/issues/61), but I quickly got stuck. 

I haven't quite got my head around `PTree`s, so I'm stumbling around in the dark.

Is this the correct way to implement this? Or do I need to add a node to the tree like you did here:
https://github.com/domluna/JuliaFormatter.jl/commit/8944eaad622b8b88fa8b0f1d9e0dd7ef506fc742#diff-9915f66306b42c958fcd076dc0eee523R1228

At the moment I get
```Julia
julia> JuliaFormatter.format_text("1.")
"1.0"

julia> JuliaFormatter.format_text("1. + 1")
ERROR: Indexing range 1 - 6, index used = 7
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] cursor_loc at /Users/oscar/.julia/dev/JuliaFormatter/src/JuliaFormatter.jl:100 [inlined]
 [3] cursor_loc at /Users/oscar/.julia/dev/JuliaFormatter/src/JuliaFormatter.jl:102 [inlined]
 [4] p_literal(::CSTParser.EXPR, ::JuliaFormatter.State) at /Users/oscar/.julia/dev/JuliaFormatter/src/pretty.jl:386
 [5] pretty(::CSTParser.EXPR, ::JuliaFormatter.State) at /Users/oscar/.julia/dev/JuliaFormatter/src/pretty.jl:192
 [6] #p_binarycall#11(::Bool, ::Bool, ::Function, ::CSTParser.EXPR, ::JuliaFormatter.State) at /Users/oscar/.julia/dev/JuliaFormatter/src/pretty.jl:1139
 [7] p_binarycall(::CSTParser.EXPR, ::JuliaFormatter.State) at /Users/oscar/.julia/dev/JuliaFormatter/src/pretty.jl:1091
 [8] pretty(::CSTParser.EXPR, ::JuliaFormatter.State) at /Users/oscar/.julia/dev/JuliaFormatter/src/pretty.jl:252
 [9] pretty(::CSTParser.EXPR, ::JuliaFormatter.State) at /Users/oscar/.julia/dev/JuliaFormatter/src/pretty.jl:307
 [10] #format_text#52(::Int64, ::Int64, ::Function, ::String) at /Users/oscar/.julia/dev/JuliaFormatter/src/JuliaFormatter.jl:135
 [11] format_text(::String) at /Users/oscar/.julia/dev/JuliaFormatter/src/JuliaFormatter.jl:123
 [12] top-level scope at none:0
```

domluna: 

@odow This looks like it's on the right track. https://github.com/julia-vscode/CSTParser.jl/blob/master/src/spec.jl is the best reference for node types. The only new types introduced in the formatter are `PLeaf` types.

You shouldn't be editing the CST from `CSTParser`, it's just used to create the `PTree`. We probably also want to add an additional check besides just val[end] == '.'. I'm not sure if this occurs in other cases besides floats without a trailing zero.

This should work:

```julia
        val = x.val
        if x.kind === Tokens.FLOAT && val[end] == '.'
            val *= '0'
        end
        return PTree(x, loc[1], loc[1], val)
```

Token types are found in https://github.com/JuliaLang/Tokenize.jl/blob/master/src/token_kinds.jl
domluna: BTW Does the trailing zero come up at all with complex numbers or would this already that?
domluna: LGTM. Let me know if it's ready to merge in or if you're still making changes.
odow: Thanks for you help. I was missing the fact that the `x.val` argument to the `PTree` didn't have to be `x.val`.

I believe `Tokens.FLOAT` is the only case where there can be a trailing `.`. I've added tests with complex number examples. 

What's the policy on running `format("src")` and `format("test")` on a PR? Because there seemed to be a lot of indentation changes, but I didn't include them because they seemed unrelated.
domluna: No problem. I need to add more documentation to this thing so it's kind of my fault :upside_down_face: . 

Generally I do `format("src")` and `format("test/runtests.jl")`.
odow: I guess one hack would be to add 
```Julia
format(joinpath(dirname(@__DIR__, "src")))
format(@__FILE__)
```
to the bottom of `test/runtests.jl` so that anyone running the tests also auto-formats things.
domluna: that or GH support can get back to me so I can get this to work https://github.com/domluna/JuliaFormatter-action :smile: 

Anyway I'm gonna merge this in, thanks again!

Patch:
diff --git a/src/pretty.jl b/src/pretty.jl
index d893355f..dcc15578 100644
--- a/src/pretty.jl
+++ b/src/pretty.jl
@@ -431,8 +431,13 @@ end
 function p_literal(x, s)
     loc = cursor_loc(s)
     if !is_str_or_cmd(x.kind)
+        val = x.val
+        if x.kind === Tokens.FLOAT && x.val[end] == '.'
+            # If a floating point ends in `.`, add trailing zero.
+            val *= '0'
+        end
         s.offset += x.fullspan
-        return PTree(x, loc[1], loc[1], x.val)
+        return PTree(x, loc[1], loc[1], val)
     end
 
     # Strings are unescaped by CSTParser
@@ -441,7 +446,7 @@ function p_literal(x, s)
     # So we'll just look at the source directly!
     str_info = get(s.doc.lit_strings, s.offset - 1, nothing)
 
-    # Tokenize treats the `ix` part of r"^(=?[^=]+)=(.*)$"ix as an 
+    # Tokenize treats the `ix` part of r"^(=?[^=]+)=(.*)$"ix as an
     # IDENTIFIER where as CSTParser parses it as a LITERAL.
     # An IDENTIFIER won't show up in the string literal lookup table.
     if str_info === nothing && x.parent.typ === CSTParser.x_Str


Test patch:
diff --git a/test/runtests.jl b/test/runtests.jl
index 76f253e7..e97d97c8 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -2508,6 +2508,17 @@ end
         @test fmt(str_, 4, length(str_) - 1) == str
     end
 
+    @testset "Trailing zeros" begin
+        @test fmt("1.") == "1.0"
+        @test fmt("a * 1. + b") == "a * 1.0 + b"
+        @test fmt("1. + 2. * im") == "1.0 + 2.0 * im"
+        @test fmt("[1., 2.]") == "[1.0, 2.0]"
+        @test fmt("""
+        1. +
+            2.
+        """) == "1.0 + 2.0\n"
+    end
+
 # @testset "meta-format" begin
 #     str = String(read("./runtests.jl"))
 #     str = fmt(str)