review 3.2.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +31 -0
  3. data/.github/workflows/ruby-win.yml +45 -0
  4. data/.github/workflows/ruby.yml +27 -0
  5. data/.rubocop.yml +167 -42
  6. data/.travis.yml +1 -1
  7. data/Dockerfile +21 -5
  8. data/NEWS.ja.md +428 -1
  9. data/NEWS.md +429 -2
  10. data/README.md +11 -7
  11. data/Rakefile +7 -2
  12. data/appveyor.yml +0 -20
  13. data/bin/review +2 -4
  14. data/bin/review-catalog-converter +5 -5
  15. data/bin/review-check +13 -17
  16. data/bin/review-checkdep +3 -6
  17. data/bin/review-compile +13 -22
  18. data/bin/review-epub2html +1 -4
  19. data/bin/review-epubmaker +3 -4
  20. data/bin/review-idgxmlmaker +14 -0
  21. data/bin/review-index +5 -86
  22. data/bin/review-init +1 -4
  23. data/bin/review-pdfmaker +1 -3
  24. data/bin/review-preproc +11 -13
  25. data/bin/review-textmaker +1 -3
  26. data/bin/review-update +1 -4
  27. data/bin/review-validate +7 -7
  28. data/bin/review-vol +5 -82
  29. data/bin/review-webmaker +1 -3
  30. data/doc/config.yml.sample +67 -16
  31. data/doc/config.yml.sample-simple +4 -3
  32. data/doc/format.ja.md +133 -21
  33. data/doc/format.md +135 -34
  34. data/doc/makeindex.ja.md +2 -2
  35. data/doc/pdfmaker.ja.md +43 -1
  36. data/doc/pdfmaker.md +42 -1
  37. data/doc/quickstart.ja.md +55 -25
  38. data/doc/quickstart.md +47 -17
  39. data/lib/review.rb +1 -1
  40. data/lib/review/book.rb +2 -2
  41. data/lib/review/book/base.rb +89 -90
  42. data/lib/review/book/bib.rb +21 -0
  43. data/lib/review/book/book_unit.rb +158 -0
  44. data/lib/review/book/chapter.rb +51 -29
  45. data/lib/review/book/index.rb +42 -248
  46. data/lib/review/book/index/item.rb +46 -0
  47. data/lib/review/book/page_metric.rb +7 -7
  48. data/lib/review/book/part.rb +45 -10
  49. data/lib/review/book/volume.rb +5 -5
  50. data/lib/review/builder.rb +171 -54
  51. data/lib/review/call_hook.rb +20 -0
  52. data/lib/review/catalog.rb +15 -17
  53. data/lib/review/compiler.rb +240 -102
  54. data/lib/review/configure.rb +101 -12
  55. data/lib/review/converter.rb +1 -1
  56. data/lib/review/epub2html.rb +6 -1
  57. data/lib/review/epubmaker.rb +120 -118
  58. data/lib/review/epubmaker/content.rb +113 -0
  59. data/lib/review/epubmaker/epubcommon.rb +372 -0
  60. data/lib/review/epubmaker/epubv2.rb +178 -0
  61. data/lib/review/epubmaker/epubv3.rb +231 -0
  62. data/lib/review/epubmaker/producer.rb +168 -0
  63. data/lib/review/epubmaker/reviewheaderlistener.rb +12 -2
  64. data/lib/review/epubmaker/zip_exporter.rb +84 -0
  65. data/lib/review/exception.rb +6 -0
  66. data/lib/review/extentions/string.rb +0 -4
  67. data/lib/review/htmlbuilder.rb +147 -149
  68. data/lib/review/htmlutils.rb +10 -14
  69. data/lib/review/i18n.rb +4 -3
  70. data/lib/review/idgxmlbuilder.rb +229 -103
  71. data/lib/review/idgxmlmaker.rb +188 -0
  72. data/lib/review/img_math.rb +245 -0
  73. data/lib/review/index_builder.rb +654 -0
  74. data/lib/review/init-web/finish.html +10 -0
  75. data/lib/review/init-web/index.html +190 -0
  76. data/lib/review/init-web/review-layout-design.js +691 -0
  77. data/lib/review/init.rb +124 -41
  78. data/lib/review/latexbox.rb +58 -0
  79. data/lib/review/latexbuilder.rb +249 -97
  80. data/lib/review/latexutils.rb +9 -1
  81. data/lib/review/lineinput.rb +113 -3
  82. data/lib/review/logger.rb +43 -8
  83. data/lib/review/makerhelper.rb +13 -186
  84. data/lib/review/markdownbuilder.rb +75 -39
  85. data/lib/review/md2inaobuilder.rb +3 -5
  86. data/lib/review/pdfmaker.rb +88 -83
  87. data/lib/review/plaintextbuilder.rb +158 -83
  88. data/lib/review/preprocessor.rb +30 -24
  89. data/lib/review/rstbuilder.rb +58 -39
  90. data/lib/review/sec_counter.rb +14 -0
  91. data/lib/review/template.rb +6 -0
  92. data/lib/review/textmaker.rb +29 -17
  93. data/lib/review/textutils.rb +68 -2
  94. data/lib/review/tocprinter.rb +242 -97
  95. data/lib/review/topbuilder.rb +147 -61
  96. data/lib/review/update.rb +27 -28
  97. data/lib/review/version.rb +1 -1
  98. data/lib/review/volumeprinter.rb +97 -0
  99. data/lib/review/webmaker.rb +36 -35
  100. data/lib/review/webtocprinter.rb +39 -35
  101. data/lib/review/yamlloader.rb +4 -0
  102. data/review.gemspec +8 -5
  103. data/samples/sample-book/README.md +7 -2
  104. data/samples/sample-book/src/.gitignore +154 -0
  105. data/samples/sample-book/src/config-ebook.yml +4 -0
  106. data/samples/sample-book/src/config-epub2.yml +1 -1
  107. data/samples/sample-book/src/config-jlreq-ebook.yml +4 -0
  108. data/samples/sample-book/src/config-jlreq.yml +6 -0
  109. data/samples/sample-book/src/config.yml +3 -3
  110. data/samples/sample-book/src/lib/tasks/review.rake +45 -14
  111. data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +14 -8
  112. data/samples/syntax-book/Gemfile +1 -1
  113. data/samples/syntax-book/ch01.re +3 -1
  114. data/samples/syntax-book/ch02.re +28 -21
  115. data/samples/syntax-book/ch03.re +4 -7
  116. data/samples/syntax-book/config-jlreq-lualatex.yml +4 -0
  117. data/samples/syntax-book/config-jlreq.yml +5 -0
  118. data/samples/syntax-book/config-print.yml +3 -0
  119. data/samples/syntax-book/config.yml +1 -1
  120. data/samples/syntax-book/images/img3-2.png +0 -0
  121. data/samples/syntax-book/lib/tasks/review.rake +30 -15
  122. data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +14 -8
  123. data/templates/html/_colophon.html.erb +23 -0
  124. data/templates/html/_colophon_history.html.erb +9 -0
  125. data/templates/html/_cover.html.erb +10 -0
  126. data/templates/html/_part_body.html.erb +6 -0
  127. data/templates/html/_titlepage.html.erb +20 -0
  128. data/templates/html/layout-html5.html.erb +6 -0
  129. data/templates/html/layout-xhtml1.html.erb +6 -0
  130. data/templates/latex/config.erb +45 -27
  131. data/templates/latex/layout.tex.erb +1 -0
  132. data/templates/latex/review-jlreq/README.md +3 -1
  133. data/templates/latex/review-jlreq/review-base.sty +124 -37
  134. data/templates/latex/review-jlreq/review-jlreq.cls +29 -22
  135. data/templates/latex/review-jlreq/review-style.sty +9 -1
  136. data/templates/latex/review-jlreq/review-tcbox.sty +348 -0
  137. data/templates/latex/review-jlreq/reviewmacro.sty +5 -0
  138. data/templates/latex/review-jsbook/README.md +46 -5
  139. data/templates/latex/review-jsbook/review-base.sty +111 -30
  140. data/templates/latex/review-jsbook/review-jsbook.cls +16 -2
  141. data/templates/latex/review-jsbook/review-style.sty +10 -2
  142. data/templates/latex/review-jsbook/review-tcbox.sty +348 -0
  143. data/templates/latex/review-jsbook/reviewmacro.sty +5 -0
  144. data/templates/opf/epubv2.opf.erb +7 -7
  145. data/templates/opf/epubv3.opf.erb +7 -7
  146. data/templates/opf/opf_manifest_epubv2.opf.erb +10 -0
  147. data/templates/opf/opf_manifest_epubv3.opf.erb +10 -0
  148. data/templates/opf/opf_metainfo_epubv2.opf.erb +17 -0
  149. data/templates/opf/opf_metainfo_epubv3.opf.erb +49 -0
  150. data/templates/opf/opf_tocx_epubv2.opf.erb +9 -0
  151. data/templates/opf/opf_tocx_epubv3.opf.erb +17 -0
  152. data/templates/web/html/layout-html5.html.erb +9 -8
  153. data/templates/web/html/layout-xhtml1.html.erb +6 -0
  154. data/test/assets/header_listener.html +35 -0
  155. data/test/assets/img_math/img1.png +0 -0
  156. data/test/assets/img_math/img2.png +0 -0
  157. data/test/assets/img_math/img3.png +0 -0
  158. data/test/assets/syntax_book_index_detail.txt +58 -0
  159. data/test/assets/test_template.tex +20 -9
  160. data/test/assets/test_template_backmatter.tex +20 -9
  161. data/test/book_test_helper.rb +11 -5
  162. data/test/run_test.rb +1 -1
  163. data/test/test_book.rb +123 -78
  164. data/test/test_book_chapter.rb +99 -56
  165. data/test/test_book_part.rb +3 -3
  166. data/test/test_builder.rb +24 -15
  167. data/test/test_catalog.rb +19 -42
  168. data/test/test_catalog_converter_cmd.rb +1 -1
  169. data/test/test_converter.rb +1 -0
  170. data/test/test_epub3maker.rb +170 -126
  171. data/test/test_epubmaker.rb +249 -129
  172. data/test/test_epubmaker_cmd.rb +16 -9
  173. data/test/test_helper.rb +23 -11
  174. data/test/test_htmlbuilder.rb +1083 -114
  175. data/test/test_htmlutils.rb +0 -12
  176. data/test/test_i18n.rb +37 -37
  177. data/test/test_idgxmlbuilder.rb +627 -27
  178. data/test/test_idgxmlmaker_cmd.rb +50 -0
  179. data/test/test_image_finder.rb +52 -70
  180. data/test/test_img_math.rb +111 -0
  181. data/test/test_index.rb +62 -52
  182. data/test/test_indexbuilder.rb +52 -0
  183. data/test/test_latexbuilder.rb +1189 -59
  184. data/test/test_latexbuilder_v2.rb +74 -34
  185. data/test/test_lineinput.rb +20 -93
  186. data/test/test_logger.rb +17 -4
  187. data/test/test_makerhelper.rb +2 -14
  188. data/test/test_markdownbuilder.rb +77 -4
  189. data/test/test_md2inaobuilder.rb +12 -2
  190. data/test/test_pdfmaker.rb +101 -12
  191. data/test/test_pdfmaker_cmd.rb +102 -8
  192. data/test/test_plaintextbuilder.rb +562 -31
  193. data/test/test_review_ext.rb +2 -1
  194. data/test/test_reviewheaderlistener.rb +49 -0
  195. data/test/test_rstbuilder.rb +58 -5
  196. data/test/test_sec_counter.rb +156 -0
  197. data/test/test_template.rb +12 -2
  198. data/test/test_textmaker_cmd.rb +58 -0
  199. data/test/test_textutils.rb +109 -2
  200. data/test/test_tocprinter.rb +46 -0
  201. data/test/test_topbuilder.rb +400 -17
  202. data/test/test_update.rb +53 -44
  203. data/test/test_webtocprinter.rb +75 -43
  204. data/test/test_yamlloader.rb +13 -0
  205. data/test/test_zip_exporter.rb +5 -6
  206. data/vendor/gentombow/LICENSE +1 -1
  207. data/vendor/gentombow/Makefile +0 -1
  208. data/vendor/gentombow/bounddvi-en.pdf +0 -0
  209. data/vendor/gentombow/bounddvi-en.tex +1 -0
  210. data/vendor/gentombow/bounddvi.pdf +0 -0
  211. data/vendor/gentombow/bounddvi.sty +30 -7
  212. data/vendor/gentombow/bounddvi.tex +1 -0
  213. data/vendor/gentombow/create_archive.sh +1 -0
  214. data/vendor/gentombow/gentombow-ja.pdf +0 -0
  215. data/vendor/gentombow/gentombow-ja.tex +9 -0
  216. data/vendor/gentombow/gentombow.pdf +0 -0
  217. data/vendor/gentombow/gentombow.sty +32 -10
  218. data/vendor/gentombow/gentombow.tex +8 -0
  219. data/vendor/gentombow/tests/gentombow-01-pdfx.tex +8 -0
  220. data/vendor/gentombow/tests/gentombow-02-pdfx.tex +8 -0
  221. data/vendor/jsclasses/LICENSE +1 -1
  222. data/vendor/jsclasses/Makefile +3 -2
  223. data/vendor/jsclasses/create_archive.sh +5 -5
  224. data/vendor/jsclasses/jis/Makefile +3 -2
  225. data/vendor/jsclasses/jis/jsarticle.cls +74 -31
  226. data/vendor/jsclasses/jis/jsbook.cls +74 -31
  227. data/vendor/jsclasses/jis/jsclasses.dtx +176 -36
  228. data/vendor/jsclasses/jis/jsclasses.ins +15 -5
  229. data/vendor/jsclasses/jis/jslogo.dtx +4 -4
  230. data/vendor/jsclasses/jis/jslogo.ins +9 -0
  231. data/vendor/jsclasses/jis/jslogo.sty +4 -16
  232. data/vendor/jsclasses/jis/jspf.cls +73 -30
  233. data/vendor/jsclasses/jis/jsreport.cls +74 -31
  234. data/vendor/jsclasses/jis/jsverb.ins +9 -0
  235. data/vendor/jsclasses/jis/jsverb.sty +1 -13
  236. data/vendor/jsclasses/jis/kiyou.cls +74 -31
  237. data/vendor/jsclasses/jis/minijs.sty +65 -22
  238. data/vendor/jsclasses/jis/okumacro.dtx +4 -5
  239. data/vendor/jsclasses/jis/okumacro.ins +9 -0
  240. data/vendor/jsclasses/jis/okumacro.sty +4 -17
  241. data/vendor/jsclasses/jis/okuverb.ins +9 -0
  242. data/vendor/jsclasses/jis/okuverb.sty +1 -13
  243. data/vendor/jsclasses/jis/winjis.sty +23 -19
  244. data/vendor/jsclasses/jsarticle.cls +74 -31
  245. data/vendor/jsclasses/jsbook.cls +74 -31
  246. data/vendor/jsclasses/jsclasses.dtx +176 -36
  247. data/vendor/jsclasses/jsclasses.ins +15 -5
  248. data/vendor/jsclasses/jsclasses.pdf +0 -0
  249. data/vendor/jsclasses/jslogo.dtx +4 -4
  250. data/vendor/jsclasses/jslogo.ins +9 -0
  251. data/vendor/jsclasses/jslogo.pdf +0 -0
  252. data/vendor/jsclasses/jslogo.sty +4 -16
  253. data/vendor/jsclasses/jspf.cls +73 -30
  254. data/vendor/jsclasses/jsreport.cls +74 -31
  255. data/vendor/jsclasses/jsverb.ins +9 -0
  256. data/vendor/jsclasses/jsverb.pdf +0 -0
  257. data/vendor/jsclasses/jsverb.sty +1 -13
  258. data/vendor/jsclasses/kiyou.cls +74 -31
  259. data/vendor/jsclasses/minijs.sty +68 -22
  260. data/vendor/jsclasses/okumacro.dtx +4 -5
  261. data/vendor/jsclasses/okumacro.ins +9 -0
  262. data/vendor/jsclasses/okumacro.pdf +0 -0
  263. data/vendor/jsclasses/okumacro.sty +4 -17
  264. data/vendor/jsclasses/okuverb.ins +9 -0
  265. data/vendor/jsclasses/okuverb.pdf +0 -0
  266. data/vendor/jsclasses/okuverb.sty +1 -13
  267. data/vendor/jsclasses/tests/relfont.tex +10 -0
  268. data/vendor/jsclasses/winjis.sty +23 -19
  269. metadata +136 -23
  270. data/.rubocop_todo.yml +0 -7
  271. data/lib/epubmaker.rb +0 -23
  272. data/lib/epubmaker/content.rb +0 -110
  273. data/lib/epubmaker/epubcommon.rb +0 -441
  274. data/lib/epubmaker/epubv2.rb +0 -143
  275. data/lib/epubmaker/epubv3.rb +0 -233
  276. data/lib/epubmaker/producer.rb +0 -375
  277. data/lib/epubmaker/zip_exporter.rb +0 -81
  278. data/lib/lineinput.rb +0 -155
  279. data/lib/review/book/compilable.rb +0 -173
  280. data/lib/review/tocparser.rb +0 -271
  281. data/samples/syntax-book/review-ext.rb +0 -14
  282. data/test/test_tocparser.rb +0 -25
