review 4.1.0 → 5.2.0

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