Repository: domluna/JuliaFormatter.jl (julia)

Issue:
invalid bluestyle format result with trailing semicolon : for array indexing
MWE:

```julia
codes = """
a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name = [1 1; 1 1]

x = a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[sum(a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[:, 1]), :]
"""

formatted = format_text(codes, BlueStyle())
# will be formatted to
# """
# a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name = [1 1; 1 1]
# 
# x = a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[
#     sum(a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[:, 1]), :
# ]
# """

Meta.parse("begin $codes end") # okay
Meta.parse("begin $formatted end") # not okay: raise Base.Meta.ParseError
```

```julia
julia> Meta.parse("begin $codes end")
quote
    #= none:1 =#
    a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name = [1 1; 1 1]
    #= none:3 =#
    x = a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[sum(a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[:, 1]), :]
end

julia> Meta.parse("begin $formatted end")
ERROR: Base.Meta.ParseError("newline not allowed after \":\" used for quoting")
Stacktrace:
 [1] #parse#3
   @ ./meta.jl:236 [inlined]
 [2] parse
   @ ./meta.jl:232 [inlined]
 [3] parse(str::String; raise::Bool, depwarn::Bool)
   @ Base.Meta ./meta.jl:267
 [4] parse(str::String)
   @ Base.Meta ./meta.jl:266
 [5] top-level scope
   @ REPL[20]:1
```

Pull request:
fixes #700, #713, #714, #715, #694

Patch:
diff --git a/Project.toml b/Project.toml
index 93ed57de6..406ca8046 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
 name = "JuliaFormatter"
 uuid = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
 authors = ["Dominique Luna <dluna132@gmail.com>"]
-version = "1.0.27"
+version = "1.0.28"
 
 [deps]
 CSTParser = "00ebfdb7-1f24-5e51-bd34-a7502290713f"
diff --git a/src/align.jl b/src/align.jl
index 2eef9164c..f08299772 100644
--- a/src/align.jl
+++ b/src/align.jl
@@ -332,7 +332,10 @@ function align_matrix!(fst::FST)
 
                 diff = n2.line_offset - n1.line_offset - length(n1)
 
-                r[i] = Whitespace(diff)
+                # fix #694 and #713
+                if diff > 0
+                    r[i] = Whitespace(diff)
+                end
             end
         end
     end
diff --git a/src/fst.jl b/src/fst.jl
index 56116b953..b46868045 100644
--- a/src/fst.jl
+++ b/src/fst.jl
@@ -1170,3 +1170,12 @@ function caller_in_list(fst, list)
 
     return false
 end
+
+"""
+    is_dot_op(x::CSTParser.EXPR)
+
+Checks if the binary operation is a dot operation (e.g. `x.y`, `x..z`, `x...z`). This is different from a dot or broadcast operation.
+"""
+function is_dot_op(x::CSTParser.EXPR)
+    op_kind(x) in (Tokens.DDDOT, Tokens.DDOT, Tokens.DDOTS)
+end
diff --git a/src/styles/blue/nest.jl b/src/styles/blue/nest.jl
index 6264a8f02..103c459cf 100644
--- a/src/styles/blue/nest.jl
+++ b/src/styles/blue/nest.jl
@@ -94,7 +94,10 @@ function n_tuple!(bs::BlueStyle, fst::FST, s::State)
                     nest!(style, n, s)
                 end
             elseif n.typ === TRAILINGCOMMA
-                if add_trailing_comma
+                if add_trailing_comma ||
+                   nodes[i-1].typ === OPERATOR &&
+                   nodes[i-1].metadata.op_kind === Tokens.COLON
+                    # if add_trailing_comma  || nodes[i-1].typ === OPERATOR && nodes[i-1].val == ":"
                     n.val = ","
                     n.len = 1
                 end