@@ -0,0 +1,20 @@
1
+ module ReVIEW
2
+ module CallHook
3
+ def call_hook(hook_name, *params, base_dir: nil)
4
+ maker = @config.maker
5
+ filename = @config.dig(maker, hook_name)
6
+ return unless filename
7
+
8
+ hook = File.absolute_path(filename, base_dir)
9
+ @logger.debug("Call #{hook_name}. (#{hook})")
10
+
11
+ return if !File.exist?(hook) || !FileTest.executable?(hook)
12
+
13
+ if ENV['REVIEW_SAFE_MODE'].to_i & 1 > 0
14
+ warn 'hook configuration is prohibited in safe mode. ignored.'
15
+ else
16
+ system(hook, *params)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,8 +3,8 @@ require 'yaml'
3
3
  module ReVIEW
4
4
  class Catalog
5
5
  def initialize(file)
6
- if file.respond_to? :read
7
- @yaml = YAML.load(file.read)
6
+ if file.respond_to?(:read)
7
+ @yaml = YAML.safe_load(file.read, [Date])
8
8
  else ## as Object
9
9
  @yaml = file
10
10
  end
@@ -12,12 +12,11 @@ module ReVIEW
12
12
  end
13
13
 
14
14
  def predef
15
- return '' unless @yaml['PREDEF']
16
- @yaml['PREDEF'].join("\n")
15
+ @yaml['PREDEF'] || []
17
16
  end
