Repository: domluna/JuliaFormatter.jl
Pull request #121 (2019-11-17 00:47:49)

Format Generators + other stuff I stumbled into
This started out as a fix for #116 and #114 but turned into somewhat as a grab-bag. Additionally to generators formatting better:

- the margin introduced by a multiline string is far more accurate
- fix corner case bug during printing multiline strings by deferring final indent calculation to printing stage
- determine nesting for invisbrackets during pretty stage - fixed a bug
- allow nesting even for 1 argument in closing punc types, i.e. Call, MacroCall, TupleH, etc.


Patch:
diff --git a/src/JuliaFormatter.jl b/src/JuliaFormatter.jl
index 48818236..fd265731 100644
--- a/src/JuliaFormatter.jl
+++ b/src/JuliaFormatter.jl
@@ -223,6 +223,7 @@ function format_text(
     hascomment(s.doc, t.endline) && (add_node!(t, InlineComment(t.endline), s))
     nest!(t, s)
 
+    s.line_offset = 0
     io = IOBuffer()
 
     # Print comments and whitespace before code.
diff --git a/src/nest.jl b/src/nest.jl
index f0ac70a0..9eba6e69 100644
--- a/src/nest.jl
+++ b/src/nest.jl
@@ -99,8 +99,6 @@ function nest!(x::PTree, s::State; extra_width = 0)
         n_for!(x, s, extra_width = extra_width)
     elseif x.typ === CSTParser.TypedVcat
         n_call!(x, s, extra_width = extra_width)
-    elseif x.typ === CSTParser.StringH
-        n_stringh!(x, s, extra_width = extra_width)
     elseif x.typ === CSTParser.Parameters
         n_tuple!(x, s, extra_width = extra_width)
     elseif x.typ === CSTParser.Braces
@@ -217,20 +215,6 @@ function n_tuple!(x, s; extra_width = 0)
     end
 end
 
-
-function n_stringh!(x, s; extra_width = 0)
-    # The indent of StringH is set to the the offset
-    # of when the quote is first encountered in the source file.
-
-    # This difference notes if there is a change due to nesting.
-    diff = x.indent - s.line_offset
-
-    # The new indent for the string is index of when a character in
-    # the multiline string is FIRST encountered in the source file - the above difference
-    x.indent = max(x.nodes[1].indent - diff, 0)
-    nest!(x.nodes, s, x.indent, extra_width = extra_width)
-end
-
 function n_for!(x, s; extra_width = 0)
     block_idx = findfirst(n -> !is_leaf(n), x.nodes)
     if block_idx === nothing
@@ -254,7 +238,7 @@ end
 function n_call!(x, s; extra_width = 0)
     line_width = s.line_offset + length(x) + extra_width
     idx = findlast(n -> n.typ === PLACEHOLDER, x.nodes)
-    # @info "ENTERING" x.typ s.line_offset length(x) extra_width s.margin
+    # @info "ENTERING" x.typ s.line_offset length(x) extra_width s.margin x.nodes[1].val
     if idx !== nothing && (line_width > s.margin || x.force_nest)
         x.nodes[end].indent = x.indent
         line_offset = s.line_offset
@@ -355,17 +339,18 @@ function n_wherecall!(x, s; extra_width = 0)
             (!is_leaf(n) || n.typ === CSTParser.IDENTIFIER) && (last_typ = n.typ)
         end
 
+
         # @info "" s.line_offset x.typ extra_width has_braces
 
         # Properly reset line offset in the case the last
         # argument is an IDENTIFIER.
         # if over && has_braces && last_typ === CSTParser.IDENTIFIER && x.nodes[end-2].typ === TRAILINGCOMMA
-        if over &&
-           has_braces &&
-           last_typ === CSTParser.IDENTIFIER && x.nodes[end-2].typ === TRAILINGCOMMA
+        if over && has_braces
             s.line_offset = x.nodes[end].indent + 1
         end
 
+        # s.line_offset = line_offset
+        # walk(reset_line_offset!, x, s)
         # @info "" s.line_offset x.typ extra_width has_braces
     else
         nest!(x.nodes, s, x.indent, extra_width = extra_width)
@@ -446,11 +431,8 @@ function n_binarycall!(x, s; extra_width = 0)
     # If there's no placeholder the binary call is not nestable
     idxs = findall(n -> n.typ === PLACEHOLDER, x.nodes)
     line_width = s.line_offset + length(x) + extra_width
-    invis_args = x.nodes[1].typ === CSTParser.InvisBrackets ||
-                 x.nodes[end].typ === CSTParser.InvisBrackets
-    # arg2_invis = false
     # @info "ENTERING" x.typ extra_width s.line_offset length(x) idxs x.ref[][2]
-    if length(idxs) == 2 && (line_width > s.margin && !invis_args || x.force_nest)
+    if length(idxs) == 2 && (line_width > s.margin || x.force_nest)
         line_offset = s.line_offset
         i1 = idxs[1]
         i2 = idxs[2]
@@ -490,7 +472,7 @@ function n_binarycall!(x, s; extra_width = 0)
                 line_width = s.line_offset + 1 + length(x.nodes[end])
                 can_unnest = line_width + extra_width <= s.margin
             else
-                rw, _ = length_to(x, (NEWLINE,), start = i2 + 1)
+                rw, _ = length_to(x, [NEWLINE], start = i2 + 1)
                 line_width = s.line_offset + 1 + rw
                 can_unnest = line_width + extra_width <= s.margin
             end
@@ -549,7 +531,7 @@ function n_binarycall!(x, s; extra_width = 0)
         if idx !== nothing && idx > 1
             return_width = length(x.nodes[idx].nodes[1]) + length(x.nodes[2])
         elseif idx === nothing
-            return_width, _ = length_to(x, (PLACEHOLDER, NEWLINE), start = 2)
+            return_width, _ = length_to(x, [PLACEHOLDER, NEWLINE], start = 2)
         end
 
         # @info "" return_width
diff --git a/src/pretty.jl b/src/pretty.jl
index d53aedde..b7fcc0ed 100644
--- a/src/pretty.jl
+++ b/src/pretty.jl
@@ -74,7 +74,8 @@ end
 # https://github.com/julia-vscode/CSTParser.jl/issues/108
 function n_args(x::CSTParser.EXPR)
     n = 0
-    if x.typ === CSTParser.MacroCall
+    if x.typ === CSTParser.MacroCall ||
+       x.typ === CSTParser.TypedVcat || x.typ === CSTParser.Ref
         for i = 2:length(x.args)
             arg = x.args[i]
             CSTParser.ispunctuation(arg) && continue
@@ -91,7 +92,11 @@ function n_args(x::CSTParser.EXPR)
             end
         end
         return n
-    elseif x.typ === CSTParser.Parameters
+    elseif x.typ === CSTParser.Parameters ||
+           x.typ === CSTParser.Braces ||
+           x.typ === CSTParser.Vcat ||
+           x.typ === CSTParser.TupleH ||
+           x.typ === CSTParser.Vect || x.typ === CSTParser.InvisBrackets
         for i = 1:length(x.args)
             arg = x.args[i]
             CSTParser.ispunctuation(arg) && continue
@@ -130,7 +135,10 @@ function add_node!(t::PTree, n::PTree, s::State; join_lines = false, max_padding
         en = t.nodes[end]
         if en.typ === CSTParser.Generator ||
            en.typ === CSTParser.Filter ||
-           en.typ === CSTParser.Flatten || en.typ === CSTParser.MacroCall
+           en.typ === CSTParser.Flatten ||
+           en.typ === CSTParser.MacroCall || (
+            is_comma(en) && t.typ === CSTParser.TupleH && n_args(t.ref[]) == 1
+        )
             # don't insert trailing comma in these cases
         elseif is_comma(en)
             t.nodes[end] = n
@@ -163,7 +171,7 @@ function add_node!(t::PTree, n::PTree, s::State; join_lines = false, max_padding
         end
         add_node!(t, Semicolon(), s)
         if length(n.nodes) > 0
-            multi_arg = n_args(t.ref[]) > 1
+            multi_arg = n_args(t.ref[]) > 0
             multi_arg ? add_node!(t, Placeholder(1), s) : add_node!(t, Whitespace(1), s)
         end
     end
@@ -229,9 +237,16 @@ function add_node!(t::PTree, n::PTree, s::State; join_lines = false, max_padding
     if !join_lines && is_end(n)
         # end keyword isn't useful w.r.t margin lengths
     elseif t.typ === CSTParser.StringH
+        # @info "insert literal into stringh" length(n) n n.indent + length(n) - t.indent t.indent n.indent
+
         # The length of this node is the length of
-        # the longest string
-        t.len = max(t.len, length(n))
+        # the longest string. The length of the string is
+        # only considered "in the positive" when it's past
+        # the hits the initial """ offset, i.e. `t.indent`.
+        t.len = max(t.len, n.indent + length(n) - t.indent)
+    elseif n.typ === CSTParser.StringH
+        closing_punc_type(t) && n_args(t.ref[]) > 1 && (t.force_nest = true)
+        t.len += length(n)
     elseif max_padding >= 0
         t.len = max(t.len, length(n) + max_padding)
     else
@@ -255,7 +270,7 @@ end
 
 Returns the length to any node type in `ntyps` based off the `start` index.
 """
-function length_to(x::PTree, ntyps; start::Int = 1)
+function length_to(x::PTree, ntyps::Vector; start::Int = 1)
     x.typ in ntyps && return 0, true
     is_leaf(x) && return length(x), false
     len = 0
@@ -534,11 +549,21 @@ function p_literal(x, s)
 
     # @debug "" lines x.val loc loc[2] sidx
 
-    t = PTree(CSTParser.StringH, -1, -1, loc[2], 0, nothing, PTree[], Ref(x), false)
+    t = PTree(CSTParser.StringH, -1, -1, loc[2] - 1, 0, nothing, PTree[], Ref(x), false)
     for (i, l) in enumerate(lines)
         ln = startline + i - 1
         l = i == 1 ? l : l[sidx:end]
-        tt = PTree(CSTParser.LITERAL, ln, ln, sidx, length(l), l, nothing, nothing, false)
+        tt = PTree(
+            CSTParser.LITERAL,
+            ln,
+            ln,
+            sidx - 1,
+            length(l),
+            l,
+            nothing,
+            nothing,
+            false,
+        )
         add_node!(t, tt, s)
     end
     t
@@ -569,11 +594,21 @@ function p_stringh(x, s)
 
     # @debug "" lines x.val loc loc[2] sidx
 
-    t = PTree(x, loc[2])
+    t = PTree(x, loc[2] - 1)
     for (i, l) in enumerate(lines)
         ln = startline + i - 1
         l = i == 1 ? l : l[sidx:end]
-        tt = PTree(CSTParser.LITERAL, ln, ln, sidx, length(l), l, nothing, nothing, false)
+        tt = PTree(
+            CSTParser.LITERAL,
+            ln,
+            ln,
+            sidx - 1,
+            length(l),
+            l,
+            nothing,
+            nothing,
+            false,
+        )
         add_node!(t, tt, s)
     end
     t
@@ -594,7 +629,7 @@ function p_macrocall(x, s)
         return t
     end
 
-    multi_arg = n_args(x) > 1
+    multi_arg = n_args(x) > 0
     has_closer = is_closer(x.args[end])
 
     # @info "" has_closer
@@ -1191,6 +1226,7 @@ closing_punc_type(x) =
     x.typ === CSTParser.Curly ||
     x.typ === CSTParser.Comprehension ||
     x.typ === CSTParser.MacroCall ||
+    x.typ === CSTParser.InvisBrackets ||
     x.typ === CSTParser.Ref || x.typ === CSTParser.TypedVcat
 
 # TODO: think of a better name?
@@ -1206,8 +1242,10 @@ nest_assignment(x::CSTParser.EXPR) = CSTParser.is_assignment(x) && block_type(x.
 function nestable(x::CSTParser.EXPR)
     CSTParser.defines_function(x) && x[1].typ !== CSTParser.UnaryOpCall && return true
     CSTParser.is_assignment(x) && return block_type(x.args[3])
-
-    op = x.args[2]
+    (
+     x[1].typ === CSTParser.InvisBrackets || x[3].typ === CSTParser.InvisBrackets
+    ) && return false
+    op = x[2]
     op.kind === Tokens.ANON_FUNC && return false
     op.kind === Tokens.PAIR_ARROW && return false
     CSTParser.precedence(op) in (1, 6) && return false
@@ -1269,10 +1307,9 @@ function p_binarycall(x, s; nonest = false, nospace = false)
     end
 
     narg2 = nest_arg2(x)
-    # @info "" nestable(x) !nonest narg2
-
     narg2 && (t.force_nest = true)
     nest = (nestable(x) && !nonest) || narg2
+    # @info "" nestable(x) !nonest narg2 nest x[2]
 
     if op.fullspan == 0 && x.args[3].typ === CSTParser.IDENTIFIER
         # do nothing
@@ -1327,7 +1364,7 @@ function p_wherecall(x, s)
     # Used to mark where `B` starts.
     add_node!(t, Placeholder(0), s)
 
-    multi_arg = length(CSTParser.get_where_params(x)) > 1
+    multi_arg = length(CSTParser.get_where_params(x)) > 0
     add_braces = !CSTParser.is_lbrace(x.args[3]) &&
                  x.parent.typ !== CSTParser.Curly && x.args[3].typ !== CSTParser.Curly
     add_braces && add_node!(
@@ -1396,7 +1433,7 @@ function p_curly(x, s)
     add_node!(t, pretty(x.args[1], s), s)
     add_node!(t, pretty(x.args[2], s), s, join_lines = true)
 
-    multi_arg = length(CSTParser.get_curly_params(x)) > 1
+    multi_arg = length(CSTParser.get_curly_params(x)) > 0
 
     if multi_arg
         add_node!(t, Placeholder(0), s)
@@ -1422,7 +1459,7 @@ function p_call(x, s)
     add_node!(t, pretty(x.args[1], s), s)
     add_node!(t, pretty(x.args[2], s), s, join_lines = true)
 
-    multi_arg = n_args(x) > 1
+    multi_arg = n_args(x) > 0
 
     if multi_arg
         add_node!(t, Placeholder(0), s)
@@ -1484,12 +1521,7 @@ end
 # TupleH
 function p_tuple(x, s)
     t = PTree(x, nspaces(s))
-    multi_arg = false
-    if CSTParser.is_lparen(x.args[1]) && length(x) > 4
-        multi_arg = true
-    elseif !CSTParser.is_lparen(x.args[1]) && length(x) > 2
-        multi_arg = true
-    end
+    multi_arg = n_args(x) > 0
 
     for (i, a) in enumerate(x)
         n = pretty(a, s)
@@ -1513,9 +1545,7 @@ end
 # Braces
 function p_braces(x, s)
     t = PTree(x, nspaces(s))
-    # {a,b}
-    multi_arg = length(x) > 4
-    # @debug "" multi_arg typeof(x)
+    multi_arg = length(x) > 2
 
     for (i, a) in enumerate(x)
         n = pretty(a, s)
@@ -1539,8 +1569,7 @@ end
 # Vect
 function p_vect(x, s)
     t = PTree(x, nspaces(s))
-    # [a,b]
-    multi_arg = length(x) > 4
+    multi_arg = length(x) > 2
 
     for (i, a) in enumerate(x)
         n = pretty(a, s)
@@ -1638,7 +1667,8 @@ end
 function p_vcat(x, s)
     t = PTree(x, nspaces(s))
     st = x.typ === CSTParser.Vcat ? 1 : 2
-    multi_arg = length(x) > st + 2
+    multi_arg = n_args(x) > 0
+    # @info "" multi_arg length(x) st
 
     for (i, a) in enumerate(x)
         n = pretty(a, s)
@@ -1652,7 +1682,7 @@ function p_vcat(x, s)
                 has_semicolon(s.doc, n.startline) && add_node!(t, TrailingSemicolon(), s)
                 add_node!(t, Placeholder(1), s)
             # Keep trailing semicolon if there's only one arg
-            elseif !multi_arg
+            elseif n_args(x) == 1
                 add_node!(t, Semicolon(), s)
                 add_node!(t, Placeholder(0), s)
             else
@@ -1701,22 +1731,27 @@ function p_gen(x, s)
     t = PTree(x, nspaces(s))
     for (i, a) in enumerate(x)
         if a.typ === CSTParser.KEYWORD
-            # if a.kind === Tokens.FOR && parent_is(
-            #     a,
-            #     x -> closing_punc_type(x),
-            #     ignore_typs = (
-            #         CSTParser.InvisBrackets,
-            #         CSTParser.Generator,
-            #         CSTParser.Flatten,
-            #         CSTParser.Filter,
-            #     ),
-            # )
+            if a.kind === Tokens.FOR && parent_is(
+                a,
+                x -> closing_punc_type(x),
+                ignore_typs = (
+                    CSTParser.InvisBrackets,
+                    CSTParser.Generator,
+                    CSTParser.Flatten,
+                    CSTParser.Filter,
+                ),
+            )
+                add_node!(t, Placeholder(1), s)
+            else
+                add_node!(t, Whitespace(1), s)
+            end
+            # if a.kind === Tokens.FOR
             #     add_node!(t, Placeholder(1), s)
-            #     t.indent += s.indent_size
             # else
             #     add_node!(t, Whitespace(1), s)
             # end
-            add_node!(t, Whitespace(1), s)
+
+            # add_node!(t, Placeholder(1), s)
             add_node!(t, pretty(a, s), s, join_lines = true)
             add_node!(t, Whitespace(1), s)
             if a.kind === Tokens.FOR
@@ -1724,6 +1759,8 @@ function p_gen(x, s)
                     eq_to_in_normalization!(x.args[j], s.always_for_in)
                 end
             end
+        elseif a.typ === CSTParser.BinaryOpCall
+            add_node!(t, p_binarycall(a, s, nonest = true), s, join_lines = true)
         elseif CSTParser.is_comma(a) && i < length(x) && !is_punc(x.args[i+1])
             add_node!(t, pretty(a, s), s, join_lines = true)
             add_node!(t, Whitespace(1), s)
diff --git a/src/print.jl b/src/print.jl
index c04f6060..43592881 100644
--- a/src/print.jl
+++ b/src/print.jl
@@ -52,6 +52,7 @@ function print_leaf(io::IOBuffer, x::PTree, s::State)
     else
         s.on && write(io, x.val)
     end
+    s.line_offset += length(x)
 end
 
 function print_tree(io::IOBuffer, x::PTree, s::State)
@@ -59,26 +60,48 @@ function print_tree(io::IOBuffer, x::PTree, s::State)
         print_leaf(io, x, s)
         return
     end
+    print_tree(io, x.nodes, s, x.indent)
+end
 
-    ws = repeat(" ", max(x.indent, 0))
-    for (i, n) in enumerate(x.nodes)
+function print_tree(io::IOBuffer, nodes::Vector{PTree}, s::State, indent::Int)
+    ws = repeat(" ", max(indent, 0))
+    for (i, n) in enumerate(nodes)
         if is_leaf(n)
             print_leaf(io, n, s)
+        elseif n.typ === CSTParser.StringH
+            print_stringh(io, n, s)
         else
             print_tree(io, n, s)
         end
 
-        if n.typ === NEWLINE && s.on && i < length(x.nodes)
-            if is_closer(x.nodes[i+1]) ||
-               x.nodes[i+1].typ === CSTParser.Block || x.nodes[i+1].typ === CSTParser.Begin
-                write(io, repeat(" ", x.nodes[i+1].indent))
-            elseif !skip_indent(x.nodes[i+1])
+        if n.typ === NEWLINE && s.on && i < length(nodes)
+            if is_closer(nodes[i+1]) ||
+               nodes[i+1].typ === CSTParser.Block || nodes[i+1].typ === CSTParser.Begin
+                write(io, repeat(" ", nodes[i+1].indent))
+                s.line_offset = nodes[i+1].indent
+            elseif !skip_indent(nodes[i+1])
                 write(io, ws)
+                s.line_offset = indent
             end
         end
     end
 end
 
+function print_stringh(io::IOBuffer, x::PTree, s::State)
+    # The indent of StringH is set to the the offset
+    # of when the quote is first encountered in the source file.
+
+    # This difference notes if there is a change due to nesting.
+    diff = x.indent + 1 - s.line_offset
+    # @info "" length(x) s.line_offset
+
+    # The new indent for the string is index of when a character in
+    # the multiline string is FIRST encountered in the source file - the above difference
+    # +1 since the character is 1 space after the indent
+    x.indent = max(x.nodes[1].indent + 1 - diff, 0)
+    print_tree(io, x, s)
+end
+
 function print_notcode(io::IOBuffer, x::PTree, s::State)
     s.on || return
     for l = x.startline:x.endline


Test patch:
diff --git a/test/files/Docs.jl b/test/files/Docs.jl
index 492df562..eae1039e 100644
--- a/test/files/Docs.jl
+++ b/test/files/Docs.jl
@@ -358,24 +358,28 @@ end
 
 function keyworddoc(__source__, __module__, str, def::Base.BaseDocs.Keyword)
     @nospecialize str
-    docstr = esc(docexpr(
-        __source__,
-        __module__,
-        lazy_iterpolate(str),
-        metadata(__source__, __module__, def, false),
-    ))
+    docstr = esc(
+        docexpr(
+            __source__,
+            __module__,
+            lazy_iterpolate(str),
+            metadata(__source__, __module__, def, false),
+        ),
+    )
     return :($setindex!($(keywords), $docstr, $(esc(quot(def.name)))); nothing)
 end
 
 function objectdoc(__source__, __module__, str, def, expr, sig = :(Union{}))
     @nospecialize str def expr sig
     binding = esc(bindingexpr(namify(expr)))
-    docstr = esc(docexpr(
-        __source__,
-        __module__,
-        lazy_iterpolate(str),
-        metadata(__source__, __module__, expr, false),
-    ))
+    docstr = esc(
+        docexpr(
+            __source__,
+            __module__,
+            lazy_iterpolate(str),
+            metadata(__source__, __module__, expr, false),
+        ),
+    )
     # Note: we want to avoid introducing line number nodes here (issue #24468)
     return Expr(:block, esc(def), :($(doc!)($__module__, $binding, $docstr, $(esc(sig)))))
 end
diff --git a/test/files/PProf.jl b/test/files/PProf.jl
index 2a5f1c47..29686e77 100644
--- a/test/files/PProf.jl
+++ b/test/files/PProf.jl
@@ -18,7 +18,9 @@ clear
 # Load in `deps.jl`, complaining if it does not exist
 const depsjl_path = joinpath(@__DIR__, "..", "deps", "deps.jl")
 if !isfile(depsjl_path)
-    error("PProf not installed properly, run Pkg.build(\"PProf\"), restart Julia and try again")
+    error(
+        "PProf not installed properly, run Pkg.build(\"PProf\"), restart Julia and try again",
+    )
 end
 include(depsjl_path)
 
diff --git a/test/files/cppwrapper.jl b/test/files/cppwrapper.jl
index 0474e653..194565c0 100644
--- a/test/files/cppwrapper.jl
+++ b/test/files/cppwrapper.jl
@@ -41,10 +41,9 @@ function JLFacet(f::CFacet{T}) where {T}
     JLFacet{T}(JLPolygon.(c_polys), holes)
 end
 
-function convert_poly(x::Vector{T}) where {T<:Union{
-    NgonFace{N,Cint},
-    NTuple{N,Cint},
-}} where {N}
+function convert_poly(
+    x::Vector{T},
+) where {T<:Union{NgonFace{N,Cint},NTuple{N,Cint}}} where {N}
     return map(1:length(x)) do i
         CPolygon(pointer(x, i), N)
     end
diff --git a/test/files/setup_databases.jl b/test/files/setup_databases.jl
index e8537519..1b49b546 100644
--- a/test/files/setup_databases.jl
+++ b/test/files/setup_databases.jl
@@ -29,7 +29,7 @@ function parse_commandline()
 
       It creates a `databases` folder in `--output` containing three folders:
       `pdb`, `uniclust` and `pdb70`
-      """,)
+      """)
 
     @add_arg_table settings begin
         "--output", "-o"
diff --git a/test/runtests.jl b/test/runtests.jl
index 6f2acbe4..61ff08b6 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -348,6 +348,7 @@ end
     end
 
     @testset "tuples" begin
+        @test fmt("(a,)") == "(a,)"
         @test fmt("a,b") == "a, b"
         @test fmt("a ,b") == "a, b"
         @test fmt("(a,b)") == "(a, b)"
@@ -637,7 +638,13 @@ end
         @test fmt("func(  a, b; c)") == "func(a, b; c)"
         @test fmt("func(a  ,b; c)") == "func(a, b; c)"
         @test fmt("func(a=1,b; c=1)") == "func(a = 1, b; c = 1)"
-        @test fmt("func(; c = 1)", 4, 1) == "func(; c = 1)"
+
+        str = """
+        func(;
+          c = 1,
+        )"""
+        @test fmt("func(; c = 1)", 2, 1) == str
+
         @test fmt("func(; c = 1,)") == "func(; c = 1)"
         @test fmt("func(a;)") == "func(a;)"
 
@@ -677,9 +684,13 @@ end
         @test fmt(str_) == str_
         @test fmt(str_, 4, 1) == str
 
-        str = "@f(; x)"
-        @test fmt(str) == str
-        @test fmt(str, 4, 1) == str
+        str = """
+        @f(;
+          x
+        )"""
+        str_ = "@f(; x)"
+        @test fmt(str_) == str_
+        @test fmt(str_, 2, 1) == str
 
         str = """
         @f(;
@@ -1375,6 +1386,22 @@ end
                                 can be created at https://gist.github.com/.\"""))
            end
         end"""
+        @test fmt(str_, 4, 200) == str
+
+        str = """
+        begin
+            begin
+                throw(
+                    ErrorException(
+                        \"""An error occured formatting \$filename. :-(
+
+                        Please file an issue at https://github.com/domluna/JuliaFormatter.jl/issues
+                        with a link to a gist containing the contents of the file. A gist
+                        can be created at https://gist.github.com/.\""",
+                    ),
+                )
+            end
+        end"""
         @test fmt(str_) == str
 
 
@@ -1384,6 +1411,8 @@ end
                          llvm2
                          \""")"""
         @test fmt(str) == str
+        # nests and then unnests
+        @test fmt(str, 2, 20) == str
 
         str_ = """
         foo() =
@@ -1391,6 +1420,35 @@ end
                    llvm1
                    llvm2
                    \""")"""
+        @test fmt(str, 2, 19) == str_
+
+        # the length calculation is kind of wonky here
+        # but it's still a worthwhile test
+        str_ = """
+        foo() =
+            llvmcall(\"""
+                     llvm1
+                     llvm2
+                     \""")"""
+        @test fmt(str, 4, 19) == str_
+        str_ = """
+        foo() = llvmcall(
+            \"""
+            llvm1
+            llvm2
+            \""",
+        )"""
+        @test fmt(str, 4, 18) == str_
+
+
+        str_ = """
+        foo() =
+          llvmcall(
+            \"""
+            llvm1
+            llvm2
+            \""",
+          )"""
         @test fmt(str, 2, 10) == str_
 
         str = """
@@ -1411,6 +1469,46 @@ end
 
         str = raw"""@test :(x`s`flag) == :(@x_cmd "s" "flag")"""
         @test fmt(str) == str
+
+        str = raw"""
+        if free < min_space
+            throw(ErrorException(\"""
+            Free space: \$free Gb
+            Please make sure to have at least \$min_space Gb of free disk space
+            before downloading the $database_name database.
+            \"""))
+        end"""
+        @test fmt(str) == str
+
+        # Technically it nests sonner than it should but that's due to the 
+        # closing parenthesis. ATM I don't think it's worth factoring that in.
+
+        str_ = raw"""
+        if free < min_space
+            throw(
+                ErrorException(\"""
+          Free space: \$free Gb
+          Please make sure to have at least \$min_space Gb of free disk space
+          before downloading the $database_name database.
+          \"""),
+            )
+        end"""
+        @test fmt(str, 4, 71) == str_
+
+        str_ = raw"""
+        if free < min_space
+            throw(
+                ErrorException(
+                    \"""
+        Free space: \$free Gb
+        Please make sure to have at least \$min_space Gb of free disk space
+        before downloading the $database_name database.
+        \""",
+                ),
+            )
+        end"""
+        @test fmt(str, 4, 69) == str_
+
     end
 
     @testset "comments" begin
@@ -1907,7 +2005,9 @@ end
                          e8
                      end
                  end"""
-        @test fmt("begin if cond1 e1; e2 elseif cond2 e3; e4 elseif cond3 e5;e6 else e7;e8  end end") == str
+        @test fmt(
+            "begin if cond1 e1; e2 elseif cond2 e3; e4 elseif cond3 e5;e6 else e7;e8  end end",
+        ) == str
 
         str = """if cond1
                      e1
@@ -2207,11 +2307,22 @@ end
         @test fmt("@somemacro function (fcall_ | fcall_) body_ end", 4, 37) == str
 
         str = """
-        @somemacro function (fcall_ |
-                             fcall_)
+        @somemacro function (
+            fcall_ | fcall_,
+        )
             body_
         end"""
         @test fmt("@somemacro function (fcall_ | fcall_) body_ end", 4, 36) == str
+        @test fmt("@somemacro function (fcall_ | fcall_) body_ end", 4, 20) == str
+
+        str = """
+        @somemacro function (
+            fcall_ |
+            fcall_,
+        )
+            body_
+        end"""
+        @test fmt("@somemacro function (fcall_ | fcall_) body_ end", 4, 19) == str
 
         str = "Val(x) = (@_pure_meta; Val{x}())"
         @test fmt("Val(x) = (@_pure_meta ; Val{x}())", 4, 80) == str
@@ -2249,25 +2360,45 @@ end
         end"""
         @test fmt(str, 4, 1) == str
 
-        # don't nest < 2 args
-
-        str = "A where {B}"
-        @test fmt(str, 4, 1) == str
+        str = """
+        A where {
+            B,
+        }"""
+        str_ = "A where {B}"
+        @test fmt(str_) == str_
+        @test fmt(str_, 4, 1) == str
 
-        str = "foo(arg1)"
-        @test fmt(str, 4, 1) == str
+        str = """
+        foo(
+          arg1,
+        )"""
+        str_ = "foo(arg1)"
+        @test fmt(str_) == str_
+        @test fmt(str, 2, 1) == str
 
-        str = "[arg1]"
+        str = """
+        [
+         arg1,
+        ]"""
+        str_ = "[arg1]"
+        @test fmt(str_) == str_
         @test fmt(str, 4, 1) == str
 
-        str = "{arg1}"
+        str = """
+        {
+         arg1,
+        }"""
+        str_ = "{arg1}"
+        @test fmt(str_) == str_
         @test fmt(str, 4, 1) == str
 
         str = """
         (
          arg1
         )"""
-        @test fmt("(arg1)", 4, 1) == str
+        str_ = "(arg1)"
+        @test fmt(str_) == str_
+        @test fmt(str_, 4, 1) == str
 
 
 
@@ -2286,15 +2417,20 @@ end
                :mesh_dim => Cint(3),)"""
         @test fmt(str_, 4, 80) == str
 
-        str = """this_is_a_long_variable_name = Dict{
+        str = """
+        this_is_a_long_variable_name = Dict{
              Symbol,
              Any,
         }(
              :numberofpointattributes => NAttributes,
              :numberofpointmtrs => NMTr,
              :numberofcorners => NSimplex,
-             :firstnumber => Cint(1),
-             :mesh_dim => Cint(3),
+             :firstnumber => Cint(
+                  1,
+             ),
+             :mesh_dim => Cint(
+                  3,
+             ),
         )"""
         @test fmt(str_, 5, 1) == str
 
@@ -2329,14 +2465,25 @@ end
         @test fmt("begin\n a && b || c && d\nend", 4, 1) == str
 
         str = """
+        func(
+            a,
+            \"""this
+            is another
+            multi-line
+            string.
+            Longest line
+            \""",
+            foo(b, c),
+        )"""
+
+        str_ = """
         func(a, \"""this
                 is another
                 multi-line
                 string.
                 Longest line
                 \""", foo(b, c))"""
-        @test fmt(str, 4, 100) == str
-
+        @test fmt(str_) == str
         str_ = """
         func(
             a,
@@ -2353,18 +2500,6 @@ end
         )"""
         @test fmt(str, 4, 1) == str_
 
-        str = """
-        func(
-            a,
-            \"""this
-            is another
-            multi-line
-            string.
-            Longest line
-            \""",
-            foo(b, c),
-        )"""
-        @test fmt(str, 4, 31) == str
 
 
         # Ref
@@ -2466,10 +2601,6 @@ end
         end"""
         @test fmt(str_) == str
 
-        # single function kwarg or param should not nest
-        @test fmt("f(;a=1)", 4, 1) == "f(; a = 1)"
-        @test fmt("f(a=1)", 4, 1) == "f(a = 1)"
-
         # any pairing of argument, kawrg, or param should nest
         str = """
         f(
@@ -2564,13 +2695,15 @@ end
         # adds surrounding {...} after `where`
         @test s.line_offset == length(str)
         s = run_nest(str, 1)
-        @test s.line_offset == 11
+        @test s.line_offset == 1
 
         str = "f(a, b, c) where {A<:S}"
         s = run_nest(str, 100)
         @test s.line_offset == length(str)
-        s = run_nest(str, 1)
+        s = run_nest(str, length(str) - 1)
         @test s.line_offset == 14
+        s = run_nest(str, 1)
+        @test s.line_offset == 1
 
         str = "f(a, b, c) where Union{A,B,Union{C,D,E}}"
         s = run_nest(str, 100)
@@ -2852,21 +2985,30 @@ end
         @test fmt(str, 4, 52) == str
 
         str_ = "transcode(::Type{THISISONESUPERLONGTYPE1234567}) where {T<:Union{Int32,UInt32}} = transcode(T, String(Vector(src)))"
+
         str = """
-        transcode(::Type{THISISONESUPERLONGTYPE1234567}) where {T<:Union{
-          Int32,
-          UInt32,
-        }} = transcode(T, String(Vector(src)))"""
+        transcode(
+          ::Type{THISISONESUPERLONGTYPE1234567},
+        ) where {T<:Union{Int32,UInt32}} = transcode(T, String(Vector(src)))"""
         @test fmt(str_, 2, 80) == str
-        @test fmt(str_, 2, 38) == str
+        @test fmt(str_, 2, 68) == str
 
         str = """
-        transcode(::Type{THISISONESUPERLONGTYPE1234567}) where {T<:Union{
-          Int32,
-          UInt32,
-        }} =
+        transcode(
+          ::Type{THISISONESUPERLONGTYPE1234567},
+        ) where {T<:Union{Int32,UInt32}} =
+          transcode(T, String(Vector(src)))"""
+        @test fmt(str_, 2, 67) == str
+        @test fmt(str_, 2, 40) == str
+
+        str = """
+        transcode(
+          ::Type{
+            THISISONESUPERLONGTYPE1234567,
+          },
+        ) where {T<:Union{Int32,UInt32}} =
           transcode(T, String(Vector(src)))"""
-        @test fmt(str_, 2, 37) == str
+        @test fmt(str_, 2, 39) == str
 
         str_ = "transcode(::Type{T}, src::AbstractVector{UInt8}) where {T<:Union{Int32,UInt32}} = transcode(T, String(Vector(src)))"
         str = """
@@ -2888,14 +3030,23 @@ end
         # issue 56
         str_ = "a_long_function_name(Array{Float64,2}[[1.0], [0.5 0.5], [0.5 0.5; 0.5 0.5], [0.5 0.5; 0.5 0.5]])"
         str = """
-        a_long_function_name(Array{Float64,2}[
-            [1.0],
-            [0.5 0.5],
-            [0.5 0.5; 0.5 0.5],
-            [0.5 0.5; 0.5 0.5],
-        ])"""
+        a_long_function_name(
+            Array{Float64,2}[[1.0], [0.5 0.5], [0.5 0.5; 0.5 0.5], [0.5 0.5; 0.5 0.5]],
+        )"""
         @test fmt(str, 4, length(str)) == str_
         @test fmt(str_) == str
+        @test fmt(str_, 4, 79) == str
+
+        str = """
+        a_long_function_name(
+            Array{Float64,2}[
+                [1.0],
+                [0.5 0.5],
+                [0.5 0.5; 0.5 0.5],
+                [0.5 0.5; 0.5 0.5],
+            ],
+        )"""
+        @test fmt(str_, 4, 78) == str
 
         # unary op
         str_ = "[1, 1]'"
@@ -3135,18 +3286,35 @@ end
 
         str = """
         begin
-            weights = Dict((file, i) => w for (file, subject) in subjects
-                for (i, w) in enumerate(weightfn.(eachrow(subject.events))))
+            weights = Dict(
+                (file, i) => w for (file, subject) in subjects
+                for (i, w) in enumerate(weightfn.(eachrow(subject.events)))
+            )
+        end"""
+        @test fmt(str_, 4, 90) == str
+
+        str = """
+        begin
+            weights = Dict(
+                (file, i) => w for (file, subject) in subjects
+                for (i, w) in enumerate(
+                    weightfn.(eachrow(subject.events)),
+                )
+            )
         end"""
-        @test_broken fmt(str_, 4, 90) == str
+        @test fmt(str_, 4, 60) == str
 
         str = """
         begin
-            weights = Dict((file, i) => w
+            weights = Dict(
+                (file, i) => w
                 for (file, subject) in subjects
-                for (i, w) in enumerate(weightfn.(eachrow(subject.events))))
+                for (i, w) in enumerate(
+                    weightfn.(eachrow(subject.events)),
+                )
+            )
         end"""
-        @test_broken fmt(str_, 4, 60) == str
+        @test fmt(str_, 4, 50) == str
     end
 
     @testset "Splitpath issue" begin
@@ -3165,7 +3333,8 @@ end
                very_very_very_very_very_very_very_very_very_very_very_very_long_function_name(
                    very_very_very_very_very_very_very_very_very_very_very_very_long_argument,
                    very_very_very_very_very_very_very_very_very_very_very_very_long_argument,
-               ) for x in xs
+               )
+               for x in xs
             ))),
             another_argument,
         )"""
@@ -3230,7 +3399,6 @@ some_function(
             end
         end"""
         @test fmt(str_, 4, 24) == str
-        @test fmt(str_, 4, 23) == str
 
         str = """
         begin
@@ -3243,7 +3411,7 @@ some_function(
             else
             end
         end"""
-        @test fmt(str_, 4, 21) == str
+        @test fmt(str_, 4, 23) == str
         @test fmt(str_, 4, 15) == str
 
         str = """