Repository: domluna/JuliaFormatter.jl
Pull request #35 (2019-08-17 19:22:42)

Eq in normalization
Transforms `=` to `in` if part of a for loop condition, ref https://github.com/domluna/JuliaFormatter.jl/issues/34. Kind of funny, calling format on the package after this changed a loop condition. I guess it works 😄 

@StefanKarpinski any other test cases you think would be good?



StefanKarpinski: Isn't this the opposite rule from what I proposed?
domluna: Hmmm, I guess misinterpreted it and now I'm kind of confused.

> However, this superficial syntax difference can help catch a common error, which is writing `for i = n` when you meant to write `for i = 1:n`. If we require `in` there then the programmer will notice that `for i in n` is not what they meant to write and catch the bug.

Doesn't argue for replacing `=` with `in`?

Follow up - would we use `=` only if it's a range like `1:10`?
domluna: Using the correct/incorrect examples #34 

Correct (these remain the same)

```julia
for i = 1:n
    println(i)
end

for i in itr
    println(i)
end
```
Incorrect

```julia
# remains the same
for i in 1:n
    println(i)
end

# `=` is replaced with `in`
for i = itr
    println(i)
end
```

So only the last example would warrant an actual transformation. Is this in line with what you meant?
StefanKarpinski: > Follow up - would we allow `=` only if it's a range like `1:10`?

Yes, and only allow `=` if the RHS is a range. So both of the later "incorrect" examples would be transformed:
* `for i in 1:n` would be turned into `for i = 1:10` and
* `for i = itr` would be turned into `for i in itr`.
domluna: @StefanKarpinski I think this does what it should now.

Patch:
diff --git a/src/pretty.jl b/src/pretty.jl
index 9118834a..168f644e 100644
--- a/src/pretty.jl
+++ b/src/pretty.jl
@@ -35,6 +35,10 @@ empty_start(x::PTree) = x.startline == 1 && x.endline == 1 && x.val == ""
 is_punc(x) = CSTParser.ispunctuation(x)
 is_end(x) = x.typ === CSTParser.KEYWORD && x.val == "end"
 
+is_colon_op(x) =
+    (x.typ === CSTParser.BinaryOpCall && x.args[2].kind === Tokens.COLON) ||
+    x.typ === CSTParser.ColonOpCall
+
 function parent_is(x, typs...)
     p = x.parent
     p === nothing && return false
@@ -781,11 +785,49 @@ function p_let(x, s)
     t
 end
 
+
+
+# Transforms
+#
+# for i = iter body end
+#
+# =>
+#
+# for i in iter body end
+#
+# AND
+#
+# for i in 1:10 body end
+#
+# =>
+#
+# for i = 1:10 body end
+#
+# https://github.com/domluna/JuliaFormatter.jl/issues/34
+function eq_to_in_normalization!(x)
+    if x.typ === CSTParser.BinaryOpCall
+        op = x.args[2]
+        arg2 = x.args[3]
+        if op.kind === Tokens.EQ && !is_colon_op(arg2)
+            x.args[2].kind = Tokens.IN
+        elseif op.kind === Tokens.IN && is_colon_op(arg2)
+            x.args[2].kind = Tokens.EQ
+        end
+    elseif x.typ === CSTParser.Block || x.typ === CSTParser.InvisBrackets
+        for a in x.args
+            eq_to_in_normalization!(a)
+        end
+    end
+end
+
 # For/While
 function p_loop(x, s)
     t = PTree(x, nspaces(s))
     add_node!(t, pretty(x.args[1], s))
     add_node!(t, Whitespace(1))