18
17
 
19
18
  def chaps
20
- return '' unless @yaml['CHAPS']
19
+ return [] unless @yaml['CHAPS']
21
20
 
22
21
  @yaml['CHAPS'].map do |entry|
23
22
  if entry.is_a?(String)
@@ -25,11 +24,11 @@ module ReVIEW
25
24
  elsif entry.is_a?(Hash)
26
25
  entry.values # chaps in a part
27
26
  end
28
- end.flatten.join("\n")
27
+ end.flatten
29
28
  end
30
29
 
31
30
  def parts
32
- return '' unless @yaml['CHAPS']
31
+ return [] unless @yaml['CHAPS']
33
32
 
34
33
  part_list = @yaml['CHAPS'].map do |entry|
35
34
  if entry.is_a?(Hash)
@@ -37,12 +36,12 @@ module ReVIEW
37
36
  end
38
37
  end
39
38
 
40
- part_list.flatten.compact.join("\n")
39
+ part_list.flatten.compact
41
40
  end
42
41
 
43
42
  def replace_part(old_name, new_name)
44
43
  @yaml['CHAPS'].map! do |e|
45
- if e.is_a?(Hash) and (e.keys.first == old_name)
44
+ if e.is_a?(Hash) && (e.keys.first == old_name)
46
45
  e = { new_name => e.values.first }
