Prompt (prompt v0.12.0)

View Source

Prompt provides a complete solution for building interactive command line applications.

It's very flexible and can be used just to provide helpers for taking input from the user and displaying output.

Basic Usage

import Prompt includes utilities for:

Advanced usage

See Prompt.Router

Examples

See Prompt.IO.Examples

Building for Distribution

There are a couple of different options for building a binary ready for distributing. Which ever approach you decide to use, you'll probably want to keep the docs instead of stripping them. For escripts, you'll add the following to the escript key in mix.exs, if using Bakeware, you'll add it to the releases key.

:strip_beams: [keep: ["Docs"]]

Escript

An escript is the most straightforward approach, but requires that erlang is already installed on the system.

Bakeware

This has been my preferred approach recently. Bakeware uses releases to build a single executable binary that can be run on the system without the dependency on erlang or elixir.

For Bakeware, I also set export RELEASE_DISTRIBUTION=none in rel/env.sh.eex and rel/env.bat.eex - unless you need erlang distribution in your CLI.

For a complete example see Genex

Run MIX_ENV=prod mix release to build the binary. This will output the usual release messages for a mix release command, but in the case of a CLI, it's a bit of a red herring. The binary you want to run as a CLI will be in _build/prod/rel/bakeware/<your_app>.

Burrito

Burrito is another option for building a single binary artifact. It is unique because you can build binaries for windows, mac and linux all from one machine.

For a complete example see Genex

Run MIX_ENV=prod mix release to build the binary. This will output the usual release messages for a mix release command, but in the case of a CLI, it's a bit of a red herring. The binary you want to run as a CLI will be in burrito_out/<your_app>.

Summary

Input Functions

Display a choice prompt with custom answers.

Display a Y/n prompt.

Can someone type an answer on device?

Prompt the user for input, but conceal the users typing.

Displays options to the user to choose from - single and multi select is supported.

Display text on the screen and wait for the users text imput.

Output Functions

Writes text to the screen.

Print an ASCII table of data. Requires a list of lists as input.

Types

The list of strings coming from the commmand-line arguments

A keyword list of commands and implementations of Prompt.Command

Functions

Does the current Erlang runtime version support raw terminal mode?

Use this to get an iolist back of the table. Useful when you want an ascii table/1 for other mediums like markdown files.

Input Functions

choice(question, custom, opts \\ [])

@spec choice(String.t(), keyword(), keyword()) :: :error | atom()

Display a choice prompt with custom answers.

Takes a keyword list of answers in the form of atom to return and string to display.

[yes: "y", no: "n"]

will show "(y/n)" and return :yes or :no based on the choice.

Supported options:

  • :color - The text color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow].

  • :background_color - The background color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow].

  • :default_answer (atom/0) - The default answer for the choices. Defaults to the first choice.

Examples

iex> Prompt.choice("Save password?",
...>   [yes: "y", no: "n", regenerate: "r"],
...>   default_answer: :regenerate
...> )
"Save Password? (y/n/R):" [enter]
iex> :regenerate

confirm(question, opts \\ [])

@spec confirm(
  String.t(),
  keyword()
) :: :yes | :no | :error

Display a Y/n prompt.

Sets 'Y' as the the default answer, allowing the user to just press the enter key. To make 'n' the default answer pass the option default_answer: :no

Supported options:

  • :color - The text color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow].

  • :background_color - The background color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow].

  • :default_answer - The default answer to the confirmation. The default value is :yes.

  • :mask_line (boolean/0) - If set to true, this will mask the current line by replacing it with #####. Useful when showing passwords in the terminal. The default value is false.

Examples

iex> Prompt.confirm("Send the email?")
"Send the email? (Y/n):" Y
iex> :yes

iex> Prompt.confirm("Send the email?", default_answer: :no)
"Send the email? (y/N):" [enter]
iex> :no

interactive?(device \\ :standard_io)

@spec interactive?(IO.device()) :: boolean()

Can someone type an answer on device?

Returns true when both the input and the output of device are terminals: the answer is read from stdin and the question is written to stdout. When input is piped, output is redirected (app > log.txt, where the question would land in the file unseen), or the program runs without a terminal, e.g. from cron, nobody can answer, so fall back to a default:

