Repository: fredrikekre/Runic.jl (julia)

Issue:
Runic breaks unpacking of only the first element of a tuple
```julia
julia> Runic.format_string("x, = (1, 2, 3)")
"x = (1, 2, 3)"
```
That is, of course, not equivalent:
```julia
julia> x, = (1, 2, 3); x
1

julia> x = (1, 2, 3); x
(1, 2, 3)
```

Pull request description:
This patch fixes the usage of trailing commas in implicit tuples when used in destructuring assignment, e.g. `x, = z`. In this context the trailing comma is needed to preserve the tuple node, which is different from e.g. implicit tuples in `do`-blocks. A trailing comma is allowed (e.g. preserved from the source) for multi-item implicit tuples so that e.g. `x, y, = z` can be used to signal that z contains more than two items. Closes #58.

Patch:
diff --git a/src/runestone.jl b/src/runestone.jl
index c1b4956..00dbabc 100644
--- a/src/runestone.jl
+++ b/src/runestone.jl
@@ -308,6 +308,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
         if opening_leaf_idx === nothing
             # Implicit tuple without (), for example arguments in a do-block
             implicit_tuple = true
+            @assert kind(node) === K"tuple"
             opening_leaf_idx = findfirst(!JuliaSyntax.is_whitespace, kids)
             if opening_leaf_idx === nothing
                 # All whitespace... return?
@@ -366,7 +367,13 @@ function spaces_in_listlike(ctx::Context, node::Node)
     if kind(node) in KSet"call dotcall macrocall"
         require_trailing_comma = false
     elseif implicit_tuple
-        require_trailing_comma = false
+        # Trailing commas in implicit tuples in the LHS of an assignment, e.g. `x, = 1, 2`,
+        # is required for single item tuples and allowed for multiple items (to allow e.g.
+        # `x, y, = z` signaling that z contains more items).
+        # Note that an implicit single item tuple can never(?) end up on the RHS so there is
+        # no need to make sure this is the LHS node.
+        require_trailing_comma = n_items == 1 && ctx.lineage_kinds[end] === K"="
+        allow_trailing_comma = ctx.lineage_kinds[end] === K"="
     elseif kind(node) === K"tuple" && n_items == 1 && ctx.lineage_kinds[end] !== K"function" &&
             kind(kids[first_item_idx::Int]) !== K"parameters"
         # TODO: May also have to check for K"where" and K"::" in the lineage above


Test patch:
diff --git a/test/runtests.jl b/test/runtests.jl
index c32586d..a0943d4 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -824,6 +824,10 @@ end
         # implicit tuple
         @test format_string("a,\n$(sp)b") == "a,\n    b"
         @test format_string("a,\n$(sp)b + \nb") == "a,\n    b +\n    b"
+        # implicit tuple in destructuring (LHS of K"=")
+        @test format_string("a,$(sp)=$(sp)z") == "a, = z"
+        @test format_string("a,$(sp)b$(sp)=$(sp)z") == "a, b = z"
+        @test format_string("a,$(sp)b$(sp),$(sp)=$(sp)z") == "a, b, = z"
         # K"cartesian_iterator"
         @test format_string("for i in I,\n$(sp)j in J\n# body\nend") ==
             "for i in I,\n        j in J\n    # body\nend"