47
46
  end
48
47
  e
@@ -51,17 +50,16 @@ module ReVIEW
51
50
 
52
51
  def parts_with_chaps
53
52
  return [] unless @yaml['CHAPS']
53
+
54
54
  @yaml['CHAPS'].flatten.compact
55
55
  end
56
56
 
57
57
  def appendix
58
- return '' unless @yaml['APPENDIX']
59
- @yaml['APPENDIX'].join("\n")
58
+ @yaml['APPENDIX'] || []
60
59
  end
61
60
 
62
61
  def postdef
63
- return '' unless @yaml['POSTDEF']
64
- @yaml['POSTDEF'].join("\n")
62
+ @yaml['POSTDEF'] || []
65
63
  end
66
64
 
67
65
  def to_s
@@ -71,7 +69,7 @@ module ReVIEW
71
69
  def validate!(config, basedir)
72
70
  filenames = []
73
71
  if predef.present?
74
- filenames.concat(predef.split(/\n/))
72
+ filenames.concat(predef)
75
73
  end
76
74
  parts_with_chaps.each do |chap|
77
75
  if chap.is_a?(Hash)
@@ -86,13 +84,13 @@ module ReVIEW
86
84
  end
87
85
  end
88
86
  if appendix.present?
89
- filenames.concat(appendix.split(/\n/))
87
+ filenames.concat(appendix)
90
88
  end
91
89
  if postdef.present?
92
- filenames.concat(postdef.split(/\n/))
90
+ filenames.concat(postdef)
93
91
  end
94
92
  filenames.each do |filename|
95
- refile = File.join(basedir, config['contentdir'], filename)
93
+ refile = File.expand_path(File.join(config['contentdir'], filename), basedir)
96
94
  unless File.exist?(refile)
97
95
  raise FileNotFound, "file not found in catalog.yml: #{refile}"
98
96
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2019 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2009-2020 Minero Aoki, Kenshi Muto
2
2
  # Copyright (c) 2002-2007 Minero Aoki
3
3
  #
4
4
  # This program is free software.
@@ -14,16 +14,37 @@ require 'strscan'
14
14
 
15
15
  module ReVIEW
16
16
  class Compiler
17
- def initialize(strategy)
18
- @strategy = strategy
17
+ MAX_HEADLINE_LEVEL = 6
18
+
19
+ def initialize(builder)
20
+ @builder = builder
21
+
22
+ ## commands which do not parse block lines in compiler
23
+ @non_parsed_commands = %i[embed texequation graph]
24
+
25
+ ## to decide escaping/non-escaping for text
26
+ @command_name_stack = []
19
27
  end
20
28
 
21
- attr_reader :strategy
29
+ attr_reader :builder, :previous_list_type
30
+
31
+ def strategy
32
+ error 'Compiler#strategy is obsoleted. Use Compiler#builder.'
33
+ @builder
34
+ end
35
+
36
+ def non_escaped_commands
37
+ if @builder.highlight?
38
+ %i[list emlist listnum emlistnum cmd]
39
+ else
40
+ []
41
+ end
42
+ end
22
43
 
