reverse_adoc 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/macos.yml +27 -0
  3. data/.github/workflows/ubuntu.yml +27 -0
  4. data/.github/workflows/windows.yml +30 -0
  5. data/.hound.yml +3 -0
  6. data/.rubocop.yml +10 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +25 -0
  9. data/README.adoc +290 -0
  10. data/Rakefile +14 -0
  11. data/bin/reverse_adoc +67 -0
  12. data/bin/w2a +85 -0
  13. data/lib/reverse_asciidoctor.rb +70 -0
  14. data/lib/reverse_asciidoctor/cleaner.rb +90 -0
  15. data/lib/reverse_asciidoctor/config.rb +53 -0
  16. data/lib/reverse_asciidoctor/converters.rb +33 -0
  17. data/lib/reverse_asciidoctor/converters/a.rb +38 -0
  18. data/lib/reverse_asciidoctor/converters/aside.rb +14 -0
  19. data/lib/reverse_asciidoctor/converters/audio.rb +34 -0
  20. data/lib/reverse_asciidoctor/converters/base.rb +24 -0
  21. data/lib/reverse_asciidoctor/converters/blockquote.rb +18 -0
  22. data/lib/reverse_asciidoctor/converters/br.rb +11 -0
  23. data/lib/reverse_asciidoctor/converters/bypass.rb +77 -0
  24. data/lib/reverse_asciidoctor/converters/code.rb +15 -0
  25. data/lib/reverse_asciidoctor/converters/div.rb +14 -0
  26. data/lib/reverse_asciidoctor/converters/drop.rb +18 -0
  27. data/lib/reverse_asciidoctor/converters/em.rb +18 -0
  28. data/lib/reverse_asciidoctor/converters/figure.rb +21 -0
  29. data/lib/reverse_asciidoctor/converters/h.rb +19 -0
  30. data/lib/reverse_asciidoctor/converters/head.rb +18 -0
  31. data/lib/reverse_asciidoctor/converters/hr.rb +11 -0
  32. data/lib/reverse_asciidoctor/converters/ignore.rb +12 -0
  33. data/lib/reverse_asciidoctor/converters/img.rb +80 -0
  34. data/lib/reverse_asciidoctor/converters/li.rb +24 -0
  35. data/lib/reverse_asciidoctor/converters/mark.rb +12 -0
  36. data/lib/reverse_asciidoctor/converters/math.rb +20 -0
  37. data/lib/reverse_asciidoctor/converters/ol.rb +46 -0
  38. data/lib/reverse_asciidoctor/converters/p.rb +17 -0
  39. data/lib/reverse_asciidoctor/converters/pass_through.rb +9 -0
  40. data/lib/reverse_asciidoctor/converters/pre.rb +38 -0
  41. data/lib/reverse_asciidoctor/converters/q.rb +12 -0
  42. data/lib/reverse_asciidoctor/converters/strong.rb +17 -0
  43. data/lib/reverse_asciidoctor/converters/sub.rb +12 -0
  44. data/lib/reverse_asciidoctor/converters/sup.rb +12 -0
  45. data/lib/reverse_asciidoctor/converters/table.rb +64 -0
  46. data/lib/reverse_asciidoctor/converters/td.rb +67 -0
  47. data/lib/reverse_asciidoctor/converters/text.rb +65 -0
  48. data/lib/reverse_asciidoctor/converters/th.rb +16 -0
  49. data/lib/reverse_asciidoctor/converters/tr.rb +22 -0
  50. data/lib/reverse_asciidoctor/converters/video.rb +36 -0
  51. data/lib/reverse_asciidoctor/errors.rb +10 -0
  52. data/lib/reverse_asciidoctor/version.rb +3 -0
  53. data/reverse_adoc.gemspec +35 -0
  54. data/spec/assets/anchors.html +22 -0
  55. data/spec/assets/basic.html +58 -0
  56. data/spec/assets/code.html +22 -0
  57. data/spec/assets/escapables.html +15 -0
  58. data/spec/assets/from_the_wild.html +23 -0
  59. data/spec/assets/full_example.html +49 -0
  60. data/spec/assets/html_fragment.html +3 -0
  61. data/spec/assets/lists.html +137 -0
  62. data/spec/assets/minimum.html +4 -0
  63. data/spec/assets/paragraphs.html +24 -0
  64. data/spec/assets/quotation.html +12 -0
  65. data/spec/assets/tables.html +99 -0
  66. data/spec/assets/unknown_tags.html +9 -0
  67. data/spec/components/anchors_spec.rb +21 -0
  68. data/spec/components/basic_spec.rb +49 -0
  69. data/spec/components/code_spec.rb +28 -0
  70. data/spec/components/escapables_spec.rb +23 -0
  71. data/spec/components/from_the_wild_spec.rb +17 -0
  72. data/spec/components/html_fragment_spec.rb +11 -0
  73. data/spec/components/lists_spec.rb +86 -0
  74. data/spec/components/paragraphs_spec.rb +15 -0
  75. data/spec/components/quotation_spec.rb +12 -0
  76. data/spec/components/tables_spec.rb +31 -0
  77. data/spec/components/unknown_tags_spec.rb +39 -0
  78. data/spec/lib/reverse_asciidoctor/cleaner_spec.rb +157 -0
  79. data/spec/lib/reverse_asciidoctor/config_spec.rb +26 -0
  80. data/spec/lib/reverse_asciidoctor/converters/aside_spec.rb +12 -0
  81. data/spec/lib/reverse_asciidoctor/converters/audio_spec.rb +18 -0
  82. data/spec/lib/reverse_asciidoctor/converters/blockquote_spec.rb +24 -0
  83. data/spec/lib/reverse_asciidoctor/converters/br_spec.rb +9 -0
  84. data/spec/lib/reverse_asciidoctor/converters/code_spec.rb +18 -0
  85. data/spec/lib/reverse_asciidoctor/converters/div_spec.rb +18 -0
  86. data/spec/lib/reverse_asciidoctor/converters/figure_spec.rb +13 -0
  87. data/spec/lib/reverse_asciidoctor/converters/img_spec.rb +28 -0
  88. data/spec/lib/reverse_asciidoctor/converters/li_spec.rb +13 -0
  89. data/spec/lib/reverse_asciidoctor/converters/mark_spec.rb +10 -0
  90. data/spec/lib/reverse_asciidoctor/converters/p_spec.rb +12 -0
  91. data/spec/lib/reverse_asciidoctor/converters/pre_spec.rb +45 -0
  92. data/spec/lib/reverse_asciidoctor/converters/q_spec.rb +10 -0
  93. data/spec/lib/reverse_asciidoctor/converters/strong_spec.rb +20 -0
  94. data/spec/lib/reverse_asciidoctor/converters/text_spec.rb +62 -0
  95. data/spec/lib/reverse_asciidoctor/converters/video_spec.rb +18 -0
  96. data/spec/lib/reverse_asciidoctor/converters_spec.rb +19 -0
  97. data/spec/lib/reverse_asciidoctor_spec.rb +37 -0
  98. data/spec/spec_helper.rb +21 -0
  99. metadata +299 -0