timeout = if Prompt.interactive?(), do: Prompt.text("Timeout"), else: "30"

This is the same rule OTP applies to raw mode, so it is also when select/3 uses the arrow-key picker.

Defaults to standard I/O. It checks the :stdin and :stdout options that standard I/O reports, or :terminal on a device without them. interactive?(:standard_error) tells whether stderr is a terminal. Returns false if the device cannot be queried.

password(display, opts \\ [])

@spec password(
  String.t(),
  keyword()
) :: String.t() | :error

Prompt the user for input, but conceal the users typing.

Supported options:

  • :color - The text color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :background_color - The background color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

Examples

iex> Prompt.password("Enter your passsword")
"Enter your password:"
iex> "super_secret_passphrase"

select(display, choices, opts \\ [])

@spec select(String.t(), [String.t()] | [{String.t(), any()}], keyword()) ::
  binary() | any() | list() | {:other, String.t()} | :error

Displays options to the user to choose from - single and multi select is supported.

Allows for a list of 2 tuples where the first value is what is displayed and the second value is what is returned to the caller.

Supported options:

  • :color - The text color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :background_color - The background color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :multi (boolean/0) - Allows multiple selections from the options presented. The default value is false.

  • :select_keys (a list of keyboard keys or escape sequences) - Erlang >= 28 only - determines which keyboard keys are used to mark a selection with multi: true. Defaults to ["\t", " "] (tab and space), or ["\t"] with filter: true, where space is typed into the filter.

  • :filter (boolean/0) - Arrow-key picker only - typing narrows the choices to those whose displayed text contains what was typed, ignoring case. Backspace removes a character and escape clears the filter. The arrow keys still move, but j and k are typed instead of moving. The allow_other choice always stays visible. Ignored by the numbered prompt. The default value is false.

  • :suggested (list of term/0) - Choices to list first, above a divider, in the given order; the rest follow in their usual order. Name each like default: a plain string choice, or the value of a {display, value} choice. A value that is not one of the choices makes select/3 write an error to stderr and return :error.

  • :select_indicator (String.t/0) - The default value is ">".

  • :default (term/0) - The choice that starts selected: a plain string choice, or the value of a {display, value} choice. Pressing enter right away returns it (in the numbered prompt, an empty line does). A default that is not one of the choices makes select/3 write an error to stderr and return :error. Not supported with multi: true.

  • :allow_other (String.t/0) - A label added as the last choice. Choosing it asks for free text and returns {:other, text}. Cannot be combined with multi: true.

  • :other_prompt (String.t/0) - The question asked when the allow_other choice is picked. The default value is "Enter a value".

Examples

iex> Prompt.select("Choose One", ["Choice A", "Choice B"])
"Choose One"

" > Choice A"
"   Choice B"

iex> Prompt.select("Choose One", [{"Choice A", 1000}, {"Choice B", 1001}])
"Choose One"

" > Choice A"
"  Choice B"

iex> Prompt.select("Choose as many as you want", ["Choice A", "Choice B"], multi: true)
"Choose as many as you want (<tab> marks selection, <enter> submits choices)

" > Choice A"
" > Choice B"

The numbered prompt

The arrow-key picker needs Erlang 28 or newer, for its raw input mode, and both standard input and output must be terminals. Otherwise, for example on Erlang 27, in IEx, when input is piped, when output is redirected to a file, or in ExUnit.CaptureIO, the select falls back to asking the user to type the number of the choice and press <enter>. With multi: true they type the numbers separated by spaces. It looks like:

  Prompt.select("Choose One", ["Choice A", "Choice B"])
    [1] Choice A
    [2] Choice B
  Choose One [1-2]:

  iex> Prompt.select("Choose One", [{"Choice A", 1000}, {"Choice B", 1001}])
    [1] Choice A
    [2] Choice B
  Choose One [1-2]:

  iex> Prompt.select("Choose as many as you want", ["Choice A", "Choice B"], multi: true)
    [1] Choice A
    [2] Choice B
  Choose as many as you want [1-2]:

text(display, opts \\ [])

@spec text(
  String.t(),
  keyword()
) :: String.t() | :error | :error_min | :error_max

Display text on the screen and wait for the users text imput.