23
44
  def compile(chap)
24
45
  @chapter = chap
25
46
  do_compile
26
- @strategy.result
47
+ @builder.result
27
48
  end
28
49
 
29
50
  class SyntaxElement
@@ -40,6 +61,7 @@ module ReVIEW
40
61
  unless @argc_spec === args.size
41
62
  raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
42
63
  end
64
+
43
65
  if @checker
44
66
  @checker.call(*args)
45
67
  end
@@ -54,23 +76,31 @@ module ReVIEW
54
76
  end
55
77
  end
56
78
 
79
+ def minicolumn?
80
+ @type == :minicolumn
81
+ end
82
+
57
83
  def block_required?
58
- @type == :block
84
+ @type == :block or @type == :minicolumn
59
85
  end
60
86
 
61
87
  def block_allowed?
62
- @type == :block or @type == :optional
88
+ @type == :block or @type == :optional or @type == :minicolumn
63
89
  end
64
90
  end
65
91
 
66
92
  SYNTAX = {}
67
93
 
68
94
  def self.defblock(name, argc, optional = false, &block)
69
- defsyntax name, (optional ? :optional : :block), argc, &block
95
+ defsyntax(name, (optional ? :optional : :block), argc, &block)
96
+ end
97
+
98
+ def self.defminicolumn(name, argc, _optional = false, &block)
99
+ defsyntax(name, :minicolumn, argc, &block)
70
100
  end
71
101
 
72
102
  def self.defsingle(name, argc, &block)
73
- defsyntax name, :line, argc, &block
103
+ defsyntax(name, :line, argc, &block)
74
104
  end
75
105
 
76
106
  def self.defsyntax(name, type, argc, &block)
@@ -81,6 +111,16 @@ module ReVIEW
81
111
  INLINE[name] = InlineSyntaxElement.new(name)
82
112
  end
83
113
 
114
+ def self.minicolumn_names
115
+ buf = []
116
+ SYNTAX.each do |name, syntax|
117
+ if syntax.minicolumn?
118
+ buf << name.to_s
119
+ end
120
+ end
121
+ buf
122
+ end
123
+
84
124
  def syntax_defined?(name)
85
125
  SYNTAX.key?(name.to_sym)
86
126
  end
@@ -109,7 +149,7 @@ module ReVIEW
109
149
  defblock :emlist, 0..2
110
150
  defblock :cmd, 0..1
111
151
  defblock :table, 0..2
112
- defblock :imgtable, 0..2
152
+ defblock :imgtable, 0..3
113
153
  defblock :emtable, 0..1
114
154
  defblock :quote, 0
115
155
  defblock :image, 2..3, true
@@ -129,18 +169,19 @@ module ReVIEW
129
169
  defblock :bpo, 0
130
170
  defblock :flushright, 0
131
171
  defblock :centering, 0
132
- defblock :note, 0..1
133
- defblock :memo, 0..1
134
- defblock :info, 0..1
135
- defblock :important, 0..1
136
- defblock :caution, 0..1
137
- defblock :notice, 0..1
138
- defblock :warning, 0..1
139
- defblock :tip, 0..1
140
172
  defblock :box, 0..1
141
173
  defblock :comment, 0..1, true
142
174
  defblock :embed, 0..1
143
175
 
176
+ defminicolumn :note, 0..1
177
+ defminicolumn :memo, 0..1
178
+ defminicolumn :tip, 0..1
179
+ defminicolumn :info, 0..1
180
+ defminicolumn :warning, 0..1
181
+ defminicolumn :important, 0..1
182
+ defminicolumn :caution, 0..1
183
+ defminicolumn :notice, 0..1
184
+
144
185
  defsingle :footnote, 2
145
186
  defsingle :noindent, 0
146
187
  defsingle :blankline, 0
@@ -153,6 +194,8 @@ module ReVIEW
153
194
  defsingle :include, 1
154
195
  defsingle :olnum, 1
155
196
  defsingle :firstlinenum, 1
197
+ defsingle :beginchild, 0
198
+ defsingle :endchild, 0
156
199
 
157
200
  definline :chapref
158
201
  definline :chap
@@ -217,32 +260,62 @@ module ReVIEW
217
260
 
218
261
  def do_compile
219
262
  f = LineInput.new(StringIO.new(@chapter.content))
220
- @strategy.bind self, @chapter, Location.new(@chapter.basename, f)
263
+ @builder.bind(self, @chapter, Location.new(@chapter.basename, f))
264
+
265
+ ## in minicolumn, such as note/info/alert...
266
+ @minicolumn_name = nil
267
+
221
268
  tagged_section_init
222
269
  while f.next?
223
270
  case f.peek
224
271
  when /\A\#@/
225
272
  f.gets # Nothing to do