+    if x.args[1].kind === Tokens.FOR
+        eq_to_in_normalization!(x.args[2])
+    end
     add_node!(t, pretty(x.args[2], s), join_lines = true)
     s.indent += s.indent_size
     add_node!(
@@ -1411,6 +1453,14 @@ function p_comprehension(x, s)
             add_node!(t, Whitespace(1))
             add_node!(t, pretty(a, s), join_lines = true)
             add_node!(t, Whitespace(1))
+            if a.kind === Tokens.FOR
+                for j = i+1:length(x)
+                    eq_to_in_normalization!(x.args[j])
+                end
+            end
+        elseif CSTParser.is_comma(a) && i < length(x) && !is_punc(x.args[i+1])
+            add_node!(t, pretty(a, s), join_lines = true)
+            add_node!(t, Whitespace(1))
         else
             add_node!(t, pretty(a, s), join_lines = true)
         end
diff --git a/src/print.jl b/src/print.jl
index abedbad1..25c74c34 100644
--- a/src/print.jl
+++ b/src/print.jl
@@ -47,7 +47,7 @@ end
 
 @inline function print_notcode(io, x, s)
     prev_nl = true
-    for l in x.startline:x.endline
+    for l = x.startline:x.endline
         v = getline(s.doc, l)
         v == "" && continue
         # @info "comment line" l v


Test patch:
diff --git a/test/runtests.jl b/test/runtests.jl
index 26dd6d19..d714b75b 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -35,6 +35,76 @@ end
         @test fmt(str) == str
     end
 
+    @testset "for = vs in normalization" begin
+        str = """
+        for i = 1:n
+            println(i)
+        end"""
+        @test fmt(str) == str
+
+        str = """
+        for i in itr
+            println(i)
+        end"""
+        @test fmt(str) == str
+
+        str = """
+        for i = 1:n
+            println(i)
+        end"""
+        str_ = """
+        for i in 1:n
+            println(i)
+        end"""
+        @test fmt(str) == str
+
+        str = """
+        for i in itr
+            println(i)
+        end"""
+        str_ = """
+        for i = itr
+            println(i)
+        end"""
+        @test fmt(str_) == str
+
+        str_ = """
+        for i = I1, j in I2
+            println(i, j)
+        end"""
+        str = """
+        for i in I1, j in I2
+            println(i, j)
+        end"""
+        @test fmt(str_) == str
+
+        str = """
+        for i = 1:30, j = 100:-2:1
+            println(i, j)
+        end"""
+        str_ = """
+        for i = 1:30, j in 100:-2:1
+            println(i, j)
+        end"""
+        @test fmt(str) == str
+
+        str_ = "[(i,j) for i=I1,j=I2]"
+        str = "[(i, j) for i in I1, j in I2]"
+        @test fmt(str_) == str
+
+        str_ = "((i,j) for i=I1,j=I2)"
+        str = "((i, j) for i in I1, j in I2)"
+        @test fmt(str_) == str
+
+        str_ = "[(i,j) for i in 1:2:10,j  in 100:-1:10]"
+        str = "[(i, j) for i = 1:2:10, j = 100:-1:10]"
+        @test fmt(str_) == str
+
+        str_ = "((i,j) for i in 1:2:10,j  in 100:-1:10)"
+        str = "((i, j) for i = 1:2:10, j = 100:-1:10)"
+        @test fmt(str_) == str
+    end
+
     @testset "tuples" begin
         @test fmt("a,b") == "a, b"
         @test fmt("a ,b") == "a, b"
@@ -447,15 +517,15 @@ end
             arg
         end"""
         @test fmt("""
-        for iter in I, iter2 in I2
+        for iter = I, iter2= I2
             arg
         end""") == str
         @test fmt("""
-        for iter in I, iter2 in I2
+        for iter= I, iter2=I2
         arg
         end""") == str
         @test fmt("""
-        for iter in I, iter2 in I2
+        for iter    = I, iter2 = I2
                 arg
             end""") == str
 
@@ -467,7 +537,7 @@ end
         @test length(t) == 12
 
         str = """
-        for i = 1:10
+        for i in 1:10
             bodybodybodybody
         end"""
         t = run_pretty(str, 80)
@@ -1224,7 +1294,10 @@ end
         @test fmt("T[a;   b;         c;   e  d    f   ]") == str
 
         str = """T[e for e in x]"""
-        @test fmt("T[e  for e in x  ]") == str
+        @test fmt("T[e  for e= x  ]") == str
+
+        str = """T[e for e = 1:2:50]"""
+        @test fmt("T[e  for e= 1:2:50  ]") == str
 
         str = """struct Foo end"""
         @test fmt("struct Foo\n      end") == str
@@ -1492,7 +1565,7 @@ end
         @test fmt("(a;b;c)", 4, 100) == str
         @test fmt("(a;b;c)", 4, 1) == str
 
-        str = "(x for x in 1:10)"
+        str = "(x for x = 1:10)"
         @test fmt("(x   for x  in  1 : 10)", 4, 100) == str
         @test fmt("(x   for x  in  1 : 10)", 4, 1) == str