@@ -0,0 +1,20 @@
1
+ # Unless run with ReverseAsciidoctor.config.mathml2asciimath,
2
+ # this is cheating: we're injecting MathML into Asciidoctor, but
3
+ # Asciidoctor only understands AsciiMath or LaTeX
4
+
5
+ require "mathml2asciimath"
6
+
7
+ module ReverseAsciidoctor
8
+ module Converters
9
+ class Math < Base
10
+ def convert(node, state = {})
11
+ stem = node.to_s.gsub(/\n/, " ")
12
+ stem = MathML2AsciiMath.m2a(stem) if ReverseAsciidoctor.config.mathml2asciimath
13
+ stem = stem.gsub(/\[/, "\\[").gsub(/\]/, "\\]").gsub(/\(\(([^\)]+)\)\)/, "(\\1)") unless stem.nil?
14
+ " stem:[" << stem << "] "
15
+ end
16
+ end
17
+
18
+ register :math, Math.new
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Ol < Base
4
+ def convert(node, state = {})
5
+ id = node['id']
6
+ anchor = id ? "[[#{id}]]\n" : ""
7
+ ol_count = state.fetch(:ol_count, 0) + 1
8
+ attrs = ol_attrs(node)
9
+ "\n#{anchor}#{attrs}" << treat_children(node, state.merge(ol_count: ol_count))
10
+ end
11
+
12
+ def number_style(node)
13
+ style = case node["style"]
14
+ when "1" then "arabic"
15
+ when "A" then "upperalpha"
16
+ when "a" then "loweralpha"
17
+ when "I" then "upperroman"
18
+ when "i" then "lowerroman"
19
+ else
20
+ nil
21
+ end
22
+ end
23
+
24
+ def ol_attrs(node)
25
+ style = number_style(node)
26
+ reversed = "%reversed" if node["reversed"]
27
+ start = "start=#{node['start']}" if node["start"]
28
+ type = "type=#{node['type']}" if node["type"]
29
+ attrs = []
30
+ attrs << style if style
31
+ attrs << reversed if reversed
32
+ attrs << start if start
33
+ attrs << type if type
34
+ if attrs.empty?
35
+ ""
36
+ else
37
+ "[#{attrs.join(',')}]\n"
38
+ end
39
+ end
40
+ end
41
+
42
+ register :ol, Ol.new
43
+ register :ul, Ol.new
44
+ register :dir, Ol.new
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class P < Base
4
+ def convert(node, state = {})
5
+ id = node['id']
6
+ anchor = id ? "[[#{id}]]\n" : ""
7
+ if state[:tdsinglepara]
8
+ "#{anchor}" << treat_children(node, state).strip
9
+ else
10
+ "\n\n#{anchor}" << treat_children(node, state).strip << "\n\n"
11
+ end
12
+ end
13
+ end
14
+
15
+ register :p, P.new
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class PassThrough < Base
4
+ def convert(node, state = {})
5
+ node.to_s
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Pre < Base
4
+ def convert(node, state = {})
5
+ id = node['id']
6
+ anchor = id ? "[[#{id}]]\n" : ""
7
+ lang = language(node)
8
+ content = treat_children(node, state)
9
+ if lang
10
+ "\n\n#{anchor}[source,#{lang}]\n----\n" << content.lines.to_a.join("") << "\n----\n\n"
11
+ else
12
+ "\n\n#{anchor}....\n" << content.lines.to_a.join("") << "\n....\n\n"
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def treat(node, state)
19
+ node.to_s
20
+ end
21
+
22
+ def language(node)
23
+ lang = language_from_highlight_class(node)
24
+ lang || language_from_confluence_class(node)
25
+ end
26
+
27
+ def language_from_highlight_class(node)
28
+ node.parent['class'].to_s[/highlight-([a-zA-Z0-9]+)/, 1]
29
+ end
30
+
31
+ def language_from_confluence_class(node)
32
+ node['class'].to_s[/brush:\s?(:?.*);/, 1]
33
+ end
34
+ end
35
+
36
+ register :pre, Pre.new
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Q < Base
4
+ def convert(node, state = {})
5
+ content = treat_children(node, state)
6
+ "#{content[/^\s*/]}\"#{content.strip}\"#{content[/\s*$/]}"
7
+ end
8
+ end
9
+
10
+ register :q, Q.new
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Strong < Base
4
+ def convert(node, state = {})
5
+ content = treat_children(node, state.merge(already_strong: true))
6
+ if content.strip.empty? || state[:already_strong]
7
+ content
8
+ else
9
+ "#{content[/^\s*/]}*#{content.strip}*#{content[/\s*$/]}"
10
+ end
11
+ end
12
+ end
13
+
14
+ register :strong, Strong.new
15
+ register :b, Strong.new
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Sub < Base
4
+ def convert(node, state = {})
5
+ content = treat_children(node, state)
6
+ "#{content[/^\s*/]}~#{content.strip}~#{content[/\s*$/]}"
7
+ end
8
+ end
9
+
10
+ register :sub, Sub.new
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Sup < Base
4
+ def convert(node, state = {})
5
+ content = treat_children(node, state)
6
+ "#{content[/^\s*/]}^#{content.strip}^#{content[/\s*$/]}"
7
+ end
8
+ end
9
+
10
+ register :sup, Sup.new
11
+ end
12
+ end
@@ -0,0 +1,64 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Table < Base
4
+ def convert(node, state = {})
5
+ id = node['id']
6
+ anchor = id ? "[[#{id}]]\n" : ""
7
+ title = extract_title(node)
8
+ title = ".#{title}\n" unless title.empty?
9
+ attrs = style(node)
10
+ "\n\n#{anchor}#{attrs}#{title}|===\n" << treat_children(node, state) << "\n|===\n"
11
+ end
12
+
13
+ def extract_title(node)
14
+ title = node.at("./caption")
15
+ return "" if title.nil?
16
+ treat_children(title, {})
17
+ end
18
+
19
+ def frame(node)
20
+ case node["frame"]
21
+ when "void"
22
+ "frame=none"
23
+ when "hsides"
24
+ "frame=topbot"
25
+ when "vsides"
26
+ "frame=sides"
27
+ when "box", "border"
28
+ "frame=all"
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def rules(node)
35
+ case node["rules"]
36
+ when "all"
37
+ "rules=all"
38
+ when "rows"
39
+ "rules=rows"
40
+ when "cols"
41
+ "rules=cols"
42
+ when "none"
43
+ "rules=none"
44
+ else
45
+ nil
46
+ end
47
+ end
48
+
49
+ def style(node)
50
+ width = "width=#{node['width']}" if node['width']
51
+ attrs = []
52
+ frame_attr = frame(node)
53
+ rules_attr = rules(node)
54
+ attrs << width if width
55
+ attrs << frame_attr if frame_attr
56
+ attrs << rules_attr if rules_attr
57
+ return "" if attrs.empty?
58
+ "[#{attrs.join(',')}]\n"
59
+ end
60
+ end
61
+
62
+ register :table, Table.new
63
+ end
64
+ end
@@ -0,0 +1,67 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Td < Base
4
+ def convert(node, state = {})
5
+ id = node['id']
6
+ anchor = id ? "[[#{id}]]" : ""
7
+ colrowattr = colrow(node['colspan'], node['rowspan'])
8
+ alignattr = alignstyle(node)
9
+ style = cellstyle(node)
10
+ singlepara = node.elements.size == 1 && node.elements.first.name == "p"
11
+ state[:tdsinglepara] = singlepara if singlepara
12
+ adoccell = node.at(".//ul | .//ol | .//pre | .//blockquote | .//br | .//hr") ||
13
+ node.at(".//p") && !singlepara
14
+ style = "a" if adoccell
15
+ delim = adoccell ? "\n" : " "
16
+ content = treat_children(node, state)
17
+ "#{colrowattr}#{alignattr}#{style}| #{anchor}#{content}#{delim}"
18
+ end
19
+
20
+ def cellstyle(node)
21
+ ""
22
+ end
23
+
24
+ def colrow(colspan, rowspan)
25
+ if colspan && rowspan
26
+ "#{colspan}.#{rowspan}+"
27
+ elsif colspan
28
+ "#{colspan}+"
29
+ elsif rowspan
30
+ ".#{rowspan}+"
31
+ else
32
+ ""
33
+ end
34
+ end
35
+
36
+ def alignstyle(node)
37
+ align = node["align"]
38
+ valign = node["valign"]
39
+ a = case align
40
+ when "left" then "<"
41
+ when "center" then "^"
42
+ when "right" then ">"
43
+ else
44
+ ""
45
+ end
46
+ v = case valign
47
+ when "top" then "<"
48
+ when "middle" then "^"
49
+ when "bottom" then ">"
50
+ else
51
+ ""
52
+ end
53
+ if align && valign
54
+ "#{a}.#{v}"
55
+ elsif align
56
+ a
57
+ elsif valign
58
+ ".#{v}"
59
+ else
60
+ ""
61
+ end
62
+ end
63
+ end
64
+
65
+ register :td, Td.new
66
+ end
67
+ end
@@ -0,0 +1,65 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Text < Base
4
+ def convert(node, state = {})
5
+ if node.text.strip.empty?
6
+ treat_empty(node, state)
7
+ else
8
+ treat_text(node)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def treat_empty(node, state)
15
+ parent = node.parent.name.to_sym
16
+ if [:ol, :ul].include?(parent) # Otherwise the identation is broken
17
+ ''
18
+ elsif state[:tdsinglepara]
19
+ ''
20
+ elsif node.text == ' ' # Regular whitespace text node
21
+ ' '
22
+ else
23
+ ''
24
+ end
25
+ end
26
+
27
+ def treat_text(node)
28
+ text = node.text
29
+ text = preserve_nbsp(text)
30
+ text = remove_border_newlines(text)
31
+ text = remove_inner_newlines(text)
32
+ text = escape_keychars(text)
33
+
34
+ text = preserve_keychars_within_backticks(text)
35
+ text = preserve_tags(text)
36
+
37
+ text
38
+ end
39
+
40
+ def preserve_nbsp(text)
41
+ text.gsub(/\u00A0/, "&nbsp;")
42
+ end
43
+
44
+ def preserve_tags(text)
45
+ text.gsub(/[<>]/, '>' => '\>', '<' => '\<')
46
+ end
47
+
48
+ def remove_border_newlines(text)
49
+ text.gsub(/\A\n+/, '').gsub(/\n+\z/, '')
50
+ end
51
+
52
+ def remove_inner_newlines(text)
53
+ text.tr("\n\t", ' ').squeeze(' ')
54
+ end
55
+
56
+ def preserve_keychars_within_backticks(text)
57
+ text.gsub(/`.*?`/) do |match|
58
+ match.gsub('\_', '_').gsub('\*', '*')
59
+ end
60
+ end
61
+ end
62
+
63
+ register :text, Text.new
64
+ end
65
+ end
@@ -0,0 +1,16 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Th < Td
4
+ def cellstyle(node)
5
+ if node.parent.previous_element.nil?
6
+ # this is the header row
7
+ ""
8
+ else
9
+ "h"
10
+ end
11
+ end
12
+ end
13
+
14
+ register :th, Th.new
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Tr < Base
4
+ def convert(node, state = {})
5
+ content = treat_children(node, state).rstrip
6
+ result = "#{content}\n"
7
+ table_header_row?(node) ? result + underline_for(node) : result
8
+ end
9
+
10
+ def table_header_row?(node)
11
+ # node.element_children.all? {|child| child.name.to_sym == :th}
12
+ node.previous_element.nil?
13
+ end
14
+
15
+ def underline_for(node)
16
+ "\n"
17
+ end
18
+ end
19
+
20
+ register :tr, Tr.new
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ module ReverseAsciidoctor
2
+ module Converters
3
+ class Video < Base
4
+ def convert(node, state = {})
5
+ autoplay = node['autoplay']
6
+ loop_attr = node['loop']
7
+ controls = node['controls']
8
+ src = node['src']
9
+ id = node['id']
10
+ anchor = id ? "[[#{id}]]\n" : ""
11
+ title = extract_title(node)
12
+ title = ".#{title}\n" unless title.empty?
13
+ [anchor, title, "video::", src, "[", options(node), "]"].join("")
14
+ end
15
+
16
+ def options(node)
17
+ autoplay = node['autoplay']
18
+ loop_attr = node['loop']
19
+ controls = node['controls']
20
+ width = node['width']
21
+ ret = ""
22
+ if autoplay || loop_attr || controls
23
+ out = []
24
+ out << "autoplay" if autoplay
25
+ out << "loop" if loop_attr
26
+ out << "controls" if controls
27
+ out << "width=#{width}" if width
28
+ ret = %{options="#{out.join(',')}"}
29
+ end
30
+ ret
31
+ end
32
+ end
33
+
34
+ register :video, Video.new
35
+ end
36
+ end