Supported options:

  • :color - The text color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :background_color - The background color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :min (integer/0) - The minimum charactors required for input The default value is 0.

  • :max (integer/0) - The maximum charactors required for input The default value is 0.

Examples

iex> Prompt.text("Enter your email")
"Enter your email:" t@t.com
iex> t@t.com

Output Functions

display(text, opts \\ [])

@spec display(
  String.t() | [String.t()],
  keyword()
) :: :ok

Writes text to the screen.

Takes a single string argument or a list of strings where each item in the list will be displayed on a new line.

Supported options:

  • :color - The text color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :background_color - The background color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :device - The IO device to write to, one of :stdio or :stderr. The default value is :stdio.

  • :position - Print the content starting from the leftmost position or the rightmost position The default value is :left.

  • :mask_line (boolean/0) - If set to true, this will mask the current line by replacing it with #####. Useful when showing passwords in the terminal. The default value is false.

  • :alt_buffer (boolean/0) - Should the display on an alternative buffer? The default value is false.

Examples

iex> Prompt.display("Hello from the terminal!")
"Hello from the terminal!"

iex> Prompt.display(["Hello", "from", "the", "terminal"])
"Hellofromtheterminal"

iex> Prompt.display(["Hello", " from", " the", " terminal"])
"Hellofromtheterminal"

table(matrix, opts \\ [])

@spec table(
  [list()],
  keyword()
) :: :ok

Print an ASCII table of data. Requires a list of lists as input.

Supported options:

  • :header (boolean/0) - Use the first element as the header for the table. The default value is false.

  • :align - Alignment of each column, a list of :left or :right. Columns beyond the list are :left. The default value is [].

  • :footer (boolean/0) - Draw a row delimiter above the last row, to set it apart as a footer. The default value is false.

  • :max_width - Cut a cell wider than its column's maximum, ending it with …. Either one positive integer for every column, or a list with a positive integer or nil (no limit) for each column. Columns past the end of the list have no limit.

  • :border - Determine how the border is displayed, one of :normal (default), :markdown, or :none The default value is :normal.

  • :color - The text color. One of [:black, :blue, :cyan, :green, :light_black, :light_blue, :light_cyan, :light_green, :light_magneta, :light_red, :light_white, :light_yellow, :magenta, :red, :white, :yellow]. Defaults to the terminal default.

  • :device - The IO device to write to, one of :stdio or :stderr. The default value is :stdio.

Examples

iex> Prompt.table([["Hello", "from", "the", "terminal!"],["this", "is", "another", "row"]])
"
 +-------+------+---------+----------+
 | Hello | from | the     | terminal |
 | this  | is   | another | row      |
 +-------+------+---------+----------+
"

iex> Prompt.table([["One", "Two", "Three", "Four"], ["Hello", "from", "the", "terminal!"],["this", "is", "another", "row"]], header: true)
"
 +-------+------+---------+----------+
 | One   | Two  | Three   | Four     |
 +-------+------+---------+----------+
 | Hello | from | the     | terminal |
 | this  | is   | another | row      |
 +-------+------+---------+----------+
"

iex> Prompt.table([["One", "Two", "Three", "Four"], ["Hello", "from", "the", "terminal!"],["this", "is", "another", "row"]], header: true, border: :markdown)
"
 | One   | Two  | Three   | Four     |
 |-------|------|---------|----------|
 | Hello | from | the     | terminal |
 | this  | is   | another | row      |
"

iex> Prompt.table([["Hello", "from", "the", "terminal!"],["this", "is", "another", "row"]], border: :none)
"
 Hello from the     terminal
 this  is   another row
"

Types

argv()

@type argv() :: [String.t()]

The list of strings coming from the commmand-line arguments

command_list()

@type command_list() :: keyword(Prompt.Command)

A keyword list of commands and implementations of Prompt.Command

Examples

iex> commands = [help: CLI.Commands.Help, version: CLI.Commands.Version]

Functions

raw_mode_supported?()

@spec raw_mode_supported?() :: boolean()

Does the current Erlang runtime version support raw terminal mode?

table_data(matrix, opts \\ [border: :normal])

@spec table_data(
  [list()],
  keyword()
) :: [<<_::0>> | [any()], ...]

Use this to get an iolist back of the table. Useful when you want an ascii table/1 for other mediums like markdown files.