review 4.2.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-win.yml +11 -5
  3. data/.rubocop.yml +113 -24
  4. data/NEWS.ja.md +94 -0
  5. data/NEWS.md +94 -0
  6. data/bin/review-catalog-converter +1 -1
  7. data/bin/review-check +2 -4
  8. data/bin/review-checkdep +1 -1
  9. data/bin/review-compile +8 -14
  10. data/bin/review-validate +1 -1
  11. data/doc/config.yml.sample +4 -1
  12. data/doc/config.yml.sample-simple +1 -1
  13. data/doc/format.ja.md +83 -4
  14. data/doc/format.md +84 -7
  15. data/doc/makeindex.ja.md +2 -2
  16. data/doc/quickstart.ja.md +3 -3
  17. data/doc/quickstart.md +2 -2
  18. data/lib/epubmaker/content.rb +3 -2
  19. data/lib/epubmaker/epubcommon.rb +33 -25
  20. data/lib/epubmaker/epubv2.rb +5 -6
  21. data/lib/epubmaker/epubv3.rb +20 -18
  22. data/lib/review/book.rb +2 -2
  23. data/lib/review/book/base.rb +61 -25
  24. data/lib/review/book/bib.rb +21 -0
  25. data/lib/review/book/book_unit.rb +155 -0
  26. data/lib/review/book/chapter.rb +30 -26
  27. data/lib/review/book/index.rb +23 -185
  28. data/lib/review/book/index/item.rb +7 -1
  29. data/lib/review/book/part.rb +21 -9
  30. data/lib/review/book/volume.rb +1 -1
  31. data/lib/review/builder.rb +68 -13
  32. data/lib/review/catalog.rb +2 -2
  33. data/lib/review/compiler.rb +159 -73
  34. data/lib/review/configure.rb +22 -1
  35. data/lib/review/converter.rb +1 -1
  36. data/lib/review/epub2html.rb +6 -1
  37. data/lib/review/epubmaker.rb +12 -23
  38. data/lib/review/htmlbuilder.rb +36 -7
  39. data/lib/review/htmlutils.rb +7 -10
  40. data/lib/review/i18n.rb +1 -1
  41. data/lib/review/idgxmlbuilder.rb +51 -6
  42. data/lib/review/idgxmlmaker.rb +9 -14
  43. data/lib/review/index_builder.rb +653 -0
  44. data/lib/review/init.rb +5 -13
  45. data/lib/review/latexbuilder.rb +65 -4
  46. data/lib/review/logger.rb +2 -2
  47. data/lib/review/makerhelper.rb +11 -0
  48. data/lib/review/markdownbuilder.rb +19 -1
  49. data/lib/review/pdfmaker.rb +17 -36
  50. data/lib/review/plaintextbuilder.rb +48 -2
  51. data/lib/review/preprocessor.rb +5 -5
  52. data/lib/review/rstbuilder.rb +25 -6
  53. data/lib/review/sec_counter.rb +13 -0
  54. data/lib/review/textmaker.rb +4 -12
  55. data/lib/review/tocprinter.rb +2 -3
  56. data/lib/review/topbuilder.rb +26 -0
  57. data/lib/review/update.rb +7 -7
  58. data/lib/review/version.rb +1 -1
  59. data/lib/review/volumeprinter.rb +2 -3
  60. data/lib/review/webmaker.rb +9 -20
  61. data/review.gemspec +2 -2
  62. data/samples/sample-book/src/config.yml +1 -1
  63. data/samples/syntax-book/Gemfile +1 -1
  64. data/samples/syntax-book/config.yml +1 -1
  65. data/templates/latex/config.erb +27 -23
  66. data/templates/latex/review-jlreq/README.md +3 -1
  67. data/templates/latex/review-jlreq/review-base.sty +31 -15
  68. data/templates/latex/review-jlreq/review-jlreq.cls +8 -24
  69. data/templates/latex/review-jsbook/README.md +7 -5
  70. data/templates/latex/review-jsbook/review-base.sty +34 -16
  71. data/templates/latex/review-jsbook/review-jsbook.cls +4 -2
  72. data/templates/web/html/layout-html5.html.erb +1 -1
  73. data/test/assets/test_template.tex +3 -3
  74. data/test/assets/test_template_backmatter.tex +3 -3
  75. data/test/book_test_helper.rb +11 -5
  76. data/test/test_book.rb +54 -63
  77. data/test/test_book_chapter.rb +93 -52
  78. data/test/test_builder.rb +24 -15
  79. data/test/test_converter.rb +1 -0
  80. data/test/test_epub3maker.rb +2 -2
  81. data/test/test_epubmaker.rb +8 -0
  82. data/test/test_helper.rb +4 -1
  83. data/test/test_htmlbuilder.rb +627 -56
  84. data/test/test_htmlutils.rb +0 -12
  85. data/test/test_i18n.rb +33 -33
  86. data/test/test_idgxmlbuilder.rb +358 -11
  87. data/test/test_idgxmlmaker_cmd.rb +1 -1
  88. data/test/test_index.rb +62 -52
  89. data/test/test_indexbuilder.rb +52 -0
  90. data/test/test_latexbuilder.rb +547 -10
  91. data/test/test_latexbuilder_v2.rb +43 -5
  92. data/test/test_logger.rb +7 -7
  93. data/test/test_makerhelper.rb +0 -12
  94. data/test/test_markdownbuilder.rb +3 -0
  95. data/test/test_pdfmaker.rb +13 -12
  96. data/test/test_pdfmaker_cmd.rb +1 -1
  97. data/test/test_plaintextbuilder.rb +422 -7
  98. data/test/test_review_ext.rb +2 -1
  99. data/test/test_rstbuilder.rb +25 -1
  100. data/test/test_sec_counter.rb +156 -0
  101. data/test/test_textmaker_cmd.rb +1 -1
  102. data/test/test_topbuilder.rb +187 -10
  103. data/test/test_update.rb +10 -10
  104. data/test/test_webtocprinter.rb +12 -12
  105. data/vendor/gentombow/LICENSE +1 -1
  106. data/vendor/gentombow/Makefile +0 -1
  107. data/vendor/gentombow/bounddvi-en.pdf +0 -0
  108. data/vendor/gentombow/bounddvi-en.tex +1 -0
  109. data/vendor/gentombow/bounddvi.pdf +0 -0
  110. data/vendor/gentombow/bounddvi.sty +30 -7
  111. data/vendor/gentombow/bounddvi.tex +1 -0
  112. data/vendor/gentombow/create_archive.sh +1 -0
  113. data/vendor/gentombow/gentombow-ja.pdf +0 -0
  114. data/vendor/gentombow/gentombow-ja.tex +9 -0
  115. data/vendor/gentombow/gentombow.pdf +0 -0
  116. data/vendor/gentombow/gentombow.sty +32 -10
  117. data/vendor/gentombow/gentombow.tex +8 -0
  118. data/vendor/gentombow/tests/gentombow-01-pdfx.tex +8 -0
  119. data/vendor/gentombow/tests/gentombow-02-pdfx.tex +8 -0
  120. data/vendor/jsclasses/Makefile +3 -2
  121. data/vendor/jsclasses/create_archive.sh +5 -5
  122. data/vendor/jsclasses/jis/Makefile +3 -2
  123. data/vendor/jsclasses/jis/jsarticle.cls +22 -18
  124. data/vendor/jsclasses/jis/jsbook.cls +22 -18
  125. data/vendor/jsclasses/jis/jsclasses.dtx +94 -13
  126. data/vendor/jsclasses/jis/jsclasses.ins +15 -5
  127. data/vendor/jsclasses/jis/jslogo.ins +9 -0
  128. data/vendor/jsclasses/jis/jslogo.sty +1 -13
  129. data/vendor/jsclasses/jis/jspf.cls +22 -18
  130. data/vendor/jsclasses/jis/jsreport.cls +22 -18
  131. data/vendor/jsclasses/jis/jsverb.ins +9 -0
  132. data/vendor/jsclasses/jis/jsverb.sty +1 -13
  133. data/vendor/jsclasses/jis/kiyou.cls +22 -18
  134. data/vendor/jsclasses/jis/minijs.sty +65 -22
  135. data/vendor/jsclasses/jis/okumacro.ins +9 -0
  136. data/vendor/jsclasses/jis/okumacro.sty +1 -13
  137. data/vendor/jsclasses/jis/okuverb.ins +9 -0
  138. data/vendor/jsclasses/jis/okuverb.sty +1 -13
  139. data/vendor/jsclasses/jis/winjis.sty +23 -19
  140. data/vendor/jsclasses/jsarticle.cls +22 -18
  141. data/vendor/jsclasses/jsbook.cls +22 -18
  142. data/vendor/jsclasses/jsclasses.dtx +94 -13
  143. data/vendor/jsclasses/jsclasses.ins +15 -5
  144. data/vendor/jsclasses/jsclasses.pdf +0 -0
  145. data/vendor/jsclasses/jslogo.ins +9 -0
  146. data/vendor/jsclasses/jslogo.pdf +0 -0
  147. data/vendor/jsclasses/jslogo.sty +1 -13
  148. data/vendor/jsclasses/jspf.cls +22 -18
  149. data/vendor/jsclasses/jsreport.cls +22 -18
  150. data/vendor/jsclasses/jsverb.ins +9 -0
  151. data/vendor/jsclasses/jsverb.pdf +0 -0
  152. data/vendor/jsclasses/jsverb.sty +1 -13
  153. data/vendor/jsclasses/kiyou.cls +22 -18
  154. data/vendor/jsclasses/minijs.sty +68 -22
  155. data/vendor/jsclasses/okumacro.ins +9 -0
  156. data/vendor/jsclasses/okumacro.pdf +0 -0
  157. data/vendor/jsclasses/okumacro.sty +1 -13
  158. data/vendor/jsclasses/okuverb.ins +9 -0
  159. data/vendor/jsclasses/okuverb.pdf +0 -0
  160. data/vendor/jsclasses/okuverb.sty +1 -13
  161. data/vendor/jsclasses/tests/relfont.tex +10 -0
  162. data/vendor/jsclasses/winjis.sty +23 -19
  163. metadata +14 -6
  164. data/.rubocop_todo.yml +0 -7
  165. data/lib/review/book/compilable.rb +0 -174