diff --git a/src/styles/blue/pretty.jl b/src/styles/blue/pretty.jl
index 5be808894..3ee02af51 100644
--- a/src/styles/blue/pretty.jl
+++ b/src/styles/blue/pretty.jl
@@ -140,8 +140,9 @@ function p_binaryopcall(
     elseif CSTParser.is_exor(op)
         add_node!(t, pretty(style, op, s), s, join_lines = true)
     elseif (
-        CSTParser.isnumber(cst[1]) || is_fwdfwd_slash(op) || is_circumflex_accent(op)
-    ) && CSTParser.isdotted(op)
+        (CSTParser.isnumber(cst[1]) || is_fwdfwd_slash(op) || is_circumflex_accent(op)) &&
+        CSTParser.isdotted(op)
+    ) || (CSTParser.isnumber(cst[3]) && startswith(cst[3].val, "-") && is_dot_op(cst))
         add_node!(t, Whitespace(1), s)
         add_node!(t, pretty(style, op, s), s, join_lines = true)
         nest ? add_node!(t, Placeholder(1), s) : add_node!(t, Whitespace(1), s)
diff --git a/src/styles/default/pretty.jl b/src/styles/default/pretty.jl
index 0fcdb69f1..cb3373422 100644
--- a/src/styles/default/pretty.jl
+++ b/src/styles/default/pretty.jl
@@ -1591,8 +1591,11 @@ function p_binaryopcall(
         # For example: `2a`, which is equivalent to `2 * a`.
     elseif CSTParser.is_exor(op)
         add_node!(t, pretty(style, op, s), s, join_lines = true)
-    elseif (CSTParser.isnumber(cst[1]) || is_circumflex_accent(op)) &&
-           CSTParser.isdotted(op)
+    elseif (
+        (CSTParser.isnumber(cst[1]) || is_circumflex_accent(op)) && CSTParser.isdotted(op)
+    ) ||
+           # 1 .. -2 (can be ., .., ..., etc)
+           (CSTParser.isnumber(cst[3]) && startswith(cst[3].val, "-") && is_dot_op(cst))
         add_node!(t, Whitespace(1), s)
         add_node!(t, pretty(style, op, s), s, join_lines = true)
         nest ? add_node!(t, Placeholder(1), s) : add_node!(t, Whitespace(1), s)


Test patch:
diff --git a/test/issues.jl b/test/issues.jl
index c14a5a911..f5f2f95ef 100644
--- a/test/issues.jl
+++ b/test/issues.jl
@@ -1601,6 +1601,22 @@
         @test fmt(s1, 4, 10, short_to_long_function_def = true) == s2
     end
 
+    @testset "700" begin
+        s1 = """
+        a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name = [1 1; 1 1]
+
+        x = a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[sum(a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[:, 1]), :]
+        """
+        s2 = """
+        a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name = [1 1; 1 1]
+
+        x = a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[
+            sum(a_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_array_name[:, 1]), :,
+        ]
+        """
+        @test bluefmt(s1, 4, 92) == s2
+    end
+
     @testset "703" begin
         s = """
         mutable struct A
@@ -1610,4 +1626,34 @@
         """
         @test fmt(s, 4, 92, align_struct_field = true) == s
     end
+
+    @testset "713" begin
+        s1 = """
+        [1. 1; 1 -1]
+        """
+        s2 = """
+        [1.0 1; 1 -1]
+        """
+        @test fmt(s1, 4, 92, align_matrix = true) == s2
+    end
+
+    @testset "714" begin
+        s1 = """
+        A = [-2.. -1 3..4; 5..6 7..8]
+        """
+        s2 = """
+        A = [-2 .. -1 3..4; 5..6 7..8]
+        """
+        @test fmt(s1, 4, 92) == s2
+    end
+
+    @testset "715" begin
+        s = """
+        map(1:10) do x
+            # empty
+        end
+        """
+        @test fmt(s, 4, 92) == s
+        @test bluefmt(s, 4, 92) == s
+    end
 end