review 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-win.yml +39 -0
  3. data/.github/workflows/ruby.yml +27 -0
  4. data/.rubocop.yml +10 -7
  5. data/Dockerfile +21 -5
  6. data/NEWS.ja.md +101 -1
  7. data/NEWS.md +102 -2
  8. data/README.md +10 -7
  9. data/appveyor.yml +0 -20
  10. data/bin/review-compile +1 -1
  11. data/bin/review-validate +1 -1
  12. data/doc/config.yml.sample +12 -1
  13. data/doc/config.yml.sample-simple +1 -0
  14. data/doc/format.ja.md +16 -4
  15. data/doc/format.md +2 -1
  16. data/doc/quickstart.ja.md +40 -23
  17. data/doc/quickstart.md +33 -15
  18. data/lib/review/book/base.rb +8 -7
  19. data/lib/review/book/index.rb +50 -56
  20. data/lib/review/book/index/item.rb +1 -1
  21. data/lib/review/builder.rb +26 -4
  22. data/lib/review/compiler.rb +6 -3
  23. data/lib/review/configure.rb +5 -3
  24. data/lib/review/epubmaker.rb +15 -2
  25. data/lib/review/extentions/string.rb +0 -4
  26. data/lib/review/htmlbuilder.rb +4 -15
  27. data/lib/review/idgxmlbuilder.rb +10 -19
  28. data/lib/review/idgxmlmaker.rb +10 -3
  29. data/lib/review/init.rb +8 -1
  30. data/lib/review/latexbuilder.rb +13 -6
  31. data/lib/review/pdfmaker.rb +24 -9
  32. data/lib/review/rstbuilder.rb +2 -2
  33. data/lib/review/textmaker.rb +7 -1
  34. data/lib/review/tocparser.rb +6 -2
  35. data/lib/review/version.rb +1 -1
  36. data/lib/review/webmaker.rb +8 -1
  37. data/review.gemspec +2 -2
  38. data/samples/sample-book/src/.gitignore +1 -0
  39. data/samples/sample-book/src/config-ebook.yml +4 -0
  40. data/samples/sample-book/src/config-jlreq-ebook.yml +4 -0
  41. data/samples/sample-book/src/config.yml +1 -1
  42. data/samples/sample-book/src/lib/tasks/review.rake +10 -6
  43. data/samples/syntax-book/ch01.re +4 -2
  44. data/samples/syntax-book/ch02.re +8 -16
  45. data/samples/syntax-book/config-jlreq-lualatex.yml +4 -0
  46. data/samples/syntax-book/config-print.yml +3 -0
  47. data/samples/syntax-book/config.yml +1 -1
  48. data/samples/syntax-book/lib/tasks/review.rake +23 -8
  49. data/templates/latex/config.erb +6 -6
  50. data/templates/latex/review-jlreq/review-base.sty +14 -4
  51. data/templates/latex/review-jlreq/review-jlreq.cls +10 -1
  52. data/templates/latex/review-jlreq/review-style.sty +1 -1
  53. data/templates/latex/review-jsbook/review-base.sty +10 -0
  54. data/templates/latex/review-jsbook/review-jsbook.cls +1 -1
  55. data/templates/latex/review-jsbook/review-style.sty +1 -1
  56. data/test/assets/test_template.tex +6 -6
  57. data/test/assets/test_template_backmatter.tex +6 -6
  58. data/test/test_book.rb +8 -0
  59. data/test/test_book_chapter.rb +4 -2
  60. data/test/test_catalog.rb +1 -0
  61. data/test/test_epubmaker_cmd.rb +12 -5
  62. data/test/test_helper.rb +11 -6
  63. data/test/test_htmlbuilder.rb +80 -8
  64. data/test/test_idgxmlbuilder.rb +57 -2
  65. data/test/test_idgxmlmaker_cmd.rb +46 -0
  66. data/test/test_image_finder.rb +52 -70
  67. data/test/test_index.rb +12 -12
  68. data/test/test_latexbuilder.rb +171 -8
  69. data/test/test_latexbuilder_v2.rb +9 -7
  70. data/test/test_pdfmaker_cmd.rb +99 -5
  71. data/test/test_textmaker_cmd.rb +54 -0
  72. data/test/test_topbuilder.rb +59 -0
  73. data/vendor/jsclasses/LICENSE +1 -1
  74. data/vendor/jsclasses/jis/jsarticle.cls +53 -14
  75. data/vendor/jsclasses/jis/jsbook.cls +53 -14
  76. data/vendor/jsclasses/jis/jsclasses.dtx +84 -25
  77. data/vendor/jsclasses/jis/jslogo.dtx +4 -4
  78. data/vendor/jsclasses/jis/jslogo.sty +3 -3
  79. data/vendor/jsclasses/jis/jspf.cls +52 -13
  80. data/vendor/jsclasses/jis/jsreport.cls +53 -14
  81. data/vendor/jsclasses/jis/kiyou.cls +53 -14
  82. data/vendor/jsclasses/jis/okumacro.dtx +4 -5
  83. data/vendor/jsclasses/jis/okumacro.sty +3 -4
  84. data/vendor/jsclasses/jsarticle.cls +53 -14
  85. data/vendor/jsclasses/jsbook.cls +53 -14
  86. data/vendor/jsclasses/jsclasses.dtx +84 -25
  87. data/vendor/jsclasses/jsclasses.pdf +0 -0
  88. data/vendor/jsclasses/jslogo.dtx +4 -4
  89. data/vendor/jsclasses/jslogo.pdf +0 -0
  90. data/vendor/jsclasses/jslogo.sty +3 -3
  91. data/vendor/jsclasses/jspf.cls +52 -13
  92. data/vendor/jsclasses/jsreport.cls +53 -14
  93. data/vendor/jsclasses/kiyou.cls +53 -14
  94. data/vendor/jsclasses/okumacro.dtx +4 -5
  95. data/vendor/jsclasses/okumacro.pdf +0 -0
  96. data/vendor/jsclasses/okumacro.sty +3 -4
  97. metadata +15 -6
  98. data/samples/syntax-book/review-ext.rb +0 -14
