Code.require_file("../test_helper.exs", __DIR__) defmodule Code.Formatter.CallsTest do use ExUnit.Case, async: true import CodeFormatterHelpers @short_length [line_length: 10] @medium_length [line_length: 20] describe "next break fits" do test "does not apply to function calls" do bad = "foo(very_long_call(bar))" good = """ foo( very_long_call( bar ) ) """ assert_format bad, good, @short_length end test "does not apply to strings" do bad = "foo(\"very long string\")" good = """ foo( "very long string" ) """ assert_format bad, good, @short_length end test "for functions" do assert_same """ foo(fn x -> y end) """ assert_same """ foo(fn a1 -> :ok b2 -> :error end) """ assert_same """ foo(bar, fn a1 -> :ok b2 -> :error end) """ assert_same """ foo(fn x -> :really_long_atom end) """, @medium_length assert_same """ foo(bar, fn a1 -> :ok b2 -> :really_long_error end) """, @medium_length end test "for heredocs" do assert_same """ foo(~c''' bar ''') """ assert_same ~S''' foo(""" bar """) ''' assert_same """ foo(~S''' bar ''') """ assert_same """ foo(~S''' very long line does trigger another break ''') """, @short_length end test "for lists" do bad = "foo([1, 2, 3, 4])" good = """ foo([ 1, 2, 3, 4 ]) """ assert_format bad, good, @short_length end test "for {} calls" do bad = """ alias Foo.{ Bar, Baz } """ good = """ alias Foo.{ Bar, Baz } """ assert_format bad, good, @medium_length end test "for binaries only on eol" do bad = "foo(<<1, 2, 3, 4>>)" good = """ foo( <<1, 2, 3, 4>> ) """ assert_format bad, good, @short_length bad = """ foo(<< # foo 1, 2, 3, 4>>) """ good = """ foo(<< # foo 1, 2, 3, 4 >>) """ assert_format bad, good, @short_length end end describe "local calls" do test "without arguments" do assert_format "foo( )", "foo()" end test "without arguments doesn't split on line limit" do assert_same "very_long_function_name()", @short_length end test "removes outer parens except for unquote_splicing/1" do assert_format "(foo())", "foo()" assert_same "(unquote_splicing(123))" end test "with arguments" do assert_format "foo( :one ,:two,\n :three)", "foo(:one, :two, :three)" end test "with arguments splits on line limit" do bad = """ fun(x, y, z) """ good = """ fun( x, y, z ) """ assert_format bad, good, @short_length end test "with arguments on comma limit" do bad = """ import(foo(abc, cde), :next) """ good = """ import( foo(abc, cde), :next ) """ assert_format bad, good, @medium_length end test "with keyword lists" do assert_same "foo(foo: 1, bar: 2)" assert_same "foo(:hello, foo: 1, bar: 2)" bad = """ foo(:hello, foo: 1, bar: 2) """ good = """ foo( :hello, foo: 1, bar: 2 ) """ assert_format bad, good, @short_length bad = """ foo(:hello, foo: 1, bar: 2, baz: 3) """ assert_format bad, """ foo(:hello, foo: 1, bar: 2, baz: 3) """ end test "with lists maybe rewritten as keyword lists" do assert_format "foo([foo: 1, bar: 2])", "foo(foo: 1, bar: 2)" assert_format "foo(:arg, [foo: 1, bar: 2])", "foo(:arg, foo: 1, bar: 2)" assert_same "foo(:arg, [:elem, foo: 1, bar: 2])" end test "without parens" do assert_same "import :foo, :bar" assert_same "bar = if foo, do: bar, else: baz" assert_same """ for :one, :two, :three, fn -> :ok end """ assert_same """ for :one, fn -> :ok end """ end test "without parens on line limit" do bad = "import :long_atom, :other_arg" good = """ import :long_atom, :other_arg """ assert_format bad, good, @short_length end test "without parens on comma limit" do bad = """ import foo(abc, cde), :next """ good = """ import foo( abc, cde ), :next """ assert_format bad, good, @medium_length end test "without parens and with keyword lists preserves multiline" do assert_same """ defstruct foo: 1, bar: 2 """ assert_same """ config :app, foo: 1 """ assert_same """ config :app, foo: 1, bar: 2 """ assert_same """ config :app, :key, foo: 1, bar: 2 """ assert_same """ config :app, :key, foo: 1, bar: 2 """ bad = """ config :app, foo: 1, bar: 2 """ assert_format bad, """ config :app, foo: 1, bar: 2 """ end test "without parens and with keyword lists on comma limit" do bad = """ import foo(abc, cde), opts: :next """ good = """ import foo( abc, cde ), opts: :next """ assert_format bad, good, @medium_length end test "without parens and with keyword lists on line limit" do assert_same "import :atom, opts: [foo: :bar]" bad = "import :atom, opts: [foo: :bar]" good = """ import :atom, opts: [foo: :bar] """ assert_format bad, good, @medium_length bad = "import :atom, really_long_key: [foo: :bar]" good = """ import :atom, really_long_key: [ foo: :bar ] """ assert_format bad, good, @medium_length assert_same """ import :foo, one: two, three: four, five: [6, 7, 8, 9] """, @medium_length assert_same """ import :really_long_atom_but_no_breaks, one: two, three: four """, @medium_length bad = "with :really_long_atom1, :really_long_atom2, opts: [foo: :bar]" good = """ with :really_long_atom1, :really_long_atom2, opts: [ foo: :bar ] """ assert_format bad, good, @medium_length end test "without parens from option" do assert_format "foo bar", "foo(bar)" assert_same "foo bar", locals_without_parens: [foo: 1] assert_same "foo(bar)", locals_without_parens: [foo: 1] assert_same "foo bar", locals_without_parens: [foo: :*] assert_same "foo(bar)", locals_without_parens: [foo: :*] end test "without parens on unique argument" do assert_same "foo(for 1, 2, 3)" assert_same "foo(bar, for(1, 2, 3))" assert_same "assert for 1, 2, 3" assert_same "assert foo, for(1, 2, 3)" assert_same """ assert for 1, 2, 3 do :ok end """ assert_same """ assert foo, for(1, 2, 3) do :ok end """ assert_same """ assert for(1, 2, 3) do :ok end """ assert_same """ assert (for 1, 2, 3 do :ok end) """ end test "call on call" do assert_same "unquote(call)()" assert_same "unquote(call)(one, two)" assert_same """ unquote(call)() do :ok end """ assert_same """ unquote(call)(one, two) do :ok end """ end test "call on call on line limit" do bad = "foo(bar)(one, two, three)" good = """ foo(bar)( one, two, three ) """ assert_format bad, good, @short_length end test "with generators" do assert_same "foo(bar <- baz, is_bat(bar))" assert_same "for bar <- baz, is_bat(bar)" assert_same """ foo( bar <- baz, is_bat(bar), bat <- bar ) """ assert_same """ for bar <- baz, is_bat(bar), bat <- bar """ assert_same """ for bar <- baz, is_bat(bar), bat <- bar do :ok end """ assert_same """ for bar <- baz, is_bat(bar), bat <- bar, into: %{} """ end test "preserves user choice on parens even when it fits" do assert_same """ call( :hello, :foo, :bar ) """ assert_same """ call( :hello, :foo, :bar ) do 1 + 2 end """ # Doesn't preserve this because only the ending has a newline assert_format "call(foo, bar, baz\n)", "call(foo, bar, baz)" # Doesn't preserve because there are no args bad = """ call() do 1 + 2 end """ assert_format bad, """ call do 1 + 2 end """ # Doesn't preserve because we have a single argument with next break fits bad = """ call( %{ key: :value } ) """ assert_format bad, """ call(%{ key: :value }) """ end end describe "remote calls" do test "with no arguments" do assert_format "Foo . Bar . baz", "Foo.Bar.baz()" assert_format ":erlang.\nget_stacktrace", ":erlang.get_stacktrace()" assert_format "@foo.bar", "@foo.bar" assert_format "@foo.bar()", "@foo.bar()" assert_format "(@foo).bar()", "@foo.bar()" assert_format "__MODULE__.start_link", "__MODULE__.start_link()" assert_format "Foo.bar.baz.bong", "Foo.bar().baz.bong" assert_format "(1 + 2).foo", "(1 + 2).foo" assert_format "(1 + 2).foo()", "(1 + 2).foo()" end test "with arguments" do assert_format "Foo . Bar. baz(1, 2, 3)", "Foo.Bar.baz(1, 2, 3)" assert_format ":erlang.\nget(\n:some_key)", ":erlang.get(:some_key)" assert_format ":erlang.\nget(:some_key\n)", ":erlang.get(:some_key)" assert_same "@foo.bar(1, 2, 3)" assert_same "__MODULE__.start_link(1, 2, 3)" assert_same "foo.bar(1).baz(2, 3)" end test "inspects function names correctly" do assert_same ~S[MyModule."my function"(1, 2)] assert_same ~S[MyModule."Foo.Bar"(1, 2)] assert_same ~S[Kernel.+(1, 2)] assert_same ~S[:erlang.+(1, 2)] assert_same ~S[foo."bar baz"(1, 2)] end test "splits on arguments and dot on line limit" do bad = """ MyModule.Foo.bar(:one, :two, :three) """ good = """ MyModule.Foo.bar( :one, :two, :three ) """ assert_format bad, good, @medium_length bad = """ My_function.foo().bar(2, 3).baz(4, 5) """ good = """ My_function.foo().bar( 2, 3 ).baz(4, 5) """ assert_format bad, good, @medium_length end test "doesn't split on parens on empty arguments" do assert_same "Mod.func()", @short_length end test "with keyword lists" do assert_same "mod.foo(foo: 1, bar: 2)" assert_same "mod.foo(:hello, foo: 1, bar: 2)" assert_same """ mod.really_long_function_name( :hello, foo: 1, bar: 2 ) """, @short_length assert_same """ really_long_module_name.foo( :hello, foo: 1, bar: 2 ) """, @short_length end test "wraps left side in parens if it is an anonymous function" do assert_same "(fn -> :ok end).foo" end test "wraps left side in parens if it is a do-end block" do assert_same """ (if true do :ok end).foo """ end test "wraps left side in parens if it is a do-end block as an argument" do assert_same """ import (if true do :ok end).foo """ end test "call on call" do assert_same "foo.bar(call)()" assert_same "foo.bar(call)(one, two)" assert_same """ foo.bar(call)() do end """ assert_same """ foo.bar(call)(one, two) do :ok end """ end test "call on call on line limit" do bad = "a.b(foo)(one, two, three)" good = """ a.b(foo)( one, two, three ) """ assert_format bad, good, @short_length end test "on vars" do assert_same "foo.bar" assert_same "foo.bar()" end test "on vars before blocks" do assert_same """ if var.field do raise "oops" end """ end test "on vars before brackets" do assert_same """ exception.opts[:foo] """ end test "preserves user choice on parens even when it fits" do assert_same """ Remote.call( :hello, :foo, :bar ) """ # Doesn't preserve this because only the ending has a newline assert_format "Remote.call(foo, bar, baz\n)", "Remote.call(foo, bar, baz)" assert_same """ Remote.call( :hello, :foo, fn -> :bar end ) """ end end describe "anonymous function calls" do test "without arguments" do assert_format "foo . ()", "foo.()" assert_format "(foo.()).().()", "foo.().().()" assert_same "@foo.()" assert_same "(1 + 1).()" assert_same ":foo.()" end test "with arguments" do assert_format "foo . (1, 2 , 3 )", "foo.(1, 2, 3)" assert_format "foo . (1, 2 ).(3,4)", "foo.(1, 2).(3, 4)" assert_same "@foo.(:one, :two)" assert_same "foo.(1 + 1).(hello)" end test "does not split on dot on line limit" do assert_same "my_function.()", @short_length end test "splits on arguments on line limit" do bad = """ my_function.(1, 2, 3) """ good = """ my_function.( 1, 2, 3 ) """ assert_format bad, good, @short_length bad = """ my_function.(1, 2).f(3, 4).(5, 6) """ good = """ my_function.( 1, 2 ).f(3, 4).( 5, 6 ) """ assert_format bad, good, @short_length end test "with keyword lists" do assert_same "foo.(foo: 1, bar: 2)" assert_same "foo.(:hello, foo: 1, bar: 2)" assert_same """ foo.( :hello, foo: 1, bar: 2 ) """, @short_length end test "wraps left side in parens if it is an anonymous function" do assert_same "(fn -> :ok end).()" end test "wraps left side in parens if it is a do-end block" do assert_same """ (if true do :ok end).() """ end test "wraps left side in parens if it is a do-end block as an argument" do assert_same """ import (if true do :ok end).() """ end test "preserves user choice on parens even when it fits" do assert_same """ call.( :hello, :foo, :bar ) """ # Doesn't preserve this because only the ending has a newline assert_format "call.(foo, bar, baz\n)", "call.(foo, bar, baz)" end end describe "do-end blocks" do test "with non-block keywords" do assert_same "foo(do: nil)" end test "with forced block keywords" do good = """ foo do nil end """ assert_format "foo(do: nil)", good, force_do_end_blocks: true # Avoid false positives assert_same "foo(do: 1, do: 2)", force_do_end_blocks: true assert_same "foo(do: 1, another: 2)", force_do_end_blocks: true end test "with multiple keywords" do assert_same """ foo do :do rescue :rescue catch :catch else :else after :after end """ end test "with multiple keywords and arrows" do assert_same """ foo do a1 -> a2 b1 -> b2 rescue a1 -> a2 b1 -> b2 catch a1 -> a2 b1 -> b2 else a1 -> a2 b1 -> b2 after a1 -> a2 b1 -> b2 end """ end test "with no extra arguments" do assert_same """ foo do :ok end """ end test "with no extra arguments and line breaks" do assert_same """ foo do a1 -> really_long_line b1 -> b2 rescue c1 catch d1 -> d1 e1 -> e1 else f2 after g1 -> really_long_line h1 -> h2 end """, @medium_length end test "with extra arguments" do assert_same """ foo bar, baz do :ok end """ end test "with extra arguments and line breaks" do assert_same """ foo bar do a1 -> really_long_line b1 -> b2 rescue c1 catch d1 -> d1 e1 -> e1 else f2 after g1 -> really_long_line h1 -> h2 end """, @medium_length assert_same """ foo really, long, list, of, arguments do a1 -> really_long_line b1 -> b2 rescue c1 catch d1 -> d1 e1 -> e1 else f2 after g1 -> really_long_line h1 -> h2 end """, @medium_length end test "when empty" do assert_same """ foo do end """ assert_same """ foo do rescue catch else after end """ end test "inside call" do bad = "foo (bar do :ok end)" good = """ foo( bar do :ok end ) """ assert_format bad, good bad = "import (bar do :ok end)" good = """ import (bar do :ok end) """ assert_format bad, good end test "inside operator" do bad = "foo + bar do :ok end" good = """ foo + bar do :ok end """ assert_format bad, good end test "inside operator inside argument" do bad = "fun foo + (bar do :ok end)" good = """ fun( foo + bar do :ok end ) """ assert_format bad, good bad = "if foo + (bar do :ok end) do :ok end" good = """ if foo + (bar do :ok end) do :ok end """ assert_format bad, good end test "inside operator inside argument with remote call" do bad = "if foo + (Bar.baz do :ok end) do :ok end" good = """ if foo + (Bar.baz do :ok end) do :ok end """ assert_format bad, good end test "keeps repeated keys" do assert_same """ receive do :ok after 0 -> 1 after 2 -> 3 end """ end test "preserves user choice even when it fits" do assert_same """ case do 1 -> :ok 2 -> :ok end """ assert_same """ case do 1 -> :ok 2 -> :ok 3 -> :ok end """ end end describe "tuple calls" do test "without arguments" do assert_format "foo . {}", "foo.{}" end test "with arguments" do assert_format "foo.{bar,baz,bat,}", "foo.{bar, baz, bat}" end test "with arguments on line limit" do bad = "foo.{bar,baz,bat,}" good = """ foo.{ bar, baz, bat } """ assert_format bad, good, @short_length bad = "really_long_expression.{bar,baz,bat,}" good = """ really_long_expression.{ bar, baz, bat } """ assert_format bad, good, @short_length end test "with keywords" do assert_same "expr.{:hello, foo: bar, baz: bat}" end test "preserves user choice on parens even when it fits" do assert_same """ call.{ :hello, :foo, :bar } """ assert_format "call.{foo, bar, baz\n}", "call.{foo, bar, baz}" end end describe "access" do test "with one argument" do assert_format "foo[ bar ]", "foo[bar]" end test "with arguments on line limit" do bad = "foo[really_long_argument()]" good = """ foo[ really_long_argument() ] """ assert_format bad, good, @short_length bad = "really_long_expression[really_long_argument()]" good = """ really_long_expression[ really_long_argument() ] """ assert_format bad, good, @short_length end test "with do-end blocks" do assert_same """ (if true do false end)[key] """ end test "with keywords" do assert_format "expr[[]]", "expr[[]]" assert_format "expr[foo: bar, baz: bat]", "expr[foo: bar, baz: bat]" assert_format "expr[[foo: bar, baz: bat]]", "expr[[foo: bar, baz: bat]]" end end end