defmodule NLdoc.Spec.Content do @moduledoc """ This module provides helper functions for interpreting the content of NLdoc Spec objects. """ alias NLdoc.Spec.Text @doc """ Returns true if the given text is discernible, i.e. consists anything else than whitespace or invisible characters. If the given text is an `NLdoc.Spec` object, this function will recursively check whether any of its children contain discernible text. ## Examples iex> alias NLdoc.Spec.{Content, Paragraph, Text} iex> Content.discernible_text?( ...> %Paragraph{children: [ ...> %Text{text: "This is"}, ...> %Text{text: " "}, ...> %Text{text: "an example."}, ...> ]} ...> ) true iex> Content.discernible_text?("") false iex> Content.discernible_text?(%Paragraph{children: [%Text{text: " "}]}) false """ @spec discernible_text?(NLdoc.Spec.object() | String.t() | nil) :: boolean() def discernible_text?(text) when is_binary(text), # \s matches standard whitespace characters. # \b matches a backspace character. # \x{0085} matches Unicode character U+0085 (Next Line). # See the tests for this module for a complete list of all these Unicode characters and what they are. # See also https://en.wikipedia.org/wiki/Whitespace_character#Unicode do: String.replace( text, ~r/[\s\b\x{0085}\x{00A0}\x{1680}\x{180E}\x{2000}\x{2001}\x{2002}\x{2003}\x{2004}\x{2005}\x{2006}\x{2007}\x{2008}\x{2009}\x{200A}\x{200B}\x{200C}\x{200D}\x{2028}\x{2029}\x{202F}\x{205F}\x{2060}\x{3000}\x{FEFF}]/u, "" ) != "" def discernible_text?(%{text: text}), do: discernible_text?(text) def discernible_text?(%{children: children}), do: Enum.any?(children, &discernible_text?/1) def discernible_text?(_), do: false @doc """ Returns all the text elements in a series of NLdoc Spec objects, concatenated into a single series of texts. ## Examples iex> alias NLdoc.Spec.{Content, Paragraph, Text} iex> Content.text( ...> %Paragraph{children: [ ...> %Text{text: "This is"}, ...> %Text{text: " "}, ...> %Text{text: "an example."}, ...> ]} ...> ) [%Text{text: "This is"}, %Text{text: " "}, %Text{text: "an example."}] iex> Content.text([ ...> %Paragraph{children: [ ...> %Text{text: "This is"}, ...> %Text{text: " "}, ...> %Text{text: "an example."}, ...> ]}, ...> %Paragraph{children: [ ...> %Text{text: "And this is"}, ...> %Text{text: " "}, ...> %Text{text: "another example."}, ...> ]} ...> ]) [%Text{text: "This is"}, %Text{text: " "}, %Text{text: "an example."}, %Text{text: "And this is"}, %Text{text: " "}, %Text{text: "another example."}] iex> Content.text(%Text{text: "Just a single text object."}) [%Text{text: "Just a single text object."}] """ @spec text(NLdoc.Spec.object() | [NLdoc.Spec.object()]) :: [Text.t()] def text(text = %Text{}) do [text] end def text(%{children: children}) do text(children) end def text(resources) when is_list(resources) do resources |> Enum.flat_map(&text/1) end end