@@ -32,7 +32,13 @@ module ReVIEW
32
32
  alias_method :content, :caption
33
33
 
34
34
  def path
35
- @path ||= @index.find_path(id)
35
+ if @path
36
+ return @path
37
+ end
38
+
39
+ @path = @index.find_path(@id)
40
+
41
+ @path
36
42
  end
37
43
  end
38
44
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2019 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2009-2020 Minero Aoki, Kenshi Muto, Masayoshi Takahashi
2
2
  # 2002-2008 Minero Aoki
3
3
  #
4
4
  # This program is free software.
@@ -6,13 +6,11 @@
6
6
  # the GNU LGPL, Lesser General Public License version 2.1.
7
7
  # For details of the GNU LGPL, see the file "COPYING".
8
8
  #
9
- require 'review/book/compilable'
9
+ require 'review/book/book_unit'
10
10
 
11
11
  module ReVIEW
12
12
  module Book
13
- class Part
14
- include Compilable
15
-
13
+ class Part < BookUnit
16
14
  def self.mkpart_from_namelistfile(book, path)
17
15
  chaps = []
18
16
  File.read(path, mode: 'rt:BOM|utf-8').split.each_with_index do |name, number|
@@ -30,7 +28,7 @@ module ReVIEW
30
28
  end
31
29
 
