logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

earmark_renderer.ex (7534B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. #
  5. # This file is derived from Earmark, under the following copyright:
  6. # Copyright © 2014 Dave Thomas, The Pragmatic Programmers
  7. # SPDX-License-Identifier: Apache-2.0
  8. # Upstream: https://github.com/pragdave/earmark/blob/master/lib/earmark/html_renderer.ex
  9. defmodule Pleroma.EarmarkRenderer do
  10. @moduledoc false
  11. alias Earmark.Block
  12. alias Earmark.Context
  13. alias Earmark.HtmlRenderer
  14. alias Earmark.Options
  15. import Earmark.Inline, only: [convert: 3]
  16. import Earmark.Helpers.HtmlHelpers
  17. import Earmark.Message, only: [add_messages_from: 2, get_messages: 1, set_messages: 2]
  18. import Earmark.Context, only: [append: 2, set_value: 2]
  19. import Earmark.Options, only: [get_mapper: 1]
  20. @doc false
  21. def render(blocks, %Context{options: %Options{}} = context) do
  22. messages = get_messages(context)
  23. {contexts, html} =
  24. get_mapper(context.options).(
  25. blocks,
  26. &render_block(&1, put_in(context.options.messages, []))
  27. )
  28. |> Enum.unzip()
  29. all_messages =
  30. contexts
  31. |> Enum.reduce(messages, fn ctx, messages1 -> messages1 ++ get_messages(ctx) end)
  32. {put_in(context.options.messages, all_messages), html |> IO.iodata_to_binary()}
  33. end
  34. #############
  35. # Paragraph #
  36. #############
  37. defp render_block(%Block.Para{lnb: lnb, lines: lines, attrs: attrs}, context) do
  38. lines = convert(lines, lnb, context)
  39. add_attrs(lines, "<p>#{lines.value}</p>", attrs, [], lnb)
  40. end
  41. ########
  42. # Html #
  43. ########
  44. defp render_block(%Block.Html{html: html}, context) do
  45. {context, html}
  46. end
  47. defp render_block(%Block.HtmlComment{lines: lines}, context) do
  48. {context, lines}
  49. end
  50. defp render_block(%Block.HtmlOneline{html: html}, context) do
  51. {context, html}
  52. end
  53. #########
  54. # Ruler #
  55. #########
  56. defp render_block(%Block.Ruler{lnb: lnb, attrs: attrs}, context) do
  57. add_attrs(context, "<hr />", attrs, [], lnb)
  58. end
  59. ###########
  60. # Heading #
  61. ###########
  62. defp render_block(
  63. %Block.Heading{lnb: lnb, level: level, content: content, attrs: attrs},
  64. context
  65. ) do
  66. converted = convert(content, lnb, context)
  67. html = "<h#{level}>#{converted.value}</h#{level}>"
  68. add_attrs(converted, html, attrs, [], lnb)
  69. end
  70. ##############
  71. # Blockquote #
  72. ##############
  73. defp render_block(%Block.BlockQuote{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
  74. {context1, body} = render(blocks, context)
  75. html = "<blockquote>#{body}</blockquote>"
  76. add_attrs(context1, html, attrs, [], lnb)
  77. end
  78. #########
  79. # Table #
  80. #########
  81. defp render_block(
  82. %Block.Table{lnb: lnb, header: header, rows: rows, alignments: aligns, attrs: attrs},
  83. context
  84. ) do
  85. {context1, html} = add_attrs(context, "<table>", attrs, [], lnb)
  86. context2 = set_value(context1, html)
  87. context3 =
  88. if header do
  89. append(add_trs(append(context2, "<thead>"), [header], "th", aligns, lnb), "</thead>")
  90. else
  91. # Maybe an error, needed append(context, html)
  92. context2
  93. end
  94. context4 = append(add_trs(append(context3, "<tbody>"), rows, "td", aligns, lnb), "</tbody>")
  95. {context4, [context4.value, "</table>"]}
  96. end
  97. ########
  98. # Code #
  99. ########
  100. defp render_block(
  101. %Block.Code{lnb: lnb, language: language, attrs: attrs} = block,
  102. %Context{options: options} = context
  103. ) do
  104. class =
  105. if language, do: ~s{ class="#{code_classes(language, options.code_class_prefix)}"}, else: ""
  106. tag = ~s[<pre><code#{class}>]
  107. lines = options.render_code.(block)
  108. html = ~s[#{tag}#{lines}</code></pre>]
  109. add_attrs(context, html, attrs, [], lnb)
  110. end
  111. #########
  112. # Lists #
  113. #########
  114. defp render_block(
  115. %Block.List{lnb: lnb, type: type, blocks: items, attrs: attrs, start: start},
  116. context
  117. ) do
  118. {context1, content} = render(items, context)
  119. html = "<#{type}#{start}>#{content}</#{type}>"
  120. add_attrs(context1, html, attrs, [], lnb)
  121. end
  122. # format a single paragraph list item, and remove the para tags
  123. defp render_block(
  124. %Block.ListItem{lnb: lnb, blocks: blocks, spaced: false, attrs: attrs},
  125. context
  126. )
  127. when length(blocks) == 1 do
  128. {context1, content} = render(blocks, context)
  129. content = Regex.replace(~r{</?p>}, content, "")
  130. html = "<li>#{content}</li>"
  131. add_attrs(context1, html, attrs, [], lnb)
  132. end
  133. # format a spaced list item
  134. defp render_block(%Block.ListItem{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
  135. {context1, content} = render(blocks, context)
  136. html = "<li>#{content}</li>"
  137. add_attrs(context1, html, attrs, [], lnb)
  138. end
  139. ##################
  140. # Footnote Block #
  141. ##################
  142. defp render_block(%Block.FnList{blocks: footnotes}, context) do
  143. items =
  144. Enum.map(footnotes, fn note ->
  145. blocks = append_footnote_link(note)
  146. %Block.ListItem{attrs: "#fn:#{note.number}", type: :ol, blocks: blocks}
  147. end)
  148. {context1, html} = render_block(%Block.List{type: :ol, blocks: items}, context)
  149. {context1, Enum.join([~s[<div class="footnotes">], "<hr />", html, "</div>"])}
  150. end
  151. #######################################
  152. # Isolated IALs are rendered as paras #
  153. #######################################
  154. defp render_block(%Block.Ial{verbatim: verbatim}, context) do
  155. {context, "<p>{:#{verbatim}}</p>"}
  156. end
  157. ####################
  158. # IDDef is ignored #
  159. ####################
  160. defp render_block(%Block.IdDef{}, context), do: {context, ""}
  161. #####################################
  162. # And here are the inline renderers #
  163. #####################################
  164. defdelegate br, to: HtmlRenderer
  165. defdelegate codespan(text), to: HtmlRenderer
  166. defdelegate em(text), to: HtmlRenderer
  167. defdelegate strong(text), to: HtmlRenderer
  168. defdelegate strikethrough(text), to: HtmlRenderer
  169. defdelegate link(url, text), to: HtmlRenderer
  170. defdelegate link(url, text, title), to: HtmlRenderer
  171. defdelegate image(path, alt, title), to: HtmlRenderer
  172. defdelegate footnote_link(ref, backref, number), to: HtmlRenderer
  173. # Table rows
  174. defp add_trs(context, rows, tag, aligns, lnb) do
  175. numbered_rows =
  176. rows
  177. |> Enum.zip(Stream.iterate(lnb, &(&1 + 1)))
  178. numbered_rows
  179. |> Enum.reduce(context, fn {row, lnb}, ctx ->
  180. append(add_tds(append(ctx, "<tr>"), row, tag, aligns, lnb), "</tr>")
  181. end)
  182. end
  183. defp add_tds(context, row, tag, aligns, lnb) do
  184. Enum.reduce(1..length(row), context, add_td_fn(row, tag, aligns, lnb))
  185. end
  186. defp add_td_fn(row, tag, aligns, lnb) do
  187. fn n, ctx ->
  188. style =
  189. case Enum.at(aligns, n - 1, :default) do
  190. :default -> ""
  191. align -> " style=\"text-align: #{align}\""
  192. end
  193. col = Enum.at(row, n - 1)
  194. converted = convert(col, lnb, set_messages(ctx, []))
  195. append(add_messages_from(ctx, converted), "<#{tag}#{style}>#{converted.value}</#{tag}>")
  196. end
  197. end
  198. ###############################
  199. # Append Footnote Return Link #
  200. ###############################
  201. defdelegate append_footnote_link(note), to: HtmlRenderer
  202. defdelegate append_footnote_link(note, fnlink), to: HtmlRenderer
  203. defdelegate render_code(lines), to: HtmlRenderer
  204. defp code_classes(language, prefix) do
  205. ["" | String.split(prefix || "")]
  206. |> Enum.map(fn pfx -> "#{pfx}#{language}" end)
  207. |> Enum.join(" ")
  208. end
  209. end