Textwrap (Textwrap v0.4.0)

View Source

Textwrap provides a set of functions for wrapping, indenting, and dedenting text. It wraps the Rust textwrap crate.

Wrapping

Use wrap/2 to turn a String into a list of Strings each no more than width characters long.

iex> Textwrap.wrap("foo bar baz", 3)
["foo", "bar", "baz"]

iex> Textwrap.wrap("foo bar baz", 7)
["foo bar", "baz"]

fill/2 is like wrap/2, except that it retuns the wrapped text as a single String.

iex> Textwrap.fill("foo bar baz", 3)
"foo\nbar\nbaz"

iex> Textwrap.fill("foo bar baz", 7)
"foo bar\nbaz"

Both wrap/2 and fill/2 can either take the width to wrap too as their second argument, or take a keyword list including a :width key and any number of other options. See the docs for wrap/2 for details.

Displayed Width vs Byte Width

Textwrap wraps text based on measured display width, not simply counting bytes. For ascii text this gives exactly the same result, but many non-ASCII characters take up more than one byte in the UTF-8 encoding.

See the documentation of the textwrap crate for more details.

Terminal Width

The width passed to wrap/2 or fill/2 can either by a positive integer, or the atom :termwidth. When standard output is connected to a terminal, passing :termwidth will wrap the text to the width of the terminal. Otherwise, it will use a width of 80 characters as a fallback.

Indenting and Dedenting

Use indent/2 and dedent/1 to indent and dedent text:

iex> Textwrap.indent("hello\nworld\n", "  ")
"  hello\n  world\n"

iex> Textwrap.dedent("  hello\n  world\n")
"hello\nworld\n"

Summary

Functions

Removes as much common leading whitespace as possible from each line.

Fills text to the given width.

Adds a given prefix to each non-empty line.

Wraps text to the given width.

Types

penalties()

@type penalties() :: %{
  optional(:nline_penalty) => non_neg_integer(),
  optional(:overflow_penalty) => non_neg_integer(),
  optional(:short_last_line_fraction) => non_neg_integer(),
  optional(:short_last_line_penalty) => non_neg_integer(),
  optional(:hyphen_penalty) => non_neg_integer()
}

wrap_algorithm()

@type wrap_algorithm() :: :first_fit | :optimal_fit | {:optimal_fit, penalties()}

wrap_opt()

@type wrap_opt() ::
  {:width, pos_integer() | :termwidth}
  | {:break_words, boolean()}
  | {:initial_indent, String.t()}
  | {:word_splitter, nil | :en_us | false}
  | {:subsequent_indent, String.t()}
  | {:wrap_algorithm, wrap_algorithm()}
  | {:splitter, nil | :en_us | false}

wrap_opts()

@type wrap_opts() :: pos_integer() | :termwidth | [wrap_opt()]

Functions

dedent(text)

@spec dedent(text :: String.t()) :: String.t()

Removes as much common leading whitespace as possible from each line.

Each non-empty line has an equal amount of whitespace removed from its start.

Empty lines (containing only whitespace) are normalized to a single \n, with no other whitespace on the line.

Examples:

iex> Textwrap.dedent("    hello world")
"hello world"

