Packages
livebook
0.6.2
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_web/helpers/ansi.ex
defmodule LivebookWeb.Helpers.ANSI do
@moduledoc false
@doc """
Takes a string with ANSI escape codes and build a HTML safe string
with `span` tags having classes corresponding to the escape codes.
Any HTML in the string is escaped.
"""
@spec ansi_string_to_html(String.t()) :: Phoenix.HTML.safe()
def ansi_string_to_html(string) do
string
|> Livebook.Utils.ANSI.parse_ansi_string()
|> elem(0)
|> parts_to_html()
end
defp parts_to_html(parts) do
parts
|> Enum.map(fn {modifiers, string} ->
style = modifiers_to_css(modifiers)
{:safe, escaped} = Phoenix.HTML.html_escape(string)
if style == "" or string == "" do
escaped
else
[~s{<span style="}, style, ~s{">}, escaped, ~s{</span>}]
end
end)
|> Phoenix.HTML.raw()
end
defp modifiers_to_css(modifiers) do
Enum.map_join(modifiers, &modifier_to_css/1)
end
defp modifier_to_css({:font_weight, :bold}), do: "font-weight: 600;"
defp modifier_to_css({:font_weight, :light}), do: "font-weight: 200;"
defp modifier_to_css({:font_style, :italic}), do: "font-style: italic;"
defp modifier_to_css({:text_decoration, :underline}), do: "text-decoration: underline;"
defp modifier_to_css({:text_decoration, :line_through}), do: "text-decoration: line-through;"
defp modifier_to_css({:text_decoration, :overline}), do: "text-decoration: overline;"
defp modifier_to_css({:foreground_color, color}), do: "color: #{color_to_css(color)};"
defp modifier_to_css({:background_color, color}),
do: "background-color: #{color_to_css(color)};"
defp color_to_css(:black), do: "var(--ansi-color-black)"
defp color_to_css(:light_black), do: "var(--ansi-color-light-black)"
defp color_to_css(:red), do: "var(--ansi-color-red)"
defp color_to_css(:light_red), do: "var(--ansi-color-light-red)"
defp color_to_css(:green), do: "var(--ansi-color-green)"
defp color_to_css(:light_green), do: "var(--ansi-color-light-green)"
defp color_to_css(:yellow), do: "var(--ansi-color-yellow)"
defp color_to_css(:light_yellow), do: "var(--ansi-color-light-yellow)"
defp color_to_css(:blue), do: "var(--ansi-color-blue)"
defp color_to_css(:light_blue), do: "var(--ansi-color-light-blue)"
defp color_to_css(:magenta), do: "var(--ansi-color-magenta)"
defp color_to_css(:light_magenta), do: "var(--ansi-color-light-magenta)"
defp color_to_css(:cyan), do: "var(--ansi-color-cyan)"
defp color_to_css(:light_cyan), do: "var(--ansi-color-light-cyan)"
defp color_to_css(:white), do: "var(--ansi-color-white)"
defp color_to_css(:light_white), do: "var(--ansi-color-light-white)"
defp color_to_css({:rgb6, r, g, b}) do
r = div(255 * r, 5)
g = div(255 * g, 5)
b = div(255 * b, 5)
"rgb(#{r}, #{g}, #{b})"
end
defp color_to_css({:grayscale24, level}) do
value = div(255 * level, 23)
"rgb(#{value}, #{value}, #{value})"
end
@doc """
Converts a string with ANSI escape codes into HTML lines.
This method is similar to `ansi_string_to_html/1`,
but makes sure each line is itself a valid HTML
(as opposed to just splitting HTML into lines).
"""
@spec ansi_string_to_html_lines(String.t()) :: list(Phoenix.HTML.safe())
def ansi_string_to_html_lines(string) do
string
|> Livebook.Utils.ANSI.parse_ansi_string()
|> elem(0)
|> split_parts_into_lines()
|> Enum.map(&parts_to_html/1)
end
defp split_parts_into_lines(parts), do: split_parts_into_lines(parts, [[]])
defp split_parts_into_lines([], groups) do
groups
|> Enum.map(&Enum.reverse/1)
|> Enum.reverse()
end
defp split_parts_into_lines([{modifiers, string} | parts], [group | groups]) do
[line | lines] = String.split(string, "\n")
new_groups = lines |> Enum.map(&[{modifiers, &1}]) |> Enum.reverse()
split_parts_into_lines(parts, new_groups ++ [[{modifiers, line} | group] | groups])
end
@doc """
Converts a string with ANSI escape codes into HTML lines.
Same as `ansi_string_to_html_lines_step/1`, but allows
for keeping track of modifiers for stream usage.
"""
@spec ansi_string_to_html_lines_step(String.t(), Livebook.Utils.ANSI.modifiers()) ::
{list(Phoenix.HTML.safe()), Livebook.Utils.ANSI.modifiers()}
def ansi_string_to_html_lines_step(string, modifiers) do
{parts, modifiers} = Livebook.Utils.ANSI.parse_ansi_string(string, modifiers: modifiers)
lines =
parts
|> split_parts_into_lines()
|> Enum.map(&parts_to_html/1)
{lines, modifiers}
end
end