diff --git a/lib/elixir/lib/code.ex b/lib/elixir/lib/code.ex index a2bc11b79a..48e9c3fa81 100644 --- a/lib/elixir/lib/code.ex +++ b/lib/elixir/lib/code.ex @@ -1333,7 +1333,7 @@ defmodule Code do defp previous_eol_count(_), do: 0 @doc ~S""" - Converts a quoted expression to an algebra document. + Converts a quoted expression to an algebra document using Elixir's formatter rules. The algebra document can be converted into a string by calling: @@ -1378,6 +1378,12 @@ defmodule Code do `\\n`. If the `:unescape` option was set to `false` when using `string_to_quoted/2`, setting this option to `false` will prevent it from escaping the sequences twice. Defaults to `true`. + + * `:locals_without_parens` - a keyword list of name and arity + pairs that should be kept without parens whenever possible. + The arity may be the atom `:*`, which implies all arities of + that name. The formatter already includes a list of functions + and this option augments this list. """ @doc since: "1.13.0" @spec quoted_to_algebra(Macro.t(), keyword) :: Inspect.Algebra.t() diff --git a/lib/elixir/lib/code/formatter.ex b/lib/elixir/lib/code/formatter.ex index 3962299e92..6e49c7aff3 100644 --- a/lib/elixir/lib/code/formatter.ex +++ b/lib/elixir/lib/code/formatter.ex @@ -207,10 +207,17 @@ defmodule Code.Formatter do doc end + @doc """ + Lists all default locals without parens. + """ + def locals_without_parens do + @locals_without_parens + end + @doc """ Checks if a function is a local without parens. """ - def local_without_parens?(fun, arity, locals_without_parens \\ @locals_without_parens) do + def local_without_parens?(fun, arity, locals_without_parens) do arity > 0 and Enum.any?(locals_without_parens, fn {key, val} -> key == fun and (val == :* or val == arity) @@ -219,13 +226,11 @@ defmodule Code.Formatter do defp state(comments, opts) do force_do_end_blocks = Keyword.get(opts, :force_do_end_blocks, false) - - locals_without_parens = - Keyword.get(opts, :locals_without_parens, []) ++ @locals_without_parens + locals_without_parens = Keyword.get(opts, :locals_without_parens, []) %{ force_do_end_blocks: force_do_end_blocks, - locals_without_parens: locals_without_parens, + locals_without_parens: locals_without_parens ++ locals_without_parens(), operand_nesting: 2, skip_eol: false, comments: comments diff --git a/lib/elixir/lib/code/normalizer.ex b/lib/elixir/lib/code/normalizer.ex index 821e61c42f..27ed95fbc3 100644 --- a/lib/elixir/lib/code/normalizer.ex +++ b/lib/elixir/lib/code/normalizer.ex @@ -14,10 +14,12 @@ defmodule Code.Normalizer do def normalize(quoted, opts \\ []) do line = Keyword.get(opts, :line, nil) escape = Keyword.get(opts, :escape, true) + locals_without_parens = Keyword.get(opts, :locals_without_parens, []) state = %{ escape: escape, - parent_meta: [line: line] + parent_meta: [line: line], + locals_without_parens: locals_without_parens ++ Code.Formatter.locals_without_parens() } do_normalize(quoted, state) @@ -283,7 +285,7 @@ defmodule Code.Normalizer do meta = if is_nil(meta[:no_parens]) and is_nil(meta[:closing]) and - not Code.Formatter.local_without_parens?(form, arity) do + not Code.Formatter.local_without_parens?(form, arity, state.locals_without_parens) do [closing: [line: meta[:line]]] ++ meta else meta diff --git a/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs b/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs index 9e5f9476f9..4412ba6133 100644 --- a/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs +++ b/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs @@ -12,6 +12,9 @@ defmodule Code.Normalizer.QuotedASTTest do assert quoted_to_string(quote(do: foo(1, 2, 3))) == "foo(1, 2, 3)" assert quoted_to_string(quote(do: foo([1, 2, 3]))) == "foo([1, 2, 3])" + assert quoted_to_string(quote(do: foo(1, 2, 3)), locals_without_parens: [foo: 3]) == + "foo 1, 2, 3" + # Mixing literals and non-literals assert quoted_to_string(quote(do: foo(a, 2))) == "foo(a, 2)" assert quoted_to_string(quote(do: foo(1, b))) == "foo(1, b)"