32
30
  def self.mkpart(chaps)
33
- chaps.empty? ? nil : Part.new(self, nil, chaps)
31
+ chaps.empty? ? nil : Part.new(chaps[0].book, nil, chaps)
34
32
  end
35
33
 
36
34
  # if Part is dummy, `number` is nil.
@@ -38,15 +36,16 @@ module ReVIEW
38
36
  def initialize(book, number, chapters, name = '', io = nil)
39
37
  @book = book
40
38
  @number = number
41
- @chapters = chapters
42
39
  @name = name
40
+ @chapters = chapters
43
41
  @path = name
44
- @content = ''
45
42
  if io
46
43
  @content = io.read
47
44
  elsif @path.present? && File.exist?(File.join(@book.config['contentdir'], @path))
48
45
  @content = File.read(File.join(@book.config['contentdir'], @path), mode: 'rt:BOM|utf-8')
49
- @name = File.basename(@name, '.re')
46
+ @name = File.basename(name, '.re')
47
+ else
48
+ @content = ''
50
49
  end
51
50
  if file?
52
51
  @title = nil
@@ -54,6 +53,19 @@ module ReVIEW
54
53
  @title = name
55
54
  end
56
55
  @volume = nil
56
+
57
+ super()
58
+ end
59
+
60
+ def generate_indexes
61
+ super
62
+
63
+ return unless content
64
+
65
+ @numberless_image_index = @indexes.numberless_image_index
66
+ @image_index = @indexes.image_index
67
+ @icon_index = @indexes.icon_index
68
+ @indepimage_index = @indexes.indepimage_index
57
69
  end
