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
|
defmodule Makeup.Styles.HTML.Style do
@moduledoc """
The style struct.
"""
defstruct long_name: "",
short_name: "",
background_color: "#ffffff",
highlight_color: "#ffffcc",
styles: []
alias Makeup.Styles.HTML.TokenStyle
require Makeup.Token.Utils
alias Makeup.Token.Utils
defp handle_inheritance(style_map) do
# Handles insheritance between styles.
# This is automatic in Pygments' design, because they use class inheritance for tokens.
# We don't have class inheritance in elixir, so we must have something else.
# Here, we use a manually build hierarchy to fake inheritance.
#
# In any case, the goal is to have flat tokens at runtime.
# This function is only called at compile time.
Enum.reduce(Utils.precedence(), style_map, fn {parent_key, child_keys}, style_map ->
parent_style = style_map[parent_key]
Enum.reduce(child_keys, style_map, fn child_key, style_map ->
child_style = style_map[child_key]
Map.put(
style_map,
child_key,
Map.merge(
parent_style,
child_style,
fn _k, v1, v2 -> v2 || v1 end
)
)
end)
end)
end
require EEx
EEx.function_from_string(
:defp,
:render_css,
"""
.<%= highlight_class %> .hll {background-color: <%= highlight_color %>}
.<%= highlight_class %> {\
<%= if token_text.color do %>color: <%= token_text.color %>; <% end %>\
<%= if token_text.font_style do %>font-style: <%= token_text.font_style %>; <% end %>\
<%= if token_text.font_weight do %>font-weight: <%= token_text.font_weight %>; <% end %>\
<%= if token_text.border do %>border: <%= token_text.border %>; <% end %>\
<%= if token_text.text_decoration do %>text-decoration: <%= token_text.text_decoration %>; <% end %>\
<%= if background_color do %>background-color: <%= background_color %><% end %>}\
.<%= highlight_class %> .unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
<%= for {css_class, token_style, token_type} <- styles do %>
.<%= highlight_class %> .<%= css_class %> {\
<%= if token_style.color do %>color: <%= token_style.color %>; <% end %>\
<%= if token_style.font_style do %>font-style: <%= token_style.font_style %>; <% end %>\
<%= if token_style.font_weight do %>font-weight: <%= token_style.font_weight %>; <% end %>\
<%= if token_style.border do %>border: <%= token_style.border %>; <% end %>\
<%= if token_style.text_decoration do %>text-decoration: <%= token_style.text_decoration %>; <% end %>\
<%= if token_style.background_color do %>background-color: <%= token_style.background_color %>; <% end %>\
} /* :<%= Atom.to_string(token_type) %> */\
<% end %>
""",
[:highlight_class, :highlight_color, :background_color, :token_text, :styles]
)
@doc """
Generate a stylesheet for a style.
"""
def stylesheet(style, css_class \\ "highlight") do
token_styles =
style.styles
|> Map.delete(:text)
|> Enum.into([])
|> Enum.map(fn {token_type, token_style} ->
css_class = Makeup.Token.Utils.css_class_for_token_type(token_type)
{css_class, token_style, token_type}
end)
|> Enum.filter(fn {_, token_style, _} ->
Makeup.Styles.HTML.TokenStyle.not_empty?(token_style)
end)
|> Enum.sort()
token_text = style.styles[:text]
render_css(
css_class,
style.highlight_color,
style.background_color,
token_text,
token_styles
)
end
@doc """
Creates a new style.
Takes care of unspecified token types and inheritance.
Writes and caches a CSS stylesheet for the style.
"""
def make_style(options \\ []) do
short_name = Keyword.fetch!(options, :short_name)
long_name = Keyword.fetch!(options, :long_name)
background_color = Keyword.fetch!(options, :background_color)
highlight_color = Keyword.fetch!(options, :highlight_color)
incomplete_style_map = Keyword.fetch!(options, :styles)
complete_style_map =
Utils.standard_token_types()
|> Enum.map(fn k -> {k, ""} end)
|> Enum.into(%{})
|> Map.merge(incomplete_style_map)
|> Enum.map(fn {k, v} -> {k, TokenStyle.from_string(v)} end)
|> Enum.into(%{})
|> handle_inheritance
%__MODULE__{
long_name: long_name,
short_name: short_name,
background_color: background_color,
highlight_color: highlight_color,
styles: complete_style_map
}
end
end
|