iex> Textwrap.dedent("
...>  foo
...>    bar
...>  baz
...>      ")
"\nfoo\n  bar\nbaz\n"

fill(text, width_or_opts)

@spec fill(text :: String.t(), opts :: wrap_opts()) :: String.t()

Fills text to the given width.

The result is a String, with lines seperated by newline. The wrap/2 function does the same thing, except that it returns a list of Strings, one for each line.

See the docs for wrap/2 for details about the options it takes.

Examples

iex> Textwrap.fill("hello world", 5)
"hello\nworld"

iex> Textwrap.fill("hello world", width: 5)
"hello\nworld"

iex> Textwrap.fill("Antidisestablishmentarianism", width: 10)
"Antidisest\nablishment\narianism"

iex> Textwrap.fill("Antidisestablishmentarianism", width: 10, break_words: false)
"Antidisestablishmentarianism"

iex> Textwrap.fill("Antidisestablishmentarianism", width: 10, word_splitter: :en_us)
"Antidis-\nestablish-\nmentarian-\nism"

iex> Textwrap.fill("foo bar baz",
...>      width: 5,
...>      initial_indent: "> ",
...>      subsequent_indent: "  ")
"> foo\n  bar\n  baz"

iex> Textwrap.fill("Lorem ipsum dolor sit amet, consectetur adipisicing elit",
...>      width: 25,
...>      wrap_algorithm: :optimal_fit)
"Lorem ipsum dolor\nsit amet, consectetur\nadipisicing elit"

iex> Textwrap.fill("Lorem ipsum dolor sit amet, consectetur adipisicing elit",
...>      width: 25,
...>      wrap_algorithm: :first_fit)
"Lorem ipsum dolor sit\namet, consectetur\nadipisicing elit"

indent(text, prefix)

@spec indent(text :: String.t(), prefix :: String.t()) :: String.t()

Adds a given prefix to each non-empty line.

Empty lines (containing only whitespace) are normalized to a single \n, and not indented.

Any leading and trailing whitespace on non-empty lines is left unchanged.

Examples:

iex> Textwrap.indent("hello world", ">")
">hello world"

iex> Textwrap.indent("foo\nbar\nbaz\n", "  ")
"  foo\n  bar\n  baz\n"

wrap(text, width_or_opts)

@spec wrap(text :: String.t(), opts :: wrap_opts()) :: [String.t()]

Wraps text to the given width.

wrap/2 returns a list of Strings, each of no more than width charecters.

Options can be either passed as a keyword list (which must include the key :width), or, if using no options other than :width, the width can be passed on its own as the second argument.

width can either by a positive integer or the atom :termwidth. See the module docs on :termwidth for more details.

Options

  • :width — the width to wrap at, a positive integer.
  • :break_words — allow long words to be broken, if they won't fit on a single line. Setting this to false may cause some lines to be longer than :width.
  • :inital_indent — will be added as a prefix to the first line of the result.
  • :subsequent_indent — will be added as a prefix to each line other than the first line of the result.
  • :word_splitter — when set to false, hyphens within words won't be treated specially as a place to split words. When set to :en_us, a language-aware hyphenation system will be used to try to break words in appropriate places.
  • :wrap_algorithm — by default, or when set to :optimal_fit, wrap/2 will do its best to balance the gaps left at the ends of lines. When set to :first_fit, a simpler greedy algorithm is used instead. You can also pass {:optimal_fit, penalties} where penalties is a map containing custom penalty values to override the defaults (which are suitable for monospace fonts). This allows you to tune the algorithm for proportional fonts. See the Penalties documentation for details on the available penalty fields. For more on the algorithms, see the textwrap crate docs.

Examples

iex> Textwrap.wrap("hello world", 5)
["hello", "world"]

iex> Textwrap.wrap("hello world", width: 5)
["hello", "world"]

iex> Textwrap.wrap("Antidisestablishmentarianism", width: 10)
["Antidisest", "ablishment", "arianism"]

iex> Textwrap.wrap("Antidisestablishmentarianism", width: 10, break_words: false)
["Antidisestablishmentarianism"]

iex> Textwrap.wrap("Antidisestablishmentarianism", width: 10, word_splitter: :en_us)
["Antidis-", "establish-", "mentarian-", "ism"]

iex> Textwrap.wrap("foo bar baz",
...>      width: 5,
...>      initial_indent: "> ",
...>      subsequent_indent: "  ")
["> foo", "  bar", "  baz"]

iex> Textwrap.wrap("Lorem ipsum dolor sit amet, consectetur adipisicing elit",
...>      width: 25,
...>      wrap_algorithm: :optimal_fit)
["Lorem ipsum dolor", "sit amet, consectetur", "adipisicing elit"]

iex> Textwrap.wrap("Lorem ipsum dolor sit amet, consectetur adipisicing elit",
...>      width: 25,
...>      wrap_algorithm: :first_fit)
["Lorem ipsum dolor sit", "amet, consectetur", "adipisicing elit"]