58
70
 
59
71
  attr_reader :number
@@ -22,7 +22,7 @@ module ReVIEW
22
22
  end
23
23
 
24
24
  def self.sum(vols)
25
- vols.inject(new) { |sum, i| sum + i }
25
+ vols.inject(new) { |sum, i| sum + i } # rubocop:disable Performance/Sum
26
26
  end
27
27
 
28
28
  def self.dummy
@@ -11,7 +11,6 @@ require 'review/textutils'
11
11
  require 'review/compiler'
12
12
  require 'review/sec_counter'
13
13
  require 'stringio'
14
- require 'cgi'
15
14
  require 'fileutils'
16
15
  require 'tempfile'
17
16
  require 'csv'
@@ -20,7 +19,7 @@ module ReVIEW
20
19
  class Builder
21
20
  include TextUtils
22
21
 
23
- CAPTION_TITLES = %w[note memo tip info warning important caution notice].freeze
22
+ CAPTION_TITLES = Compiler.minicolumn_names
24
23
 
25
24
  def pre_paragraph
26
25
  nil
@@ -30,21 +29,17 @@ module ReVIEW
30
29
  nil
31
30
  end
32
31
 
33
- attr_accessor :doc_status
32
+ attr_accessor :doc_status, :previous_list_type
34
33
 
35
- def initialize(strict = false, *args)
34
+ def initialize(strict = false, *_args)
36
35
  @strict = strict
37
36
  @output = nil
38
37
  @logger = ReVIEW.logger
39
38
  @doc_status = {}
40
39
  @dictionary = {}
41
- builder_init(*args)
40
+ @previous_list_type = nil
42
41
  end
43
42
 
44
- def builder_init(*args)
45
- end
46
- private :builder_init
47
-
48
43
  def bind(compiler, chapter, location)
49
44
  @compiler = compiler
50
45
  @chapter = chapter
@@ -53,6 +48,10 @@ module ReVIEW
53
48
  if @chapter.present?
54
49
  @book = @chapter.book
55
50
  end
51
+ @chapter.generate_indexes
52
+ if @book
53
+ @book.generate_indexes
54
+ end
56
55
  @tabwidth = nil
57
56
  @tsize = nil
58
57
  if @book && @book.config
@@ -84,6 +83,7 @@ module ReVIEW
84
83
 
85
84
  def builder_init_file
86
85
  @sec_counter = SecCounter.new(5, @chapter)
86
+ @doc_status = {}
87
87
  end
88
88
  private :builder_init_file
89
89
 
@@ -91,8 +91,19 @@ module ReVIEW
91
91
  false
92
92
  end
93
93
 
94
+ def solve_nest(s)
95
+ check_nest
96
+ s.gsub(/\x01→.+?←\x01/, '')
97
+ end
98
+
99
+ def check_nest
100
+ if @children && !@children.empty?
101
+ error "//beginchild of #{@children.reverse.join(',')} misses //endchild"
102
+ end
103
+ end
104
+
94
105
  def result
95
- @output.string
106
+ solve_nest(@output.string)
96
107
  end
97
108
 
98
109
  alias_method :raw_result, :result
@@ -211,7 +222,7 @@ module ReVIEW
211
222
  sepidx = nil
212
223
  rows = []
213
224
  lines.each_with_index do |line, idx|
214
- if /\A[\=\-]{12}/ =~ line || /\A[\=\{\-\}]{12}/ =~ line
225
+ if /\A[=\-]{12}/ =~ line || /\A[={\-}]{12}/ =~ line
215
226
  sepidx ||= idx
216
227
  next
217
228
  end
@@ -240,7 +251,7 @@ module ReVIEW
240
251
 
241
252
  def adjust_n_cols(rows)
242
253
  rows.each do |cols|
243
- while cols.last and cols.last.strip.empty?
254
+ while cols.last && cols.last.strip.empty?
244
255
  cols.pop
245
256
  end
246
257
  end
@@ -544,9 +555,36 @@ module ReVIEW
544
555
  CAPTION_TITLES.each do |name|
