defmodule PhoenixHTMLHelpers.Format do @moduledoc """ Formatting functions. """ @doc ~S""" Returns text transformed into HTML using simple formatting rules. Two or more consecutive newlines `\n\n` or `\r\n\r\n` are considered as a paragraph and text between them is wrapped in `

` tags. One newline `\n` or `\r\n` is considered as a linebreak and a `
` tag is inserted. ## Examples iex> text_to_html("Hello\n\nWorld") |> safe_to_string "

Hello

\n

World

\n" iex> text_to_html("Hello\nWorld") |> safe_to_string "

Hello
\nWorld

\n" iex> opts = [wrapper_tag: :div, attributes: [class: "p"]] ...> text_to_html("Hello\n\nWorld", opts) |> safe_to_string "
Hello
\n
World
\n" ## Options * `:escape` - if `false` does not html escape input (default: `true`) * `:wrapper_tag` - tag to wrap each paragraph (default: `:p`) * `:attributes` - html attributes of the wrapper tag (default: `[]`) * `:insert_brs` - if `true` insert `
` for single line breaks (default: `true`) """ @spec text_to_html(Phoenix.HTML.unsafe(), Keyword.t()) :: Phoenix.HTML.safe() def text_to_html(string, opts \\ []) do escape? = Keyword.get(opts, :escape, true) wrapper_tag = Keyword.get(opts, :wrapper_tag, :p) attributes = Keyword.get(opts, :attributes, []) insert_brs? = Keyword.get(opts, :insert_brs, true) string |> maybe_html_escape(escape?) |> String.split(["\n\n", "\r\n\r\n"], trim: true) |> Enum.filter(¬_blank?/1) |> Enum.map(&wrap_paragraph(&1, wrapper_tag, attributes, insert_brs?)) |> Phoenix.HTML.html_escape() end defp maybe_html_escape(string, true), do: string |> Phoenix.HTML.Engine.html_escape() |> IO.iodata_to_binary() defp maybe_html_escape(string, false), do: string defp not_blank?("\r\n" <> rest), do: not_blank?(rest) defp not_blank?("\n" <> rest), do: not_blank?(rest) defp not_blank?(" " <> rest), do: not_blank?(rest) defp not_blank?(""), do: false defp not_blank?(_), do: true defp wrap_paragraph(text, tag, attributes, insert_brs?) do [PhoenixHTMLHelpers.Tag.content_tag(tag, insert_brs(text, insert_brs?), attributes), ?\n] end defp insert_brs(text, false) do text |> split_lines() |> Enum.intersperse(?\s) |> Phoenix.HTML.raw() end defp insert_brs(text, true) do text |> split_lines() |> Enum.map(&Phoenix.HTML.raw/1) |> Enum.intersperse([PhoenixHTMLHelpers.Tag.tag(:br), ?\n]) end defp split_lines(text) do String.split(text, ["\n", "\r\n"], trim: true) end end