From 01c220360c539924dc9eadadba15b7f639bbde6e Mon Sep 17 00:00:00 2001 From: Michael Neumann Date: Sun, 9 Mar 2025 09:49:23 +0100 Subject: [PATCH 1/3] Remove surplus newlines (fixes #59) Do a `flat_map_reduce` over the list of children and filter out any child that is either `[]` or `nil`. The `count` is required to properly detect the first non-blank item, which shouldn't be prefixed with a new-line. As the code is used in both `format` and `format_content`, factor it out into `format_children`. Fixes: #59 --- lib/xml_builder.ex | 26 +++++++++++++++++++++++--- test/xml_builder_test.exs | 6 ++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/xml_builder.ex b/lib/xml_builder.ex index d6872eb..77365a9 100644 --- a/lib/xml_builder.ex +++ b/lib/xml_builder.ex @@ -257,8 +257,7 @@ defmodule XmlBuilder do do: format({nil, nil, string}, level, options) defp format(list, level, options) when is_list(list) do - formatter = formatter(options) - map_intersperse(list, formatter.line_break(), &format(&1, level, options)) + format_children(list, level, options) end defp format({nil, nil, name}, level, options) when is_bitstring(name), @@ -339,6 +338,27 @@ defmodule XmlBuilder do ] end + defp format_children(list, level, options) when is_list(list) do + format_char = formatter(options).line_break() + + {result, _} = + Enum.flat_map_reduce(list, 0, fn + [], count -> + {[], count} + + nil, count -> + {[], count} + + elm, 0 -> + {[format(elm, level, options)], 1} + + elm, count -> + {[format_char, format(elm, level, options)], count + 1} + end) + + result + end + defp elements_with_prolog([first | rest]) when length(rest) > 0, do: [first_element(first) | element(rest)] @@ -360,7 +380,7 @@ defmodule XmlBuilder do defp format_content(children, level, options) when is_list(children) do format_char = formatter(options).line_break() - [format_char, map_intersperse(children, format_char, &format(&1, level, options))] + [format_char, format_children(children, level, options)] end defp format_content(content, _level, _options), diff --git a/test/xml_builder_test.exs b/test/xml_builder_test.exs index 955b2a6..3cb7084 100644 --- a/test/xml_builder_test.exs +++ b/test/xml_builder_test.exs @@ -320,6 +320,12 @@ defmodule XmlBuilderTest do assert warning =~ "doc/1 is deprecated. Use document/1 with generate/1 instead." end + test "removal of empty child elements" do + assert {:person, nil, [nil, {:first, nil, "Josh"}, [], {:last, nil, "Nussbaum"}]} + |> XmlBuilder.generate() == + ~s|\n Josh\n Nussbaum\n| + end + def element(name, arg), do: XmlBuilder.element(name, arg) |> XmlBuilder.generate() From 9b5ee43c2d93b38aafcfc1bc75d6d27da3293244 Mon Sep 17 00:00:00 2001 From: Michael Neumann Date: Sun, 9 Mar 2025 09:57:40 +0100 Subject: [PATCH 2/3] Slightly optimize "intersperse" --- lib/xml_builder.ex | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/xml_builder.ex b/lib/xml_builder.ex index 77365a9..4afe2d9 100644 --- a/lib/xml_builder.ex +++ b/lib/xml_builder.ex @@ -343,17 +343,15 @@ defmodule XmlBuilder do {result, _} = Enum.flat_map_reduce(list, 0, fn - [], count -> + elm, count when is_blank_list(elm) -> {[], count} - nil, count -> - {[], count} - - elm, 0 -> - {[format(elm, level, options)], 1} - elm, count -> - {[format_char, format(elm, level, options)], count + 1} + if format_char == "" or count == 0 do + {[format(elm, level, options)], count + 1} + else + {[format_char, format(elm, level, options)], count + 1} + end end) result From d8f785ccf0b31490258350c432f73d6a2706e2fa Mon Sep 17 00:00:00 2001 From: Michael Neumann Date: Wed, 26 Mar 2025 23:41:16 +0100 Subject: [PATCH 3/3] Fix readability issues --- lib/xml_builder.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/xml_builder.ex b/lib/xml_builder.ex index 4afe2d9..49e413c 100644 --- a/lib/xml_builder.ex +++ b/lib/xml_builder.ex @@ -339,18 +339,18 @@ defmodule XmlBuilder do end defp format_children(list, level, options) when is_list(list) do - format_char = formatter(options).line_break() + line_break = formatter(options).line_break() {result, _} = Enum.flat_map_reduce(list, 0, fn - elm, count when is_blank_list(elm) -> + element, count when is_blank_list(element) -> {[], count} - elm, count -> - if format_char == "" or count == 0 do - {[format(elm, level, options)], count + 1} + element, count -> + if line_break == "" or count == 0 do + {[format(element, level, options)], count + 1} else - {[format_char, format(elm, level, options)], count + 1} + {[line_break, format(element, level, options)], count + 1} end end)