545
556
  class_eval %Q(
546
557
  def #{name}(lines, caption = nil)
558
+ check_nested_minicolumn
547
559
  captionblock("#{name}", lines, caption)
548
560
  end
549
- )
561
+
562
+ def #{name}_begin(caption = nil)
563
+ check_nested_minicolumn
564
+ @doc_status[:minicolumn] = '#{name}'
565
+ if caption
566
+ puts compile_inline(caption)
567
+ end
568
+ end
569
+
570
+ def #{name}_end
571
+ @doc_status[:minicolumn] = nil
572
+ end
573
+ ), __FILE__, __LINE__ - 17
574
+ end
575
+
576
+ def check_nested_minicolumn
577
+ if @doc_status[:minicolumn]
578
+ error "#{@location}: nested mini-column is not allowed"
579
+ end
580
+ end
581
+
582
+ def in_minicolumn?
583
+ @doc_status[:minicolumn]
584
+ end
585
+
586
+ def minicolumn_block_name?(name)
587
+ CAPTION_TITLES.include?(name)
550
588
  end
551
589
 
552
590
  def graph(lines, id, command, caption = '')
@@ -687,6 +725,23 @@ EOTGNUPLOT
687
725
  str
688
726
  end
689
727
 
728
+ def beginchild
729
+ @children ||= []
730
+ unless @previous_list_type
731
+ error "//beginchild is shown, but previous element isn't ul, ol, or dl"
732
+ end
733
+ puts "\x01→#{@previous_list_type}←\x01"
734
+ @children.push(@previous_list_type)
735
+ end
736
+
737
+ def endchild
738
+ if @children.nil? || @children.empty?
739
+ error "//endchild is shown, but any opened //beginchild doesn't exist"
740
+ else
741
+ puts "\x01→/#{@children.pop}←\x01"
742
+ end
743
+ end
744
+
690
745
  def caption_top?(type)
691
746
  unless %w[top bottom].include?(@book.config['caption_position'][type])
692
747
  warn("invalid caption_position/#{type} parameter. 'top' is assumed")
@@ -41,7 +41,7 @@ module ReVIEW
41
41
 
42
42
  def replace_part(old_name, new_name)
43
43
  @yaml['CHAPS'].map! do |e|
44
- if e.is_a?(Hash) and (e.keys.first == old_name)
44
+ if e.is_a?(Hash) && (e.keys.first == old_name)
45
45
  e = { new_name => e.values.first }
46
46
  end
47
47
  e
@@ -89,7 +89,7 @@ module ReVIEW
89
89
  filenames.concat(postdef)
90
90
  end
91
91
  filenames.each do |filename|
92
- refile = File.join(basedir, config['contentdir'], filename)
92
+ refile = File.expand_path(File.join(config['contentdir'], filename), basedir)
93
93
  unless File.exist?(refile)
94
94
  raise FileNotFound, "file not found in catalog.yml: #{refile}"
95
95
  end
@@ -14,8 +14,8 @@ require 'strscan'
14
14
 
15
15
  module ReVIEW
16
16
  class Compiler
17
- def initialize(strategy)
18
- @strategy = strategy
17
+ def initialize(builder)
18
+ @builder = builder
19
19
 
20
20
  ## commands which do not parse block lines in compiler
21
21
  @non_parsed_commands = %i[embed texequation graph]
@@ -24,10 +24,15 @@ module ReVIEW
24
24
  @command_name_stack = []
25
25
  end
26
26
 
27
- attr_reader :strategy
27
+ attr_reader :builder, :previous_list_type
28
+
29
+ def strategy
30
+ error 'Compiler#strategy is obsoleted. Use Compiler#builder.'
31
+ @builder
32
+ end
28
33
 
29
34
  def non_escaped_commands
30
- if @strategy.highlight?
35
+ if @builder.highlight?
31
36
  %i[list emlist listnum emlistnum cmd]
32
37
  else
33
38
  []
@@ -37,7 +42,7 @@ module ReVIEW
37
42
  def compile(chap)
38
43
  @chapter = chap
39
44
  do_compile
40
- @strategy.result
45
+ @builder.result
41
46
  end
42
47
 
43
48
  class SyntaxElement
@@ -68,12 +73,16 @@ module ReVIEW
68
73
  end
69
74
  end
70
75
 
76
+ def minicolumn?
77
+ @type == :minicolumn
78
+ end
79
+
71
80
  def block_required?
72
- @type == :block
81
+ @type == :block or @type == :minicolumn
73
82
  end
74
83
 
75
84
  def block_allowed?
76
- @type == :block or @type == :optional
85
+ @type == :block or @type == :optional or @type == :minicolumn
77
86
  end
78
87
  end
79
88
 
@@ -83,6 +92,10 @@ module ReVIEW
83
92
  defsyntax(name, (optional ? :optional : :block), argc, &block)
84
93
  end