226
- when /\A=+[\[\s\{]/
227
- compile_headline f.gets
273
+ when /\A=+[\[\s{]/
274
+ compile_headline(f.gets)
275
+ @builder.previous_list_type = nil
228
276
  when /\A\s+\*/
229
- compile_ulist f
277
+ compile_ulist(f)
278
+ @builder.previous_list_type = 'ul'
230
279
  when /\A\s+\d+\./
231
- compile_olist f
280
+ compile_olist(f)
281
+ @builder.previous_list_type = 'ol'
282
+ when /\A\s+:\s/
283
+ compile_dlist(f)
284
+ @builder.previous_list_type = 'dl'
232
285
  when /\A\s*:\s/
233
- compile_dlist f
286
+ warn 'Definition list starting with `:` is deprecated. It should start with ` : `.'
287
+ compile_dlist(f)
288
+ @builder.previous_list_type = 'dl'
234
289
  when %r{\A//\}}
235
- f.gets
236
- error 'block end seen but not opened'
290
+ if in_minicolumn?
291
+ _line = f.gets
292
+ compile_minicolumn_end
293
+ else
294
+ f.gets
295
+ error 'block end seen but not opened'
296
+ end
237
297
  when %r{\A//[a-z]+}
238
- name, args, lines = read_command(f)
239
- syntax = syntax_descriptor(name)
240
- unless syntax
241
- error "unknown command: //#{name}"
242
- compile_unknown_command args, lines
243
- next
298
+ line = f.peek
299
+ matched = line =~ %r|\A//([a-z]+)(:?\[.*\])?{\s*$|
300
+ if matched && minicolumn_block_name?($1)
301
+ line = f.gets
302
+ name = $1
303
+ args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name)
304
+ compile_minicolumn_begin(name, *args)
305
+ else
306
+ # @command_name_stack.push(name) ## <- move into read_command() to use name
307
+ name, args, lines = read_command(f)
308
+ syntax = syntax_descriptor(name)
309
+ unless syntax
310
+ error "unknown command: //#{name}"
311
+ compile_unknown_command(args, lines)
312
+ @command_name_stack.pop
313
+ next
314
+ end
315
+ compile_command(syntax, args, lines)
316
+ @command_name_stack.pop
244
317
  end
245
- compile_command syntax, args, lines
318
+ @builder.previous_list_type = nil
246
319
  when %r{\A//}
247
320
  line = f.gets
248
321
  warn "`//' seen but is not valid command: #{line.strip.inspect}"
@@ -250,39 +323,74 @@ module ReVIEW
250
323
  warn 'skipping block...'
251
324
  read_block(f, false)
252
325
  end
326
+ @builder.previous_list_type = nil
253
327
  else
254
328
  if f.peek.strip.empty?
255
329
  f.gets
256
330
  next
257
331
  end
258
- compile_paragraph f
332
+ compile_paragraph(f)
333
+ @builder.previous_list_type = nil
259
334
  end
260
335
  end
261
336
  close_all_tagged_section
337
+ rescue SyntaxError => e
338
+ error e
339
+ end
340
+
341
+ def compile_minicolumn_begin(name, caption = nil)
342
+ mid = "#{name}_begin"
343
+ unless @builder.respond_to?(mid)
344
+ error "strategy does not support minicolumn: #{name}"
345
+ end
346
+
347
+ if @minicolumn_name
348
+ error "minicolumn cannot be nested: #{name}"
349
+ return
350
+ end
351
+ @minicolumn_name = name
352
+
353
+ @builder.__send__(mid, caption)
354
+ end
355
+
356
+ def compile_minicolumn_end
357
+ unless @minicolumn_name
358
+ error "minicolumn is not used: #{name}"
359
+ return
360
+ end
361
+ name = @minicolumn_name
362
+
363
+ mid = "#{name}_end"
364
+ @builder.__send__(mid)
365
+ @minicolumn_name = nil
262
366
  end
263
367
 
264
368
  def compile_headline(line)
265
369
  @headline_indexs ||= [@chapter.number.to_i - 1]
266
370
  m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line)
267
371
  level = m[1].size
372
+ if level > MAX_HEADLINE_LEVEL
373
+ raise CompileError, "Invalid header: max headline level is #{MAX_HEADLINE_LEVEL}"
374
+ end
375
+
268
376
  tag = m[2]
269
377
  label = m[3]
270
378
  caption = m[4].strip
271
379
  index = level - 1
272
380
  if tag
273
- if tag !~ %r{\A/}
274
- if caption.empty?
275
- warn 'headline is empty.'
276
- end
277
- close_current_tagged_section(level)
278
- open_tagged_section(tag, level, label, caption)
279
- else
381
+ if tag.start_with?('/')
280
382
  open_tag = tag[1..-1]
281
383
  prev_tag_info = @tagged_section.pop
282
384
  if prev_tag_info.nil? || prev_tag_info.first != open_tag
283
385
  error "#{open_tag} is not opened."
284
386
  end
285
387
  close_tagged_section(*prev_tag_info)
388
+ else
389
+ if caption.empty?
390
+ warn 'headline is empty.'
391
+ end
392
+ close_current_tagged_section(level)
393
+ open_tagged_section(tag, level, label, caption)
286
394
  end
287
395
  else
288
396
  if caption.empty?
@@ -296,18 +404,18 @@ module ReVIEW
296
404
  end
297
405
  @headline_indexs[index] += 1
298
406
  close_current_tagged_section(level)
299
- @strategy.headline level, label, caption
407
+ @builder.headline(level, label, caption)
300
408
  end
301
409
  end
302
410
 
303
411
  def close_current_tagged_section(level)
304
- while @tagged_section.last and @tagged_section.last[1] >= level
412
+ while @tagged_section.last && (@tagged_section.last[1] >= level)
305
413
  close_tagged_section(* @tagged_section.pop)
306
414
  end
307
415
  end
308
416
 
309
417
  def headline(level, label, caption)
310
- @strategy.headline level, label, caption
418
+ @builder.headline(level, label, caption)
311
419
  end
312
420
 
313
421
  def tagged_section_init
@@ -316,21 +424,21 @@ module ReVIEW
316
424
 
317
425
  def open_tagged_section(tag, level, label, caption)
318
426
  mid = "#{tag}_begin"
319
- unless @strategy.respond_to?(mid)
320
- error "strategy does not support tagged section: #{tag}"
321
- headline level, label, caption
427
+ unless @builder.respond_to?(mid)
428
+ error "builder does not support tagged section: #{tag}"
429
+ headline(level, label, caption)
322
430
  return
323
431
  end
324
- @tagged_section.push [tag, level]
325
- @strategy.__send__ mid, level, label, caption
432
+ @tagged_section.push([tag, level])
433
+ @builder.__send__(mid, level, label, caption)
326
434
  end
327
435
 
328
436
  def close_tagged_section(tag, level)
329
437
  mid = "#{tag}_end"
330
- if @strategy.respond_to?(mid)
331
- @strategy.__send__ mid, level
438
+ if @builder.respond_to?(mid)
439
+ @builder.__send__(mid, level)
332
440
  else
333
- error "strategy does not support block op: #{mid}"
441
+ error "builder does not support block op: #{mid}"
334
442
  end
335
443
  end
336
444
 
@@ -347,86 +455,94 @@ module ReVIEW
347
455
 
348
456
  buf = [text(line.sub(/\*+/, '').strip)]
349
457
  f.while_match(/\A\s+(?!\*)\S/) do |cont|
350
- buf.push text(cont.strip)
458
+ buf.push(text(cont.strip))
351
459
  end
352
460
 
353
461
  line =~ /\A\s+(\*+)/
354
462
  current_level = $1.size
355
463
  if level == current_level
356
- @strategy.ul_item_end
464
+ @builder.ul_item_end
357
465
  # body
358
- @strategy.ul_item_begin buf
466
+ @builder.ul_item_begin(buf)
359
467
  elsif level < current_level # down
360
468
  level_diff = current_level - level
361
469
  if level_diff != 1
362
470
  error 'too many *.'
363
471
  end
364
472
  level = current_level
365
- @strategy.ul_begin { level }
366
- @strategy.ul_item_begin buf
473
+ @builder.ul_begin { level }
474
+ @builder.ul_item_begin(buf)
367
475
  elsif level > current_level # up
368
476
  level_diff = level - current_level
369
477
  level = current_level
370
478
  (1..level_diff).to_a.reverse_each do |i|
371
- @strategy.ul_item_end
372
- @strategy.ul_end { level + i }
479
+ @builder.ul_item_end
480
+ @builder.ul_end { level + i }
373
481
  end
374
- @strategy.ul_item_end
482
+ @builder.ul_item_end
375
483
  # body
376
- @strategy.ul_item_begin buf
484
+ @builder.ul_item_begin(buf)
377
485
  end
378
486
  end
379
487
 
380
488
  (1..level).to_a.reverse_each do |i|
381
- @strategy.ul_item_end
382
- @strategy.ul_end { i }
489
+ @builder.ul_item_end
490
+ @builder.ul_end { i }
383
491
  end
384
492
  end
385
493
 
386
494
  def compile_olist(f)
387
- @strategy.ol_begin
495
+ @builder.ol_begin
388
496
  f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
389
497
  next if line =~ /\A\#@/
390
498
 
391
499
  num = line.match(/(\d+)\./)[1]
392
500
  buf = [text(line.sub(/\d+\./, '').strip)]
393
501
  f.while_match(/\A\s+(?!\d+\.)\S/) do |cont|
394
- buf.push text(cont.strip)
502
+ buf.push(text(cont.strip))
395
503
  end
396
- @strategy.ol_item buf, num
504
+ @builder.ol_item(buf, num)
397
505
  end
398
- @strategy.ol_end
506
+ @builder.ol_end
399
507
  end
400
508
 
401
509
  def compile_dlist(f)
402
- @strategy.dl_begin
510
+ @builder.dl_begin
403
511
  while /\A\s*:/ =~ f.peek
404
- @strategy.dt text(f.gets.sub(/\A\s*:/, '').strip)
405
- desc = f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) }
406
- @strategy.dd(desc)
512
+ # defer compile_inline to handle footnotes
513
+ @builder.doc_status[:dt] = true
514
+ @builder.dt(text(f.gets.sub(/\A\s*:/, '').strip))
515
+ @builder.doc_status[:dt] = nil
516
+ desc = []
517
+ f.until_match(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/) do |line|
518
+ desc << text(line.strip)
519
+ end
520
+ @builder.dd(desc)
407
521
  f.skip_blank_lines
408
522
  f.skip_comment_lines
409
523
  end
410
- @strategy.dl_end
524
+ @builder.dl_end
411
525
  end
412
526
 
413
527
  def compile_paragraph(f)
414
528
  buf = []
415
529
  f.until_match(%r{\A//|\A\#@}) do |line|
416
530
  break if line.strip.empty?
417
- buf.push text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t"))
531
+
532
+ buf.push(text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t")))
418
533
  end
419
- @strategy.paragraph buf
534
+ @builder.paragraph(buf)
420
535
  end
421
536
 
422
537
  def read_command(f)
423
538
  line = f.gets
424
539
  name = line.slice(/[a-z]+/).to_sym
425
- ignore_inline = (name == :embed)
540
+ ignore_inline = @non_parsed_commands.include?(name)
541
+ @command_name_stack.push(name)
426
542
  args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name)
427
- @strategy.doc_status[name] = true
543
+ @builder.doc_status[name] = true
428
544
  lines = block_open?(line) ? read_block(f, ignore_inline) : nil
429
- @strategy.doc_status[name] = nil
545
+ @builder.doc_status[name] = nil
430
546
  [name, args, lines]
431
547
  end
432
548
 
@@ -439,12 +555,12 @@ module ReVIEW
439
555
  buf = []
440
556
  f.until_match(%r{\A//\}}) do |line|
441
557
  if ignore_inline
442
- buf.push line
558
+ buf.push(line.chomp)
443
559
  elsif line !~ /\A\#@/
444
- buf.push text(line.rstrip)
560
+ buf.push(text(line.rstrip, true))
445
561
  end
446
562
  end
447
- unless %r{\A//\}} =~ f.peek
563
+ unless f.peek.to_s.start_with?('//}')
448
564
  error "unexpected EOF (block begins at: #{head})"
449
565
  return buf
450
566
  end
@@ -454,6 +570,7 @@ module ReVIEW
454
570
 
455
571
  def parse_args(str, _name = nil)
456
572
  return [] if str.empty?
573
+
457
574
  scanner = StringScanner.new(str)
458
575
  words = []
459
576
  while word = scanner.scan(/(\[\]|\[.*?[^\\]\])/)
@@ -471,33 +588,33 @@ module ReVIEW
471
588
  end
472
589
 
473
590
  def compile_command(syntax, args, lines)
474
- unless @strategy.respond_to?(syntax.name)
475
- error "strategy does not support command: //#{syntax.name}"
476
- compile_unknown_command args, lines
591
+ unless @builder.respond_to?(syntax.name)
592
+ error "builder does not support command: //#{syntax.name}"
593
+ compile_unknown_command(args, lines)
477
594
  return
478
595
  end
479
596
  begin
480
- syntax.check_args args
597
+ syntax.check_args(args)
481
598
  rescue CompileError => e
482
599
  error e.message
483
600
  args = ['(NoArgument)'] * syntax.min_argc
484
601
  end
485
602
  if syntax.block_allowed?
486
- compile_block syntax, args, lines
603
+ compile_block(syntax, args, lines)
487
604
  else
488
605
  if lines
489
606
  error "block is not allowed for command //#{syntax.name}; ignore"
490
607
  end
491
- compile_single syntax, args
608
+ compile_single(syntax, args)
492
609
  end
493
610
  end
494
611
 
495
612
  def compile_unknown_command(args, lines)
496
- @strategy.unknown_command args, lines
613
+ @builder.unknown_command(args, lines)
497
614
  end
498
615
 
499
616
  def compile_block(syntax, args, lines)
500
- @strategy.__send__(syntax.name, (lines || default_block(syntax)), *args)
617
+ @builder.__send__(syntax.name, (lines || default_block(syntax)), *args)
501
618
  end
502
619
 
503
620
  def default_block(syntax)
@@ -508,7 +625,7 @@ module ReVIEW
508
625
  end
509
626
 
510
627
  def compile_single(syntax, args)
511
- @strategy.__send__(syntax.name, *args)
628
+ @builder.__send__(syntax.name, *args)
512
629
  end
513
630
 
514
631
  def replace_fence(str)
@@ -518,54 +635,75 @@ module ReVIEW
518
635
  if arg =~ /[\x01\x02\x03\x04]/
519
636
  error "invalid character in '#{str}'"
520
637
  end
521
- replaced = arg.gsub('@', "\x01").gsub('\\', "\x02").gsub('{', "\x03").gsub('}', "\x04")
638
+ replaced = arg.tr('@', "\x01").tr('\\', "\x02").tr('{', "\x03").tr('}', "\x04")
522
639
  "@<#{op}>{#{replaced}}"
523
640
  end
524
641
  end
525
642
 
526
643
  def revert_replace_fence(str)
527
- str.gsub("\x01", '@').gsub("\x02", '\\').gsub("\x03", '{').gsub("\x04", '}')
644
+ str.tr("\x01", '@').tr("\x02", '\\').tr("\x03", '{').tr("\x04", '}')
645
+ end
646
+
647
+ def in_non_escaped_command?
648
+ current_command = @command_name_stack.last
649
+ current_command && non_escaped_commands.include?(current_command)
528
650
  end
529
651
 
530
- def text(str)
652
+ def text(str, block_mode = false)
531
653
  return '' if str.empty?
532
- words = replace_fence(str).split(/(@<\w+>\{(?:[^\}\\]|\\.)*?\})/, -1)
654
+
655
+ words = replace_fence(str).split(/(@<\w+>\{(?:[^}\\]|\\.)*?\})/, -1)
533
656
  words.each do |w|
534
657
  if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
535
658
  error "`@<xxx>' seen but is not valid inline op: #{w}"
536
659
  end
537
660
  end
538
- result = @strategy.nofunc_text(revert_replace_fence(words.shift))
661
+ result = ''
539
662
  until words.empty?
663
+ if in_non_escaped_command? && block_mode
664
+ result << revert_replace_fence(words.shift)
665
+ else
666
+ result << @builder.nofunc_text(revert_replace_fence(words.shift))
667
+ end
668
+ break if words.empty?
669
+
540
670
  result << compile_inline(revert_replace_fence(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\')))
541
- result << @strategy.nofunc_text(revert_replace_fence(words.shift))
542
671
  end
543
672
  result
544
673
  rescue => e
545
674
  error e.message
546
675
  end
547
- public :text # called from strategy
676
+ public :text # called from builder
548
677
 
549
678
  def compile_inline(str)
550
679
  op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures
551
680
  unless inline_defined?(op)
552
681
  raise CompileError, "no such inline op: #{op}"
553
682
  end
554
- unless @strategy.respond_to?("inline_#{op}")
555
- raise "strategy does not support inline op: @<#{op}>"
683
+ unless @builder.respond_to?("inline_#{op}")
684
+ raise "builder does not support inline op: @<#{op}>"
556
685
  end
557
- @strategy.__send__("inline_#{op}", arg)
686
+
687
+ @builder.__send__("inline_#{op}", arg)
558
688
  rescue => e
559
689
  error e.message
560
- @strategy.nofunc_text(str)
690
+ @builder.nofunc_text(str)
691
+ end
692
+
693
+ def in_minicolumn?
694
+ @builder.in_minicolumn?
695
+ end
696
+
697
+ def minicolumn_block_name?(name)
698
+ @builder.minicolumn_block_name?(name)
561
699
  end
562
700
 
563
701
  def warn(msg)
564
- @strategy.warn msg
702
+ @builder.warn msg
565
703
  end
566
704
 
567
705
  def error(msg)
568
- @strategy.error msg
706
+ @builder.error msg
569
707
  end
570
708
  end
571
709
  end # module ReVIEW