1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
defmodule Makeup.Formatters.HTML.HTMLFormatter do
@moduledoc """
Turns a list of tokens into HTML fragments.
"""
@group_highlight_js "lib/makeup/formatters/html/scripts/group_highlighter_javascript.js" |> File.read!
defp render_token(escaped_value, css_class, meta, highlight_tag) do
group_id = meta[:group_id]
selectable = Map.get(meta, :selectable, [])
classes = [
css_class || [],
if selectable == false do " unselectable" else [] end
]
[
"<",
highlight_tag,
~S( class="),
classes,
~S("),
if group_id do [~S( data-group-id="), group_id, ~S(")] else [] end,
">",
escaped_value,
"</",
highlight_tag,
">",
]
end
@doc """
Format a single token into an iolist.
"""
def format_token({tag, meta, value}, highlight_tag) do
escaped_value = escape(value)
css_class = Makeup.Token.Utils.css_class_for_token_type(tag)
render_token(escaped_value, css_class, meta, highlight_tag)
end
defp escape_for(?&), do: "&"
defp escape_for(?<), do: "<"
defp escape_for(?>), do: ">"
defp escape_for(?"), do: """
defp escape_for(?'), do: "'"
defp escape_for(c) when is_integer(c) and c <= 127, do: c
defp escape_for(c) when is_integer(c) and c >= 128, do: << c :: utf8 >>
defp escape_for(string) when is_binary(string) do
string
|> to_charlist()
|> Enum.map(&escape_for/1)
end
defp escape(iodata) when is_list(iodata) do
iodata
|> :lists.flatten()
|> Enum.map(&escape_for/1)
end
defp escape(other) when is_binary(other) do
escape_for(other)
end
defp escape(c) when is_integer(c) do
[escape_for(c)]
end
defp escape(other) do
raise "Found `#{inspect(other)}` inside what should be an iolist"
end
@doc """
Turns a list of tokens into an iolist which represents an HTML fragment.
This fragment can be embedded directly into an HTML document.
"""
def format_inner_as_iolist(tokens, opts) do
highlight_tag = Keyword.get(opts, :highlight_tag, "span")
Enum.map(tokens, &format_token(&1, highlight_tag))
end
@doc """
Turns a list of tokens into an HTML fragment.
This fragment can be embedded directly into an HTML document.
"""
def format_inner_as_binary(tokens, opts) do
tokens
|> format_inner_as_iolist(opts)
|> IO.iodata_to_binary
end
@doc """
Turns a list of tokens into an iolist which represents an HTML fragment.
This fragment can be embedded directly into an HTML document.
"""
def format_as_iolist(tokens, opts \\ []) do
css_class = Keyword.get(opts, :css_class, "highlight")
inner = format_inner_as_iolist(tokens, opts)
[
~S(<pre class="),
css_class,
~S("><code>),
inner,
~S(</code></pre>)
]
end
@doc """
Turns a list of tokens into an HTML fragment.
This fragment can be embedded directly into an HTML document.
"""
def format_as_binary(tokens, opts \\ []) do
tokens
|> format_as_iolist(opts)
|> IO.iodata_to_binary
end
@doc """
Return the CSS stylesheet for a given style.
"""
def stylesheet(style \\ :default_style, css_class \\ "highlight")
def stylesheet(style, css_class) when is_atom(style) do
stylesheet(apply(Makeup.Styles.HTML.StyleMap, style, []), css_class)
end
def stylesheet(style, css_class) do
Makeup.Styles.HTML.Style.stylesheet(style, css_class)
end
@doc """
Return a JavaScript snippet to highlight code on mouseover.
This is "raw" javascript, and for inclusion in an HTML file
it must be wrapped in a `<script>` tag.
"""
def group_highlighter_javascript() do
@group_highlight_js
end
end
|