85
94
 
95
+ def self.defminicolumn(name, argc, _optional = false, &block)
96
+ defsyntax(name, :minicolumn, argc, &block)
97
+ end
98
+
86
99
  def self.defsingle(name, argc, &block)
87
100
  defsyntax(name, :line, argc, &block)
88
101
  end
@@ -95,6 +108,16 @@ module ReVIEW
95
108
  INLINE[name] = InlineSyntaxElement.new(name)
96
109
  end
97
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
+
98
121
  def syntax_defined?(name)
99
122
  SYNTAX.key?(name.to_sym)
100
123
  end
@@ -143,18 +166,19 @@ module ReVIEW
143
166
  defblock :bpo, 0
144
167
  defblock :flushright, 0
145
168
  defblock :centering, 0
146
- defblock :note, 0..1
147
- defblock :memo, 0..1
148
- defblock :info, 0..1
149
- defblock :important, 0..1
150
- defblock :caution, 0..1
151
- defblock :notice, 0..1
152
- defblock :warning, 0..1
153
- defblock :tip, 0..1
154
169
  defblock :box, 0..1
155
170
  defblock :comment, 0..1, true
156
171
  defblock :embed, 0..1
157
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
+
158
182
  defsingle :footnote, 2
159
183
  defsingle :noindent, 0
160
184
  defsingle :blankline, 0
@@ -167,6 +191,8 @@ module ReVIEW
167
191
  defsingle :include, 1
168
192
  defsingle :olnum, 1
169
193
  defsingle :firstlinenum, 1
194
+ defsingle :beginchild, 0
195
+ defsingle :endchild, 0
170
196
 
171
197
  definline :chapref
172
198
  definline :chap
@@ -231,39 +257,62 @@ module ReVIEW
231
257
 
232
258
  def do_compile
233
259
  f = LineInput.new(StringIO.new(@chapter.content))
234
- @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
235
264
 
236
265
  tagged_section_init
237
266
  while f.next?
238
267
  case f.peek
239
268
  when /\A\#@/
240
269
  f.gets # Nothing to do
