review 3.1.0 → 5.0.0

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