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