241
- when /\A=+[\[\s\{]/
270
+ when /\A=+[\[\s{]/
242
271
  compile_headline(f.gets)
272
+ @builder.previous_list_type = nil
243
273
  when /\A\s+\*/
244
274
  compile_ulist(f)
275
+ @builder.previous_list_type = 'ul'
245
276
  when /\A\s+\d+\./
246
277
  compile_olist(f)
278
+ @builder.previous_list_type = 'ol'
247
279
  when /\A\s+:\s/
248
280
  compile_dlist(f)
281
+ @builder.previous_list_type = 'dl'
249
282
  when /\A\s*:\s/
250
283
  warn 'Definition list starting with `:` is deprecated. It should start with ` : `.'
251
284
  compile_dlist(f)
285
+ @builder.previous_list_type = 'dl'
252
286
  when %r{\A//\}}
253
- f.gets
254
- 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
255
294
  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)
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)
262
313
  @command_name_stack.pop
263
- next
264
314
  end
265
- compile_command(syntax, args, lines)
266
- @command_name_stack.pop
315
+ @builder.previous_list_type = nil
267
316
  when %r{\A//}
268
317
  line = f.gets
269
318
  warn "`//' seen but is not valid command: #{line.strip.inspect}"
@@ -271,17 +320,46 @@ module ReVIEW
271
320
  warn 'skipping block...'
272
321
  read_block(f, false)
273
322
  end
323
+ @builder.previous_list_type = nil
274
324
  else
275
325
  if f.peek.strip.empty?
276
326
  f.gets
277
327
  next
278
328
  end
279
329
  compile_paragraph(f)
330
+ @builder.previous_list_type = nil
280
331
  end
281
332
  end
282
333
  close_all_tagged_section
283
334
  end
284
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
+
285
363
  def compile_headline(line)
286
364
  @headline_indexs ||= [@chapter.number.to_i - 1]
287
365
  m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line)
@@ -317,18 +395,18 @@ module ReVIEW
317
395
  end
318
396
  @headline_indexs[index] += 1
319
397
  close_current_tagged_section(level)
320
- @strategy.headline(level, label, caption)
398
+ @builder.headline(level, label, caption)
321
399
  end
322
400
  end
323
401
 
324
402
  def close_current_tagged_section(level)
325
- while @tagged_section.last and @tagged_section.last[1] >= level
403
+ while @tagged_section.last && (@tagged_section.last[1] >= level)
326
404
  close_tagged_section(* @tagged_section.pop)
327
405
  end
328
406
  end
329
407
 
330
408
  def headline(level, label, caption)
331
- @strategy.headline(level, label, caption)
409
+ @builder.headline(level, label, caption)
332
410
  end
333
411
 
334
412
  def tagged_section_init
@@ -337,21 +415,21 @@ module ReVIEW
337
415
 
338
416
  def open_tagged_section(tag, level, label, caption)
339
417
  mid = "#{tag}_begin"
340
- unless @strategy.respond_to?(mid)
341
- error "strategy does not support tagged section: #{tag}"
418
+ unless @builder.respond_to?(mid)
419
+ error "builder does not support tagged section: #{tag}"
342
420
  headline(level, label, caption)
343
421
  return
344
422
  end
345
423
  @tagged_section.push([tag, level])
346
- @strategy.__send__(mid, level, label, caption)
424
+ @builder.__send__(mid, level, label, caption)
347
425
  end
348
426
 
349
427
  def close_tagged_section(tag, level)
350
428
  mid = "#{tag}_end"
351
- if @strategy.respond_to?(mid)
352
- @strategy.__send__(mid, level)
429
+ if @builder.respond_to?(mid)
430
+ @builder.__send__(mid, level)
353
431
  else
354
- error "strategy does not support block op: #{mid}"
432
+ error "builder does not support block op: #{mid}"
355
433
  end
356
434
  end
357
435
 
@@ -374,38 +452,38 @@ module ReVIEW
374
452
  line =~ /\A\s+(\*+)/
375
453
  current_level = $1.size
376
454
  if level == current_level
377
- @strategy.ul_item_end
455
+ @builder.ul_item_end
378
456
  # body
379
- @strategy.ul_item_begin(buf)
457
+ @builder.ul_item_begin(buf)
380
458
  elsif level < current_level # down
381
459
  level_diff = current_level - level
382
460
  if level_diff != 1
383
461
  error 'too many *.'
384
462
  end
385
463
  level = current_level
386
- @strategy.ul_begin { level }
387
- @strategy.ul_item_begin(buf)
464
+ @builder.ul_begin { level }
465
+ @builder.ul_item_begin(buf)
388
466
  elsif level > current_level # up
389
467
  level_diff = level - current_level
390
468
  level = current_level
391
469
  (1..level_diff).to_a.reverse_each do |i|
392
- @strategy.ul_item_end
393
- @strategy.ul_end { level + i }
470
+ @builder.ul_item_end
471
+ @builder.ul_end { level + i }
394
472
  end
395
- @strategy.ul_item_end
473
+ @builder.ul_item_end
396
474
  # body
397
- @strategy.ul_item_begin(buf)
475
+ @builder.ul_item_begin(buf)
398
476
  end
399
477
  end
400
478
 
401
479
  (1..level).to_a.reverse_each do |i|
402
- @strategy.ul_item_end
403
- @strategy.ul_end { i }
480
+ @builder.ul_item_end
481
+ @builder.ul_end { i }
404
482
  end
405
483
  end
406
484
 
407
485
  def compile_olist(f)
408
- @strategy.ol_begin
486
+ @builder.ol_begin
409
487
  f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
410
488
  next if line =~ /\A\#@/
411
489
 
@@ -414,24 +492,24 @@ module ReVIEW
414
492
  f.while_match(/\A\s+(?!\d+\.)\S/) do |cont|
415
493
  buf.push(text(cont.strip))
416
494
  end
417
- @strategy.ol_item(buf, num)
495
+ @builder.ol_item(buf, num)
418
496
  end
419
- @strategy.ol_end
497
+ @builder.ol_end
420
498
  end
421
499
 
422
500
  def compile_dlist(f)
423
- @strategy.dl_begin
501
+ @builder.dl_begin
424
502
  while /\A\s*:/ =~ f.peek
425
503
  # 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
504
+ @builder.doc_status[:dt] = true
505
+ @builder.dt(text(f.gets.sub(/\A\s*:/, '').strip))
506
+ @builder.doc_status[:dt] = nil
429
507
  desc = f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) }
430
- @strategy.dd(desc)
508
+ @builder.dd(desc)
431
509
  f.skip_blank_lines
432
510
  f.skip_comment_lines
433
511
  end
434
- @strategy.dl_end
512
+ @builder.dl_end
435
513
  end
436
514
 
437
515
  def compile_paragraph(f)
@@ -440,7 +518,7 @@ module ReVIEW
440
518
  break if line.strip.empty?
441
519
  buf.push(text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t")))
442
520
  end
443
- @strategy.paragraph(buf)
521
+ @builder.paragraph(buf)
444
522
  end
445
523
 
446
524
  def read_command(f)
@@ -449,9 +527,9 @@ module ReVIEW
449
527
  ignore_inline = @non_parsed_commands.include?(name)
450
528
  @command_name_stack.push(name)
451
529
  args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name)
452
- @strategy.doc_status[name] = true
530
+ @builder.doc_status[name] = true
453
531
  lines = block_open?(line) ? read_block(f, ignore_inline) : nil
454
- @strategy.doc_status[name] = nil
532
+ @builder.doc_status[name] = nil
455
533
  [name, args, lines]
456
534
  end
457
535
 
@@ -496,8 +574,8 @@ module ReVIEW
496
574
  end
497
575
 
498
576
  def compile_command(syntax, args, lines)
499
- unless @strategy.respond_to?(syntax.name)
500
- error "strategy does not support command: //#{syntax.name}"
577
+ unless @builder.respond_to?(syntax.name)
578
+ error "builder does not support command: //#{syntax.name}"
501
579
  compile_unknown_command(args, lines)
502
580
  return
503
581
  end
@@ -518,11 +596,11 @@ module ReVIEW
518
596
  end
519
597
 
520
598
  def compile_unknown_command(args, lines)
521
- @strategy.unknown_command(args, lines)
599
+ @builder.unknown_command(args, lines)
522
600
  end
523
601
 
524
602
  def compile_block(syntax, args, lines)
525
- @strategy.__send__(syntax.name, (lines || default_block(syntax)), *args)
603
+ @builder.__send__(syntax.name, (lines || default_block(syntax)), *args)
526
604
  end
527
605
 
528
606
  def default_block(syntax)
@@ -533,7 +611,7 @@ module ReVIEW
533
611
  end
534
612
 
535
613
  def compile_single(syntax, args)
536
- @strategy.__send__(syntax.name, *args)
614
+ @builder.__send__(syntax.name, *args)
537
615
  end
538
616
 
539
617
  def replace_fence(str)
@@ -559,7 +637,7 @@ module ReVIEW
559
637
 
560
638
  def text(str, block_mode = false)
561
639
  return '' if str.empty?
562
- words = replace_fence(str).split(/(@<\w+>\{(?:[^\}\\]|\\.)*?\})/, -1)
640
+ words = replace_fence(str).split(/(@<\w+>\{(?:[^}\\]|\\.)*?\})/, -1)
563
641
  words.each do |w|