@@ -2,7 +2,3 @@ if defined?(Encoding) && Encoding.respond_to?('default_external') &&
2
2
  Encoding.default_external != Encoding::UTF_8
3
3
  Encoding.default_external = 'UTF-8'
4
4
  end
5
-
6
- class String
7
- alias_method :each, :each_line
8
- end
@@ -373,12 +373,7 @@ module ReVIEW
373
373
 
374
374
  def list(lines, id, caption, lang = nil)
375
375
  puts %Q(<div id="#{normalize_id(id)}" class="caption-code">)
376
- begin
377
- list_header(id, caption, lang)
378
- rescue KeyError
379
- error "no such list: #{id}"
380
- end
381
- list_body(id, lines, lang)
376
+ super(lines, id, caption, lang)
382
377
  puts '</div>'
383
378
  end
384
379
 
@@ -403,8 +398,7 @@ module ReVIEW
403
398
 
404
399
  def source(lines, caption = nil, lang = nil)
405
400
  puts %Q(<div class="source-code">)
406
- source_header(caption)
407
- source_body(caption, lines, lang)
401
+ super(lines, caption, lang)
408
402
  puts '</div>'
409
403
  end
410
404
 
@@ -414,7 +408,7 @@ module ReVIEW
414
408
  end
415
409
  end
416
410
 
417
- def source_body(_id, lines, lang)
411
+ def source_body(lines, lang)
418
412
  print %Q(<pre class="source">)
419
413
  body = lines.inject('') { |i, j| i + detab(j) + "\n" }
420
414
  lexer = lang
@@ -424,12 +418,7 @@ module ReVIEW
424
418
 
425
419
  def listnum(lines, id, caption, lang = nil)
426
420
  puts %Q(<div id="#{normalize_id(id)}" class="code">)
427
- begin
428
- list_header(id, caption, lang)
429
- rescue KeyError
430
- error "no such list: #{id}"
431
- end
432
- listnum_body(lines, lang)
421
+ super(lines, id, caption, lang)
433
422
  puts '</div>'
434
423
  end
