Current section

Files

Jump to
artificery lib console color.ex
Raw

lib/console/color.ex

defmodule Artificery.Console.Color do
@moduledoc false
defmodule ANSI do
@moduledoc false
require IO.ANSI.Sequence
import IO.ANSI.Sequence
defsequence :grey, 37
@ansi_pattern Enum.join([
"[\\e\\x{c29B}][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\a)",
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
], "|") |> Regex.compile!("u")
def strip(text) when is_binary(text) do
String.replace(text, @ansi_pattern, "")
end
end
@doc """
Applies one or more styles to the given binary, styles can include
any of those found in `IO.ANSI`, such as `:faint` or `:cyan`, and also
supports `:dim` as an alias for `:faint`, as well as `:grey` or `:gray`.
Returns a formatted binary
"""
@spec style(binary, [atom]) :: binary
def style(msg, styles) when is_list(styles) do
do_style(Enum.uniq(styles), [msg], false)
end
defp do_style([], acc, _grey), do: IO.iodata_to_binary([acc, IO.ANSI.reset])
defp do_style([:dim], acc, true) do
# Ignore dimming to prevent text disappearing on Windows
acc
end
defp do_style([:dim], acc, false) do
[IO.ANSI.faint | acc]
end
defp do_style([:dim | styles], acc, grey?) do
# Apply dimming last to be sure there is no grey
do_style(styles ++ [:dim], acc, grey?)
end
defp do_style([grey | styles], acc, _grey?) when grey in [:gray, :grey] do
do_style(styles, [ANSI.grey | acc], true)
end
defp do_style([style | styles], acc, grey?) do
do_style(styles, [apply(IO.ANSI, style, []) | acc], grey?)
end
@doc """
Returns true if the current terminal supports color output, false if not
"""
@spec supports_color?() :: boolean
def supports_color? do
supports() != :nocolor
end
@doc """
Returns true if the current terminal supports 256bit color output, false if not
"""
@spec supports_256color?() :: boolean
def supports_256color? do
supports() in [:color256, :color16m]
end
@doc """
Returns true if the current terminal supports 16M bit color output, false if not
"""
@spec supports_truecolor?() :: boolean
def supports_truecolor? do
supports() == :color16m
end
# Returns what level of color support the current TTY has
@spec supports() :: :nocolor | :color | :color256 | :color16m
defp supports do
min =
if IO.ANSI.enabled? do
:color
else
:nocolor
end
case :os.type() do
{:win32, _} ->
case :os.version() do
{10, _, build} when build >= 14931 ->
throw :color16m
{10, _, build} when build >= 10586 ->
throw :color256
{_, _, _} ->
throw :color
end
_ ->
:ok
end
if System.get_env("CI") do
cond do
System.get_env("TRAVIS") ->
throw :color
System.get_env("CIRCLECI") ->
throw :color
System.get_env("GITLAB_CI") ->
throw :color
:else ->
throw :nocolor
end
end
if System.get_env("COLORTERM") == "truecolor" do
throw :color16m
end
if System.get_env("TERM_PROGRAM") do
case System.get_env("TERM_PROGRAM") do
"iTerm.app" ->
{:ok, min_version} = Version.parse("3.0.0")
case Version.parse(System.get_env("TERM_PROGRAM_VERSION")) do
{:ok, term_version} ->
if Version.compare(term_version, min_version) in [:gt, :eq] do
throw :color16m
else
throw :color256
end
_ ->
:ok
end
"Apple_Terminal" ->
throw :color256
end
end
if String.match?(System.get_env("TERM"), ~r/-256(color)?$/i) do
throw :color256
end
if String.match?(System.get_env("TERM"), ~r/^screen|^xterm|^vt100|^rxvt|color|ansi|cygwin|linux/i) do
throw :color
end
if System.get_env("COLORTERM") do
throw :color
end
if System.get_env("TERM") == "dumb" do
min
end
min
catch
:throw, supported_level ->
supported_level
end
end