564
642
  if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
565
643
  error "`@<xxx>' seen but is not valid inline op: #{w}"
@@ -570,7 +648,7 @@ module ReVIEW
570
648
  if in_non_escaped_command? && block_mode
571
649
  result << revert_replace_fence(words.shift)
572
650
  else
573
- result << @strategy.nofunc_text(revert_replace_fence(words.shift))
651
+ result << @builder.nofunc_text(revert_replace_fence(words.shift))
574
652
  end
575
653
  break if words.empty?
576
654
  result << compile_inline(revert_replace_fence(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\')))
@@ -579,28 +657,36 @@ module ReVIEW
579
657
  rescue => e
580
658
  error e.message
581
659
  end
582
- public :text # called from strategy
660
+ public :text # called from builder
583
661
 
584
662
  def compile_inline(str)
585
663
  op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures
586
664
  unless inline_defined?(op)
587
665
  raise CompileError, "no such inline op: #{op}"
588
666
  end
589
- unless @strategy.respond_to?("inline_#{op}")
590
- raise "strategy does not support inline op: @<#{op}>"
667
+ unless @builder.respond_to?("inline_#{op}")
668
+ raise "builder does not support inline op: @<#{op}>"
591
669
  end
592
- @strategy.__send__("inline_#{op}", arg)
670
+ @builder.__send__("inline_#{op}", arg)
593
671
  rescue => e
594
672
  error e.message
595
- @strategy.nofunc_text(str)
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)
596
682
  end
597
683
 
598
684
  def warn(msg)
599
- @strategy.warn msg
685
+ @builder.warn msg
600
686
  end
601
687
 
602
688
  def error(msg)
603
- @strategy.error msg
689
+ @builder.error msg
604
690
  end
605
691
  end
606
692
  end # module ReVIEW