435
424
 
@@ -297,12 +297,7 @@ module ReVIEW
297
297
 
298
298
  def list(lines, id, caption, lang = nil)
299
299
  puts '<codelist>'
300
- begin
301
- list_header(id, caption, lang)
302
- rescue KeyError
303
- error "no such list: #{id}"
304
- end
305
- list_body(id, lines, lang)
300
+ super(lines, id, caption, lang)
306
301
  puts '</codelist>'
307
302
  end
308
303
 
@@ -327,12 +322,7 @@ module ReVIEW
327
322
 
328
323
  def listnum(lines, id, caption, lang = nil)
329
324
  puts '<codelist>'
330
- begin
331
- list_header(id, caption, lang)
332
- rescue KeyError
333
- error "no such list: #{id}"
334
- end
335
- listnum_body(lines, lang)
325
+ super(lines, id, caption, lang)
336
326
  puts '</codelist>'
337
327
  end
338
328
 
@@ -490,6 +480,7 @@ module ReVIEW
490
480
  else
491
481
  print %Q(<tbody xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/" aid:table="table" aid:trows="#{rows.length}" aid:tcols="#{@col}">)
492
482
  end
483
+ @table_id = id
493
484
  table_rows(sepidx, rows)
494
485
  puts '</tbody></table>'
495
486
  @tsize = nil
@@ -508,7 +499,7 @@ module ReVIEW
508
499
  else
509
500
  rows.push(line.gsub(/\t\.\t/, "\t\t").gsub(/\t\.\.\t/, "\t.\t").gsub(/\t\.\Z/, "\t").gsub(/\t\.\.\Z/, "\t.").gsub(/\A\./, ''))
510
501
  end
511
- col2 = rows[rows.length - 1].split(/\t/).length
502
+ col2 = rows[rows.length - 1].split(table_row_separator_regexp).length
512
503
  @col = col2 if col2 > @col
513
504
  end
514
505
  error 'no rows in the table' if rows.empty?
@@ -526,11 +517,11 @@ module ReVIEW
526
517
  cellwidth.size.times do |n|
527
518
  cellwidth[n] = cellwidth[n].to_f / @book.config['pt_to_mm_unit'].to_f
528
519
  totallength += cellwidth[n]
529
- warn "total length exceeds limit for table: #{id}" if totallength > @tablewidth
520
+ warn "total length exceeds limit for table: #{@table_id}" if totallength > @tablewidth
530
521
  end
531
522
  if cellwidth.size < @col
532
523
  cw = (@tablewidth - totallength) / (@col - cellwidth.size)
533
- warn "auto cell sizing exceeds limit for table: #{id}" if cw <= 0
524
+ warn "auto cell sizing exceeds limit for table: #{@table_id}" if cw <= 0
534
525
  (cellwidth.size..(@col - 1)).each { |i| cellwidth[i] = cw }
535
526
  end
536
527
  end
@@ -542,7 +533,7 @@ module ReVIEW
542
533
  puts %Q(<tr type="header">#{rows.shift}</tr>)
543
534
  else
544
535
  i = 0
545
- rows.shift.split("\t").each_with_index do |cell, x|
536
+ rows.shift.split(table_row_separator_regexp).each_with_index do |cell, x|
546
537
  print %Q(<td xyh="#{x + 1},#{y + 1},#{sepidx}" aid:table="cell" aid:theader="1" aid:crows="1" aid:ccols="1" aid:ccolwidth="#{sprintf('%.3f', cellwidth[i])}">#{cell.sub('DUMMYCELLSPLITTER', '')}</td>)
547
538
  i += 1
548
539
  end
@@ -557,7 +548,7 @@ module ReVIEW
557
548
  if tablewidth
558
549
  rows.each_with_index do |row, y|
559
550
  i = 0
560
- row.split("\t").each_with_index do |cell, x|
551
+ row.split(table_row_separator_regexp).each_with_index do |cell, x|
561
552
  print %Q(<td xyh="#{x + 1},#{y + 1 + sepidx},#{sepidx}" aid:table="cell" aid:crows="1" aid:ccols="1" aid:ccolwidth="#{sprintf('%.3f', cellwidth[i])}">#{cell.sub('DUMMYCELLSPLITTER', '')}</td>)
562
553
  i += 1
563
554
  end
@@ -602,7 +593,7 @@ module ReVIEW
602
593
  end
603
594
 
604
595
  def imgtable(lines, id, caption = nil, metric = nil)
605
- if @chapter.image(id).bound?
596
+ if @chapter.image_bound?(id)
606
597
  metrics = parse_metric('idgxml', metric)
607
598
  puts '<table>'
608
599
  table_header(id, caption) if caption.present?
@@ -1155,7 +1146,7 @@ module ReVIEW
1155
1146
  error "unknown chapter: #{id}"
1156
1147
  end
1157
1148
 
1158
- def source(lines, caption, lang = nil)
1149
+ def source(lines, caption = nil, lang = nil)
1159
1150
  puts '<source>'
1160
1151
  source_header(caption)
1161
1152
  source_body(lines, lang)
@@ -44,11 +44,13 @@ module ReVIEW
44
44
  opts = OptionParser.new
45
45
  @table = nil
46
46
  @filter = nil
47
+ @buildonly = nil
47
48
 
48
49
  opts.banner = 'Usage: review-idgxmlmaker [options] configfile'
49
50
  opts.version = ReVIEW::VERSION
50
51
  opts.on('-w', '--width widthoftypepage', 'Specify the width of type page for layouting tables (mm).') { |v| @table = v }
51
52
  opts.on('-f', '--filter filterprogrampath', 'Specify the filter path.') { |v| @filter = v }
53
+ opts.on('-y', '--only file1,file2,...', 'Build only specified files.') { |v| @buildonly = v.split(/\s*,\s*/).map { |m| m.strip.sub(/\.re\Z/, '') } }
52
54
  opts.on('--help', 'Prints this message and quit.') do
53
55
  puts opts.help
54
56
  exit 0
@@ -116,13 +118,14 @@ module ReVIEW
116
118
  ENV['REVIEW_FNAME'] = File.basename(xmlfile).sub(/.xml\Z/, '.re')
117
119
  begin
118
120
  o, e, s = Open3.capture3(@filter, stdin_data: File.read(xmlfile))
121
+ unless e.empty?
122
+ warn("filter error for #{xmlfile}: #{e}")
123
+ end
119
124
  if s.success?
120
125
  File.write(xmlfile, o) # override
121
- else
122
- warn("filter error for #{xmlfile}: #{e.message}")
123
126
  end
124
127
  rescue => e
125
- warn("filter error for #{xmlfile}: #{e}")
128
+ warn("filter error for #{xmlfile}: #{e.message}")
126
129
  end
127
130
  end
128
131
 
@@ -169,6 +172,10 @@ module ReVIEW
169
172
  filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
170
173
  end
171
174
  id = File.basename(filename).sub(/\.re\Z/, '')
175
+ if @buildonly && !@buildonly.include?(id)
176
+ warn "skip #{id}.re"
177
+ return
178
+ end
172
179
 
173
180
  xmlfile = "#{id}.xml"
174
181
 
@@ -82,6 +82,9 @@ module ReVIEW
82
82
  opts.on('', '--epub-version VERSION', 'define EPUB version.') do |version|
83
83
  @epub_version = version
84
84
  end
85
+ opts.on('', '--without-config-comment', "don't include comments in config.yml.") do
86
+ @without_config_comment = true
87
+ end
85
88
  opts.on('', '--without-doc', "don't generate doc files.") do
86
89
  @without_doc = true
87
90
  end
@@ -176,6 +179,10 @@ EOS
176
179
  content.gsub!(/^#\s*texdocumentclass:.*$/, %Q(texdocumentclass: ["#{@template}", "#{@tex_documentclass_opts[@template]}"]))
177
180
  end
178
181
 
182
+ if @without_config_comment
183
+ content = content.split("\n").delete_if { |l| l.strip.start_with?('#') || l.strip.empty? }.join("\n")
184
+ end
185
+
179
186
  File.open(File.join(dir, 'config.yml'), 'w') { |f| f.write content }
180
187
  if @webui && !@web_result[2].empty?
181
188
  File.open(File.join(dir, 'config-ebook.yml'), 'w') do |f|
@@ -292,7 +299,7 @@ EOS
292
299
  next
293
300
  end
294
301
 
295
- if fname =~ %r{\A/} || fname =~ /\.\./ # simple fool proof
302
+ if fname.start_with?('/') || fname =~ /\.\./ # simple fool proof
296
303
  made = nil
297
304
  break
298
305
  end
@@ -1,6 +1,6 @@
1
1
  # Copyright (c) 2002-2007 Minero Aoki
2
2
  # 2008-2009 Minero Aoki, Kenshi Muto
3
- # 2010-2019 Minero Aoki, Kenshi Muto, TAKAHASHI Masayoshi
3
+ # 2010-2020 Minero Aoki, Kenshi Muto, TAKAHASHI Masayoshi
4
4
  #
5
5
  # This program is free software.
6
6
  # You can distribute or modify this program under the terms of
@@ -60,7 +60,7 @@ module ReVIEW
60
60
  require 'nkf'
61
61
  @index_mecab = MeCab::Tagger.new(@book.config['pdfmaker']['makeindex_mecab_opts'])
62
62
  rescue LoadError
63
- error 'not found MeCab'
63
+ warn 'not found MeCab'
64
64
  end
65
65
  end
66
66
 
@@ -299,8 +299,7 @@ module ReVIEW
299
299
  end
300
300
 
301
301
  def dt(str)
302
- str.sub!(/\[/) { '\lbrack{}' }
303
- str.sub!(/\]/) { '\rbrack{}' }
302
+ str = str.gsub('[', '\lbrack{}').gsub(']', '\rbrack{}')
304
303
  puts '\item[' + str + '] \mbox{} \\\\'
305
304
  end
306
305
 
@@ -482,8 +481,16 @@ module ReVIEW
482
481
  def image_header(id, caption)
483
482
  end
484
483
 
484
+ def parse_metric(type, metric)
485
+ s = super(type, metric)
486
+ if @book.config['pdfmaker']['use_original_image_size'] && s.empty? && !metric.present?
487
+ return ' ' # pass empty to \reviewincludegraphics
488
+ end
489
+ s
490
+ end
491
+
485
492
  def handle_metric(str)
486
- if @book.config['image_scale2width'] && str =~ /\Ascale=([\d.]+)\Z/
493
+ if @book.config['pdfmaker']['image_scale2width'] && str =~ /\Ascale=([\d.]+)\Z/
487
494
  return "width=#{$1}\\maxwidth"
488
495
  end
489
496
  str
@@ -1019,7 +1026,7 @@ module ReVIEW
1019
1026
  def inline_fn(id)
1020
1027
  if @book.config['footnotetext']
1021
1028
  macro("footnotemark[#{@chapter.footnote(id).number}]", '')
1022
- elsif @doc_status[:caption] || @doc_status[:table] || @doc_status[:column]
1029
+ elsif @doc_status[:caption] || @doc_status[:table] || @doc_status[:column] || @doc_status[:dt]
1023
1030
  @foottext[id] = @chapter.footnote(id).number
1024
1031
  macro('protect\\footnotemark', '')
1025
1032
  else
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2010-2019 Kenshi Muto and Masayoshi Takahashi
1
+ # Copyright (c) 2010-2020 Kenshi Muto and Masayoshi Takahashi
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -100,6 +100,7 @@ module ReVIEW
100
100
  def parse_opts(args)
101
101
  cmd_config = {}
102
102
  opts = OptionParser.new
103
+ @buildonly = nil
103
104
 
104
105
  opts.banner = 'Usage: review-pdfmaker configfile'
105
106
  opts.version = ReVIEW::VERSION
@@ -109,6 +110,7 @@ module ReVIEW
109
110
  end
110
111
  opts.on('--[no-]debug', 'Keep temporary files.') { |debug| cmd_config['debug'] = debug }
111
112
  opts.on('--ignore-errors', 'Ignore review-compile errors.') { cmd_config['ignore-errors'] = true }
113
+ opts.on('-y', '--only file1,file2,...', 'Build only specified files.') { |v| @buildonly = v.split(/\s*,\s*/).map { |m| m.strip.sub(/\.re\Z/, '') } }
112
114
 
113
115
  opts.parse!(args)
114
116
  if args.size != 1
@@ -166,8 +168,13 @@ module ReVIEW
166
168
  if part.name.present?
167
169
  @config['use_part'] = true
168
170
  if part.file?
169
- output_chaps(part.name)
170
- input_files['CHAPS'] << %Q(\\input{#{part.name}.tex}\n)
171
+ if @buildonly && !@buildonly.include?(part.name)
172
+ warn "skip #{part.name}.re"
173
+ input_files['CHAPS'] << %Q(\\part{}\n)
174
+ else
175
+ output_chaps(part.name)
176
+ input_files['CHAPS'] << %Q(\\input{#{part.name}.tex}\n)
177
+ end
171
178
  else
172
179
  input_files['CHAPS'] << %Q(\\part{#{part.name}}\n)
173
180
  end
@@ -175,11 +182,18 @@ module ReVIEW
175
182
 
176
183
  part.chapters.each do |chap|
177
184
  filename = File.basename(chap.path, '.*')
178
- output_chaps(filename)
179
- input_files['PREDEF'] << "\\input{#{filename}.tex}\n" if chap.on_predef?
180
- input_files['CHAPS'] << "\\input{#{filename}.tex}\n" if chap.on_chaps?
181
- input_files['APPENDIX'] << "\\input{#{filename}.tex}\n" if chap.on_appendix?
182
- input_files['POSTDEF'] << "\\input{#{filename}.tex}\n" if chap.on_postdef?
185
+ entry = "\\input{#{filename}.tex}\n"
186
+ if @buildonly && !@buildonly.include?(filename)
187
+ warn "skip #{filename}.re"
188
+ entry = "\\chapter{}\n"
189
+ else
190
+ output_chaps(filename)
191
+ end
192
+
193
+ input_files['PREDEF'] << entry if chap.on_predef?
194
+ input_files['CHAPS'] << entry if chap.on_chaps?
195
+ input_files['APPENDIX'] << entry if chap.on_appendix?
196
+ input_files['POSTDEF'] << entry if chap.on_postdef?
183
197
  end
184
198
  end
185
199
 
@@ -235,8 +249,9 @@ module ReVIEW
235
249
  end
236
250
 
237
251
  call_hook('hook_beforemakeindex')
238
- if @config['pdfmaker']['makeindex'] && File.exist?("#{@mastertex}.idx")
252
+ if @config['pdfmaker']['makeindex'] && File.size?("#{@mastertex}.idx")
239
253
  system_or_raise(*[makeindex_command, makeindex_options, @mastertex].flatten.compact)
254
+ system_or_raise(*[texcommand, texoptions, "#{@mastertex}.tex"].flatten.compact)
240
255
  end
241
256
  call_hook('hook_aftermakeindex')
242
257
 
@@ -67,13 +67,13 @@ module ReVIEW
67
67
  end
68
68
  private :builder_init_file
69
69
 
70
- def print(s)
70
+ def print(*s)
71
71
  @blank_seen = false
72
72
  super
73
73
  end
74
74
  private :print
75
75
 
76
- def puts(s)
76
+ def puts(*s)
77
77
  @blank_seen = false
78
78
  super
79
79
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2019 Kenshi Muto
1
+ # Copyright (c) 2018-2020 Kenshi Muto
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -45,10 +45,12 @@ module ReVIEW
45
45
  def parse_opts(args)
46
46
  cmd_config = {}
47
47
  opts = OptionParser.new
48
+ @buildonly = nil
48
49
 
49
50
  opts.banner = 'Usage: review-textmaker [-n] configfile'
50
51
  opts.version = ReVIEW::VERSION
51
52
  opts.on('-n', 'No decoration.') { @plaintext = true }
53
+ opts.on('-y', '--only file1,file2,...', 'Build only specified files.') { |v| @buildonly = v.split(/\s*,\s*/).map { |m| m.strip.sub(/\.re\Z/, '') } }
52
54
  opts.on('--help', 'Prints this message and quit.') do
53
55
  puts opts.help
54
56
  exit 0
@@ -154,6 +156,10 @@ module ReVIEW
154
156
  filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
155
157
  end
156
158
  id = File.basename(filename).sub(/\.re\Z/, '')
159
+ if @buildonly && !@buildonly.include?(id)
160
+ warn "skip #{id}.re"
161
+ return
162
+ end
157
163
 
158
164
  textfile = "#{id}.txt"
159
165
 
@@ -31,6 +31,7 @@ module ReVIEW
31
31
  def parse(f, chap)
32
32
  roots = [] # list of chapters
33
33
  node_stack = []
34
+ @chap = chap
34
35
  filename = chap.path
35
36
  while line = f.gets
36
37
  case line
@@ -52,7 +53,7 @@ module ReVIEW
52
53
  node_stack.push(dummy_chapter)
53
54
  roots.push(dummy_chapter)
54
55
  end
55
- next if label =~ %r{\A\[/} # ex) "[/column]"
56
+ next if label.start_with?('[/') # ex) "[/column]"
56
57
  sec = Section.new(lev, label.gsub(/\A\{.*?\}\s?/, ''))
57
58
  node_stack.pop until node_stack.last.level < sec.level
58
59
  node_stack.last.add_child(sec)
@@ -73,7 +74,7 @@ module ReVIEW
73
74
  beg = f.lineno
74
75
  list.add(line)
75
76
  while line = f.gets
76
- break if %r{\A//\}} =~ line
77
+ break if line.start_with?('//}')
77
78
  list.add(line)
78
79
  end
79
80
  error!(filename, beg, 'unterminated list') unless line
@@ -107,6 +108,9 @@ module ReVIEW
107
108
  b = ReVIEW::TEXTBuilder.new
108
109
  dummy_book = ReVIEW::Book::Base.load
109
110
  dummy_chapter = ReVIEW::Book::Chapter.new(dummy_book, 1, '-', nil, StringIO.new)
111
+ if @chap
112
+ dummy_chapter = @chap
113
+ end
110
114
  dummy_loc = Location.new('', StringIO.new)
111
115
  b.bind(ReVIEW::Compiler.new(b), dummy_chapter, dummy_loc)
112
116
  b.compile_inline(line)
@@ -1,3 +1,3 @@
1
1
  module ReVIEW
2
- VERSION = '4.0.0'.freeze
2
+ VERSION = '4.1.0'.freeze
3
3
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2018 Masayoshi Takahashi, Masanori Kado, Kenshi Muto
1
+ # Copyright (c) 2016-2020 Masayoshi Takahashi, Masanori Kado, Kenshi Muto
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -50,6 +50,7 @@ module ReVIEW
50
50
  def parse_opts(args)
51
51
  cmd_config = {}
52
52
  opts = OptionParser.new
53
+ @buildonly = nil
53
54
 
54
55
  opts.banner = 'Usage: review-webmaker [option] configfile'
55
56
  opts.version = ReVIEW::VERSION
@@ -58,6 +59,7 @@ module ReVIEW
58
59
  exit 0
59
60
  end
60
61
  opts.on('--ignore-errors', 'Ignore review-compile errors.') { cmd_config['ignore-errors'] = true }
62
+ opts.on('-y', '--only file1,file2,...', 'Build only specified files.') { |v| @buildonly = v.split(/\s*,\s*/).map { |m| m.strip.sub(/\.re\Z/, '') } }
61
63
 
62
64
  opts.parse!(args)
63
65
  if args.size != 1
@@ -186,6 +188,11 @@ module ReVIEW
186
188
  end
187
189
  id = File.basename(filename).sub(/\.re\Z/, '')
188
190
 
191
+ if @buildonly && !@buildonly.include?(id)
192
+ warn "skip #{id}.re"
193
+ return
194
+ end
195
+
189
196
  htmlfile = "#{id}.#{@config['htmlext']}"
190
197
 
191
198
  if @config['params'].present?