diff --git a/lib/elixir/lib/io/ansi/docs.ex b/lib/elixir/lib/io/ansi/docs.ex index e0e72a1f48d..d0be03e3805 100644 --- a/lib/elixir/lib/io/ansi/docs.ex +++ b/lib/elixir/lib/io/ansi/docs.ex @@ -50,6 +50,10 @@ defmodule IO.ANSI.Docs do """ @spec print_headings([String.t()], keyword) :: :ok def print_headings(headings, options \\ []) do + # It's possible for some of the headings to contain newline characters (`\n`), so in order to prevent it from + # breaking the output from `print_headings/2`, as `print_headings/2` tries to pad the whole heading, we first split + # any heading containgin newline characters into multiple headings, that way each one is padded on its own. + headings = Enum.flat_map(headings, fn heading -> String.split(heading, "\n") end) options = Keyword.merge(default_options(), options) newline_after_block(options) width = options[:width] diff --git a/lib/elixir/test/elixir/io/ansi/docs_test.exs b/lib/elixir/test/elixir/io/ansi/docs_test.exs index cce77a81fd0..9a6ac082d39 100644 --- a/lib/elixir/test/elixir/io/ansi/docs_test.exs +++ b/lib/elixir/test/elixir/io/ansi/docs_test.exs @@ -33,6 +33,14 @@ defmodule IO.ANSI.DocsTest do assert String.contains?(result, " foo ") assert String.contains?(result, " bar ") end + + test "is correctly formatted when newline character is present" do + result = format_headings(["foo\nbar"]) + assert :binary.matches(result, "\e[0m\n\e[7m\e[33m") |> length == 2 + assert ["\e[0m", foo_line, bar_line, "\e[0m"] = String.split(result, "\n") + assert Regex.match?(~r/\e\[7m\e\[33m +foo +\e\[0m/, foo_line) + assert Regex.match?(~r/\e\[7m\e\[33m +bar +\e\[0m/, bar_line) + end end describe "metadata" do