review-retrovert 0.9.7 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +9 -0
  3. data/.editorconfig +3 -0
  4. data/.github/workflows/retrovert.yml +20 -5
  5. data/.gitignore +1 -0
  6. data/.vscode/launch.json +48 -0
  7. data/Gemfile.lock +13 -4
  8. data/Makefile +24 -0
  9. data/docker/review.Dockerfile +10 -0
  10. data/lib/review/retrovert/converter.rb +374 -111
  11. data/lib/review/retrovert/reviewcompat.rb +57 -0
  12. data/lib/review/retrovert/reviewdef.rb +70 -0
  13. data/lib/review/retrovert/sty/{ird.sty → ird.sty.erb} +9 -1
  14. data/lib/review/retrovert/sty/review-retrovert-custom.sty.erb +6 -0
  15. data/lib/review/retrovert/utils.rb +35 -0
  16. data/lib/review/retrovert/version.rb +1 -1
  17. data/lib/review/retrovert/yamlconfig.rb +0 -1
  18. data/package-lock.json +6 -0
  19. data/package.json +1 -0
  20. data/review-retrovert.gemspec +5 -1
  21. data/testdata/mybook/.gitignore +3 -1
  22. data/testdata/mybook/.textlintrc +11 -0
  23. data/testdata/mybook/Rakefile +8 -0
  24. data/testdata/mybook/catalog.yml +36 -10
  25. data/testdata/mybook/config-starter.yml +134 -9
  26. data/testdata/mybook/config.yml +44 -15
  27. data/testdata/mybook/contents/00-preface.re +48 -2
  28. data/testdata/mybook/contents/01-install.re +38 -5
  29. data/testdata/mybook/contents/02-tutorial.re +333 -113
  30. data/testdata/mybook/contents/03-syntax.re +2370 -373
  31. data/testdata/mybook/contents/04-customize.re +424 -88
  32. data/testdata/mybook/contents/05-faq.re +288 -10
  33. data/testdata/mybook/contents/06-bestpractice.re +431 -59
  34. data/testdata/mybook/contents/91-compare.re +402 -2
  35. data/testdata/mybook/contents/92-filelist.re +14 -8
  36. data/testdata/mybook/contents/93-background.re +10 -10
  37. data/testdata/mybook/contents/99-postface.re +2 -1
  38. data/testdata/mybook/contents/r0-root.re +1 -1
  39. data/testdata/mybook/css/webstyle.css +180 -2
  40. data/testdata/mybook/data/terms.txt +3 -0
  41. data/testdata/mybook/data/words.txt +15 -0
  42. data/testdata/mybook/images/03-syntax/index-page.png +0 -0
  43. data/testdata/mybook/images/03-syntax/order-detail.png +0 -0
  44. data/testdata/mybook/images/04-customize/section_decoration_samples.png +0 -0
  45. data/testdata/mybook/images/05-faq/dummy-image.png +0 -0
  46. data/testdata/mybook/images/06-bestpractice/section_title_wlines.png +0 -0
  47. data/testdata/mybook/images/avatar-b.png +0 -0
  48. data/testdata/mybook/images/avatar-g.png +0 -0
  49. data/testdata/mybook/images/avatar-r.png +0 -0
  50. data/testdata/mybook/images/caution-icon.png +0 -0
  51. data/testdata/mybook/images/info-icon.png +0 -0
  52. data/testdata/mybook/images/warning-icon.png +0 -0
  53. data/testdata/mybook/layouts/layout.html5.erb +3 -1
  54. data/testdata/mybook/layouts/layout.tex.erb +265 -379
  55. data/testdata/mybook/layouts/layout.tex.erb.orig +386 -0
  56. data/testdata/mybook/lib/ruby/review-book.rb +64 -0
  57. data/testdata/mybook/lib/ruby/review-builder.rb +902 -63
  58. data/testdata/mybook/lib/ruby/review-compiler.rb +675 -22
  59. data/testdata/mybook/lib/ruby/review-epubbuilder.rb +33 -0
  60. data/testdata/mybook/lib/ruby/review-epubmaker.rb +10 -7
  61. data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +354 -66
  62. data/testdata/mybook/lib/ruby/review-latexbuilder.rb +429 -146
  63. data/testdata/mybook/lib/ruby/review-maker.rb +117 -6
  64. data/testdata/mybook/lib/ruby/review-markdownbuilder.rb +945 -0
  65. data/testdata/mybook/lib/ruby/review-markdownmaker.rb +91 -0
  66. data/testdata/mybook/lib/ruby/review-monkeypatch.rb +2 -0
  67. data/testdata/mybook/lib/ruby/review-pdfmaker.rb +160 -82
  68. data/testdata/mybook/lib/ruby/review-webmaker.rb +20 -5
  69. data/testdata/mybook/lib/tasks/review.rake +14 -0
  70. data/testdata/mybook/lib/tasks/starter.rake +148 -4
  71. data/testdata/mybook/sty/indexstyle.ist +25 -0
  72. data/testdata/mybook/sty/mytextsize.sty +29 -0
  73. data/testdata/mybook/sty/mytitlepage.sty +34 -11
  74. data/testdata/mybook/sty/review-base.sty +276 -0
  75. data/testdata/mybook/sty/starter-codeblock.sty +237 -106
  76. data/testdata/mybook/sty/starter-color.sty +72 -17
  77. data/testdata/mybook/sty/starter-heading.sty +60 -13
  78. data/testdata/mybook/sty/starter-misc.sty +894 -0
  79. data/testdata/mybook/sty/starter-note.sty +67 -14
  80. data/testdata/mybook/sty/starter-talklist.sty +105 -0
  81. data/testdata/mybook/sty/starter-util.sty +35 -0
  82. data/testdata/mybook/sty/starter.sty +8 -526
  83. metadata +80 -4
@@ -15,33 +15,601 @@ module ReVIEW
15
15
 
16
16
  class Compiler
17
17
 
18
+ #------------------------------ original code
19
+
20
+ =begin
21
+
22
+ def initialize(strategy)
23
+ @strategy = strategy
24
+ end
25
+
26
+ attr_reader :strategy
27
+
28
+ def compile(chap)
29
+ @chapter = chap
30
+ do_compile
31
+ @strategy.result
32
+ end
33
+
34
+ class SyntaxElement
35
+ def initialize(name, type, argc, &block)
36
+ @name = name
37
+ @type = type
38
+ @argc_spec = argc
39
+ @checker = block
40
+ end
41
+
42
+ attr_reader :name
43
+
44
+ def check_args(args)
45
+ unless @argc_spec === args.size
46
+ raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
47
+ end
48
+ if @checker
49
+ @checker.call(*args)
50
+ end
51
+ end
52
+
53
+ def min_argc
54
+ case @argc_spec
55
+ when Range then @argc_spec.begin
56
+ when Integer then @argc_spec
57
+ else
58
+ raise TypeError, "argc_spec is not Range/Integer: #{inspect}"
59
+ end
60
+ end
61
+
62
+ def block_required?
63
+ @type == :block
64
+ end
65
+
66
+ def block_allowed?
67
+ @type == :block or @type == :optional
68
+ end
69
+ end
70
+
71
+ SYNTAX = {}
72
+
73
+ def self.defblock(name, argc, optional = false, &block)
74
+ defsyntax name, (optional ? :optional : :block), argc, &block
75
+ end
76
+
77
+ def self.defsingle(name, argc, &block)
78
+ defsyntax name, :line, argc, &block
79
+ end
80
+
81
+ def self.defsyntax(name, type, argc, &block)
82
+ SYNTAX[name] = SyntaxElement.new(name, type, argc, &block)
83
+ end
84
+
85
+ def self.definline(name)
86
+ INLINE[name] = InlineSyntaxElement.new(name)
87
+ end
88
+
89
+ def syntax_defined?(name)
90
+ SYNTAX.key?(name.to_sym)
91
+ end
92
+
93
+ def syntax_descriptor(name)
94
+ SYNTAX[name.to_sym]
95
+ end
96
+
97
+ class InlineSyntaxElement
98
+ def initialize(name)
99
+ @name = name
100
+ end
101
+
102
+ attr_reader :name
103
+ end
104
+
105
+ INLINE = {}
106
+
107
+ def inline_defined?(name)
108
+ INLINE.key?(name.to_sym)
109
+ end
110
+
111
+ defblock :read, 0
112
+ defblock :lead, 0
113
+ defblock :list, 2..3
114
+ defblock :emlist, 0..2
115
+ defblock :cmd, 0..1
116
+ defblock :table, 0..2
117
+ defblock :imgtable, 0..2
118
+ defblock :emtable, 0..1
119
+ defblock :quote, 0
120
+ defblock :image, 2..3, true
121
+ defblock :source, 0..2
122
+ defblock :listnum, 2..3
123
+ defblock :emlistnum, 0..2
124
+ defblock :bibpaper, 2..3, true
125
+ defblock :doorquote, 1
126
+ defblock :talk, 0
127
+ defblock :texequation, 0
128
+ defblock :graph, 1..3
129
+ defblock :indepimage, 1..3, true
130
+ defblock :numberlessimage, 1..3, true
131
+
132
+ defblock :address, 0
133
+ defblock :blockquote, 0
134
+ defblock :bpo, 0
135
+ defblock :flushright, 0
136
+ defblock :centering, 0
137
+ defblock :note, 0..1
138
+ defblock :memo, 0..1
139
+ defblock :info, 0..1
140
+ defblock :important, 0..1
141
+ defblock :caution, 0..1
142
+ defblock :notice, 0..1
143
+ defblock :warning, 0..1
144
+ defblock :tip, 0..1
145
+ defblock :box, 0..1
146
+ defblock :comment, 0..1, true
147
+ defblock :embed, 0..1
148
+
149
+ defsingle :footnote, 2
150
+ defsingle :noindent, 0
151
+ defsingle :blankline, 0
152
+ defsingle :pagebreak, 0
153
+ defsingle :hr, 0
154
+ defsingle :parasep, 0
155
+ defsingle :label, 1
156
+ defsingle :raw, 1
157
+ defsingle :tsize, 1
158
+ defsingle :include, 1
159
+ defsingle :olnum, 1
160
+ defsingle :firstlinenum, 1
161
+
162
+ definline :chapref
163
+ definline :chap
164
+ definline :title
165
+ definline :img
166
+ definline :imgref
167
+ definline :icon
168
+ definline :list
169
+ definline :table
170
+ definline :fn
171
+ definline :kw
172
+ definline :ruby
173
+ definline :bou
174
+ definline :ami
175
+ definline :b
176
+ definline :dtp
177
+ definline :code
178
+ definline :bib
179
+ definline :hd
180
+ definline :href
181
+ definline :recipe
182
+ definline :column
183
+ definline :tcy
184
+
185
+ definline :abbr
186
+ definline :acronym
187
+ definline :cite
188
+ definline :dfn
189
+ definline :em
190
+ definline :kbd
191
+ definline :q
192
+ definline :samp
193
+ definline :strong
194
+ definline :var
195
+ definline :big
196
+ definline :small
197
+ definline :del
198
+ definline :ins
199
+ definline :sup
200
+ definline :sub
201
+ definline :tt
202
+ definline :i
203
+ definline :tti
204
+ definline :ttb
205
+ definline :u
206
+ definline :raw
207
+ definline :br
208
+ definline :m
209
+ definline :uchar
210
+ definline :idx
211
+ definline :hidx
212
+ definline :comment
213
+ definline :include
214
+ definline :tcy
215
+ definline :embed
216
+ definline :pageref
217
+
218
+ private
219
+
220
+ def do_compile
221
+ f = LineInput.new(StringIO.new(@chapter.content))
222
+ @strategy.bind self, @chapter, Location.new(@chapter.basename, f)
223
+ tagged_section_init
224
+ while f.next?
225
+ case f.peek
226
+ when /\A\#@/
227
+ f.gets # Nothing to do
228
+ when /\A=+[\[\s\{]/
229
+ compile_headline f.gets
230
+ when /\A\s+\*/
231
+ compile_ulist f
232
+ when /\A\s+\d+\./
233
+ compile_olist f
234
+ when /\A\s*:\s/
235
+ compile_dlist f
236
+ when %r{\A//\}}
237
+ f.gets
238
+ error 'block end seen but not opened'
239
+ when %r{\A//[a-z]+}
240
+ name, args, lines = read_command(f)
241
+ syntax = syntax_descriptor(name)
242
+ unless syntax
243
+ error "unknown command: //#{name}"
244
+ compile_unknown_command args, lines
245
+ next
246
+ end
247
+ compile_command syntax, args, lines
248
+ when %r{\A//}
249
+ line = f.gets
250
+ warn "`//' seen but is not valid command: #{line.strip.inspect}"
251
+ if block_open?(line)
252
+ warn 'skipping block...'
253
+ read_block(f, false)
254
+ end
255
+ else
256
+ if f.peek.strip.empty?
257
+ f.gets
258
+ next
259
+ end
260
+ compile_paragraph f
261
+ end
262
+ end
263
+ close_all_tagged_section
264
+ end
265
+
266
+ def compile_headline(line)
267
+ @headline_indexs ||= [@chapter.number.to_i - 1]
268
+ m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line)
269
+ level = m[1].size
270
+ tag = m[2]
271
+ label = m[3]
272
+ caption = m[4].strip
273
+ index = level - 1
274
+ if tag
275
+ if tag !~ %r{\A/}
276
+ if caption.empty?
277
+ warn 'headline is empty.'
278
+ end
279
+ close_current_tagged_section(level)
280
+ open_tagged_section(tag, level, label, caption)
281
+ else
282
+ open_tag = tag[1..-1]
283
+ prev_tag_info = @tagged_section.pop
284
+ if prev_tag_info.nil? || prev_tag_info.first != open_tag
285
+ error "#{open_tag} is not opened."
286
+ end
287
+ close_tagged_section(*prev_tag_info)
288
+ end
289
+ else
290
+ if caption.empty?
291
+ warn 'headline is empty.'
292
+ end
293
+ if @headline_indexs.size > (index + 1)
294
+ @headline_indexs = @headline_indexs[0..index]
295
+ end
296
+ if @headline_indexs[index].nil?
297
+ @headline_indexs[index] = 0
298
+ end
299
+ @headline_indexs[index] += 1
300
+ close_current_tagged_section(level)
301
+ @strategy.headline level, label, caption
302
+ end
303
+ end
304
+
305
+ def close_current_tagged_section(level)
306
+ while @tagged_section.last and @tagged_section.last[1] >= level
307
+ close_tagged_section(* @tagged_section.pop)
308
+ end
309
+ end
310
+
311
+ def headline(level, label, caption)
312
+ @strategy.headline level, label, caption
313
+ end
314
+
315
+ def tagged_section_init
316
+ @tagged_section = []
317
+ end
318
+
319
+ def open_tagged_section(tag, level, label, caption)
320
+ mid = "#{tag}_begin"
321
+ unless @strategy.respond_to?(mid)
322
+ error "strategy does not support tagged section: #{tag}"
323
+ headline level, label, caption
324
+ return
325
+ end
326
+ @tagged_section.push [tag, level]
327
+ @strategy.__send__ mid, level, label, caption
328
+ end
329
+
330
+ def close_tagged_section(tag, level)
331
+ mid = "#{tag}_end"
332
+ if @strategy.respond_to?(mid)
333
+ @strategy.__send__ mid, level
334
+ else
335
+ error "strategy does not support block op: #{mid}"
336
+ end
337
+ end
338
+
339
+ def close_all_tagged_section
340
+ until @tagged_section.empty?
341
+ close_tagged_section(* @tagged_section.pop)
342
+ end
343
+ end
344
+
345
+ def compile_ulist(f)
346
+ level = 0
347
+ f.while_match(/\A\s+\*|\A\#@/) do |line|
348
+ next if line =~ /\A\#@/
349
+
350
+ buf = [text(line.sub(/\*+/, '').strip)]
351
+ f.while_match(/\A\s+(?!\*)\S/) do |cont|
352
+ buf.push text(cont.strip)
353
+ end
354
+
355
+ line =~ /\A\s+(\*+)/
356
+ current_level = $1.size
357
+ if level == current_level
358
+ @strategy.ul_item_end
359
+ # body
360
+ @strategy.ul_item_begin buf
361
+ elsif level < current_level # down
362
+ level_diff = current_level - level
363
+ level = current_level
364
+ (1..(level_diff - 1)).to_a.reverse_each do |i|
365
+ @strategy.ul_begin { i }
366
+ @strategy.ul_item_begin []
367
+ end
368
+ @strategy.ul_begin { level }
369
+ @strategy.ul_item_begin buf
370
+ elsif level > current_level # up
371
+ level_diff = level - current_level
372
+ level = current_level
373
+ (1..level_diff).to_a.reverse_each do |i|
374
+ @strategy.ul_item_end
375
+ @strategy.ul_end { level + i }
376
+ end
377
+ @strategy.ul_item_end
378
+ # body
379
+ @strategy.ul_item_begin buf
380
+ end
381
+ end
382
+
383
+ (1..level).to_a.reverse_each do |i|
384
+ @strategy.ul_item_end
385
+ @strategy.ul_end { i }
386
+ end
387
+ end
388
+
389
+ def compile_olist(f)
390
+ @strategy.ol_begin
391
+ f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
392
+ next if line =~ /\A\#@/
393
+
394
+ num = line.match(/(\d+)\./)[1]
395
+ buf = [text(line.sub(/\d+\./, '').strip)]
396
+ f.while_match(/\A\s+(?!\d+\.)\S/) do |cont|
397
+ buf.push text(cont.strip)
398
+ end
399
+ @strategy.ol_item buf, num
400
+ end
401
+ @strategy.ol_end
402
+ end
403
+
404
+ def compile_dlist(f)
405
+ @strategy.dl_begin
406
+ while /\A\s*:/ =~ f.peek
407
+ @strategy.dt text(f.gets.sub(/\A\s*:/, '').strip)
408
+ desc = f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) }
409
+ @strategy.dd(desc)
410
+ f.skip_blank_lines
411
+ f.skip_comment_lines
412
+ end
413
+ @strategy.dl_end
414
+ end
415
+
416
+ def compile_paragraph(f)
417
+ buf = []
418
+ f.until_match(%r{\A//|\A\#@}) do |line|
419
+ break if line.strip.empty?
420
+ buf.push text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t"))
421
+ end
422
+ @strategy.paragraph buf
423
+ end
424
+
425
+ def read_command(f)
426
+ line = f.gets
427
+ name = line.slice(/[a-z]+/).to_sym
428
+ ignore_inline = (name == :embed)
429
+ args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name)
430
+ @strategy.doc_status[name] = true
431
+ lines = block_open?(line) ? read_block(f, ignore_inline) : nil
432
+ @strategy.doc_status[name] = nil
433
+ [name, args, lines]
434
+ end
435
+
436
+ def block_open?(line)
437
+ line.rstrip[-1, 1] == '{'
438
+ end
439
+
440
+ def read_block(f, ignore_inline)
441
+ head = f.lineno
442
+ buf = []
443
+ f.until_match(%r{\A//\}}) do |line|
444
+ if ignore_inline
445
+ buf.push line
446
+ elsif line !~ /\A\#@/
447
+ buf.push text(line.rstrip)
448
+ end
449
+ end
450
+ unless %r{\A//\}} =~ f.peek
451
+ error "unexpected EOF (block begins at: #{head})"
452
+ return buf
453
+ end
454
+ f.gets # discard terminator
455
+ buf
456
+ end
457
+
458
+ def parse_args(str, _name = nil)
459
+ return [] if str.empty?
460
+ scanner = StringScanner.new(str)
461
+ words = []
462
+ while word = scanner.scan(/(\[\]|\[.*?[^\\]\])/)
463
+ w2 = word[1..-2].gsub(/\\(.)/) do
464
+ ch = $1
465
+ (ch == ']' or ch == '\\') ? ch : '\\' + ch
466
+ end
467
+ words << w2
468
+ end
469
+ unless scanner.eos?
470
+ error "argument syntax error: #{scanner.rest} in #{str.inspect}"
471
+ return []
472
+ end
473
+ words
474
+ end
475
+
476
+ def compile_command(syntax, args, lines)
477
+ unless @strategy.respond_to?(syntax.name)
478
+ error "strategy does not support command: //#{syntax.name}"
479
+ compile_unknown_command args, lines
480
+ return
481
+ end
482
+ begin
483
+ syntax.check_args args
484
+ rescue CompileError => err
485
+ error err.message
486
+ args = ['(NoArgument)'] * syntax.min_argc
487
+ end
488
+ if syntax.block_allowed?
489
+ compile_block syntax, args, lines
490
+ else
491
+ if lines
492
+ error "block is not allowed for command //#{syntax.name}; ignore"
493
+ end
494
+ compile_single syntax, args
495
+ end
496
+ end
497
+
498
+ def compile_unknown_command(args, lines)
499
+ @strategy.unknown_command args, lines
500
+ end
501
+
502
+ def compile_block(syntax, args, lines)
503
+ @strategy.__send__(syntax.name, (lines || default_block(syntax)), *args)
504
+ end
505
+
506
+ def default_block(syntax)
507
+ if syntax.block_required?
508
+ error "block is required for //#{syntax.name}; use empty block"
509
+ end
510
+ []
511
+ end
512
+
513
+ def compile_single(syntax, args)
514
+ @strategy.__send__(syntax.name, *args)
515
+ end
516
+
517
+ def replace_fence(str)
518
+ str.gsub(/@<(\w+)>([$|])(.+?)(\2)/) do
519
+ op = $1
520
+ arg = $3.gsub('@', "\x01").gsub('\\}') { '\\\\}' }.gsub('}') { '\}' }.sub(/(?:\\)+$/) { |m| '\\\\' * m.size }
521
+ "@<#{op}>{#{arg}}"
522
+ end
523
+ end
524
+
525
+ def text(str)
526
+ return '' if str.empty?
527
+ words = replace_fence(str).split(/(@<\w+>\{(?:[^\}\\]|\\.)*?\})/, -1)
528
+ words.each do |w|
529
+ if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
530
+ error "`@<xxx>' seen but is not valid inline op: #{w}"
531
+ end
532
+ end
533
+ result = @strategy.nofunc_text(words.shift)
534
+ until words.empty?
535
+ result << compile_inline(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\'))
536
+ result << @strategy.nofunc_text(words.shift)
537
+ end
538
+ result.gsub("\x01", '@')
539
+ rescue => err
540
+ error err.message
541
+ end
542
+ public :text # called from strategy
543
+
544
+ def compile_inline(str)
545
+ op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures
546
+ unless inline_defined?(op)
547
+ raise CompileError, "no such inline op: #{op}"
548
+ end
549
+ unless @strategy.respond_to?("inline_#{op}")
550
+ raise "strategy does not support inline op: @<#{op}>"
551
+ end
552
+ @strategy.__send__("inline_#{op}", arg)
553
+ rescue => err
554
+ error err.message
555
+ @strategy.nofunc_text(str)
556
+ end
557
+
558
+ def warn(msg)
559
+ @strategy.warn msg
560
+ end
561
+
562
+ def error(msg)
563
+ @strategy.error msg
564
+ end
565
+
566
+ =end
567
+
568
+ #------------------------------
569
+
18
570
  ## ブロック命令
19
571
  defblock :program, 0..3 ## プログラム
20
572
  defblock :terminal, 0..3 ## ターミナル
573
+ defblock :output, 0..3 ## 出力結果
21
574
  defblock :sideimage, 2..3 ## テキストの横に画像を表示
22
575
  defblock :abstract, 0 ## 章の概要
576
+ defblock :chapterauthor, 1 ## 章の著者
577
+ defblock :talklist, 0..1 ## 会話リスト
578
+ defblock :talk, 1..3, true ## 会話項目
579
+ defblock :t, 1..3, true ## 会話項目(ショートカット用)
580
+ defblock :desclist, 0..1 ## キーと説明文のリスト
581
+ defblock :desc, 1..2, true ## キーと説明文のリスト
23
582
  defblock :list, 0..3 ## (上書き)
24
583
  defblock :listnum, 0..3 ## (上書き)
25
584
  defblock :note, 0..2 ## (上書き)
26
585
  defblock :texequation, 0..2 ## (上書き)
586
+ defblock :table, 0..3 ## (上書き)
587
+ defblock :imgtable, 0..3 ## (上書き)
27
588
 
28
589
  defsingle :makechaptitlepage, 0..1 ## 章扉をつける
29
590
  defsingle :needvspace, 2 ## 縦方向のスペースがなければ改ページ
30
591
  defsingle :paragraphend, 0 ## 段の終わりにスペースを入れる
31
592
  defsingle :subparagraphend, 0## 小段の終わりにスペースを入れる(あれば)
593
+ defsingle :vspace, 2 ## 縦方向の空きを入れる(\vspace)
594
+ defsingle :addvspace, 2 ## 縦方向の空きを入れる(\addvspace)
595
+ defsingle :tsize, 1..2 ## (上書き)
32
596
 
33
597
  ## インライン命令
598
+ definline :par ## 箇条書き内で改段落するのに使う
34
599
  definline :balloon ## コード内でのふきだし説明(Re:VIEW3から追加)
35
600
  definline :eq ## 数式を参照
36
601
  definline :secref ## 節(Section)や項(Subsection)を参照
37
602
  definline :noteref ## ノートを参照
38
603
  definline :hlink ## @<href>{}の代わり
604
+ definline :term ## @<idx>{}かつゴシック体
605
+ definline :termnoidx ## ゴシック体にするだけで索引には登録しない
39
606
  definline :file ## ファイル名
40
607
  definline :userinput ## ユーザ入力
41
608
  definline :nop ## 引数をそのまま表示 (No Operation)
42
609
  definline :letitgo ## (nopのエイリアス名)
43
610
  definline :foldhere ## 折り返し箇所を手動で指定
44
611
  definline :cursor ## ターミナルでのカーソル
612
+ definline :qq ## 「``」と「''」で囲う
45
613
  definline :weak ## 目立たせない(@<strong>{} の反対)
46
614
  definline :small ## 文字サイズを小さく
47
615
  definline :xsmall ## 文字サイズをもっと小さく
@@ -51,6 +619,9 @@ module ReVIEW
51
619
  definline :xxlarge ## 文字サイズをもっともっと大きく
52
620
  definline :xstrong ## 文字を大きくした@<strong>{}
53
621
  definline :xxstrong ## 文字をもっと大きくした@<strong>{}
622
+ definline :w ## キーに対応した単語に展開
623
+ definline :wb ## キーに対応した単語に展開、かつ太字で表示
624
+ definline :W ## キーに対応した単語に展開、かつ強調表示
54
625
 
55
626
  private
56
627
 
@@ -59,11 +630,13 @@ module ReVIEW
59
630
  def do_compile
60
631
  f = LineInput.new(StringIO.new(@chapter.content))
61
632
  @strategy.bind self, @chapter, Location.new(@chapter.basename, f)
62
- tagged_section_init
633
+ tagged_section_init()
63
634
  parse_document(f, false)
64
- close_all_tagged_section
635
+ close_all_tagged_section()
65
636
  end
66
637
 
638
+ BLOCK_END_REXP = /\A\/\/\}\s*$/
639
+
67
640
  def parse_document(f, block_cmd)
68
641
  while f.next?
69
642
  case f.peek
@@ -83,7 +656,8 @@ module ReVIEW
83
656
  compile_olist f
84
657
  when /\A\s*:\s/
85
658
  compile_dlist f
86
- when %r{\A//\}}
659
+ #when %r{\A//\}} #-
660
+ when BLOCK_END_REXP #+
87
661
  return if block_cmd #+
88
662
  f.gets
89
663
  #error 'block end seen but not opened' #-
@@ -126,35 +700,59 @@ module ReVIEW
126
700
  ## ・'//table' と '//embed' ではタブ文字の展開は行わない。
127
701
  def read_block_for(cmdname, f) # 追加
128
702
  disable_comment = cmdname == :embed # '//embed' では行コメントを読み飛ばさない
129
- ignore_inline = cmdname == :embed # '//embed' ではインライン命令を解釈しない
703
+ ignore_inline = _ignore_inline?(cmdname) # '//embed' と '//table' ではインライン命令を解釈しない
130
704
  enable_detab = cmdname !~ /\A(?:em)?table\z/ # '//table' ではタブ展開しない
131
705
  f.enable_comment(false) if disable_comment
132
- lines = read_block(f, ignore_inline, enable_detab)
706
+ lines = read_block(f, ignore_inline, enable_detab) { "//#{cmdname}" }
133
707
  f.enable_comment(true) if disable_comment
134
708
  return lines
135
709
  end
710
+ def _ignore_inline?(cmdname)
711
+ return RAW_BLOCK_COMMANDS[cmdname]
712
+ end
136
713
  def read_block(f, ignore_inline, enable_detab=true) # 上書き
137
714
  head = f.lineno
138
715
  buf = []
139
716
  builder = @strategy #+
140
- f.until_match(%r{\A//\}}) do |line|
717
+ #f.until_match(%r{\A//\}}) do |line| #-
718
+ f.until_match(BLOCK_END_REXP) do |line| #+
141
719
  if ignore_inline
142
720
  buf.push line
143
721
  elsif line !~ /\A\#@/
144
722
  #buf.push text(line.rstrip) #-
145
723
  line = line.rstrip #+
146
724
  line = builder.detab(line) if enable_detab #+
147
- buf << text(line) #+
725
+ buf << parse_text(line) #+
148
726
  end
149
727
  end
150
- unless %r{\A//\}} =~ f.peek
151
- error "unexpected EOF (block begins at: #{head})"
728
+ #unless %r{\A//\}} =~ f.peek #-
729
+ unless f.peek() =~ BLOCK_END_REXP #+
730
+ if block_given? #+
731
+ error "#{yield} (at line #{head}): block command not closed." #+
732
+ else #+
733
+ error "unexpected EOF (block begins at: #{head})"
734
+ end #+
152
735
  return buf
153
736
  end
154
737
  f.gets # discard terminator
155
738
  buf
156
739
  end
157
740
 
741
+ RAW_BLOCK_COMMANDS = {
742
+ embed: true,
743
+ raw: true,
744
+ table: true,
745
+ list: true,
746
+ emlist: true,
747
+ listnum: true,
748
+ emlistnum: true,
749
+ source: true,
750
+ program: true,
751
+ terminal: true,
752
+ cmd: true,
753
+ output: true,
754
+ }
755
+
158
756
  ## ブロック命令を入れ子可能に変更('//note' と '//quote')
159
757
 
160
758
  def parse_block_command(f)
@@ -188,7 +786,7 @@ module ReVIEW
188
786
  parse_document(f, cmdname)
189
787
  end
190
788
  s = f.peek()
191
- f.peek() =~ /\A\/\/}/ or
789
+ f.peek() =~ BLOCK_END_REXP or
192
790
  error "'//#{cmdname}': not closed (reached to EOF)"
193
791
  f.gets() ## '//}' を読み捨てる
194
792
  else
@@ -275,6 +873,57 @@ module ReVIEW
275
873
  return line
276
874
  end
277
875
 
876
+ def compile_dlist(f)
877
+ @strategy.dl_begin()
878
+ while /\A\s*:/ =~ f.peek()
879
+ dtext = f.gets.sub(/\A\s*:/, '').strip
880
+ @strategy.dt(parse_text(dtext))
881
+ buf = []
882
+ li_rexp = LIST_ITEM_REXP
883
+ indent = nil
884
+ first_p = true
885
+ @strategy.dl_dd_begin()
886
+ while (line = f.peek()) =~ /\A( [ \t]+|\t\s*)/ || line =~ /\A\s*$/
887
+ indent ||= $1
888
+ if indent
889
+ line =~ /\A\s*$/ || line.start_with?(indent) or
890
+ warn "` : #{dtext}': indent mismatched (maybe space and tab are mixed)"
891
+ if _dl_start_list?(line, indent, li_rexp)
892
+ buf, first_p = _dl_par(buf, first_p) unless buf.empty?
893
+ compile_list(f)
894
+ @strategy.noindent # disable indent just after ordered/unordered list
895
+ next
896
+ end
897
+ end
898
+ if line =~ /\A\s*$/
899
+ buf, first_p = _dl_par(buf, first_p) unless buf.empty?
900
+ else
901
+ buf << parse_text(line)
902
+ end
903
+ f.gets()
904
+ end
905
+ _, _ = _dl_par(buf, first_p) unless buf.empty?
906
+ @strategy.dl_dd_end()
907
+ f.skip_blank_lines()
908
+ f.skip_comment_lines()
909
+ end
910
+ @strategy.dl_end()
911
+ end
912
+
913
+ def _dl_start_list?(line, indent, li_rexp)
914
+ return line.start_with?(indent) && line.sub(indent, '') =~ li_rexp && line =~ li_rexp
915
+ end
916
+
917
+ def _dl_par(buf, first_p)
918
+ return buf, first_p if buf.empty?
919
+ if first_p
920
+ @strategy.puts buf.join
921
+ else
922
+ @strategy.paragraph(buf)
923
+ end
924
+ return [], false
925
+ end
926
+
278
927
  public
279
928
 
280
929
  ## 入れ子のインライン命令をパースできるよう上書き
@@ -366,10 +1015,9 @@ module ReVIEW
366
1015
  elsif strategy.respond_to?("inline_#{op}")
367
1016
  children.empty? || children.all? {|x| x.is_a?(String) } or
368
1017
  error "'@<#{op}>' does not support nested inline commands."
369
-
370
1018
  buf << strategy.__send__("inline_#{op}", children[0])
371
1019
  else
372
- error "strategy does not support inline op: @<#{op}>"
1020
+ error "strategy does not support inline op: @<#{op}> (strategy.class=#{strategy.class})"
373
1021
  end
374
1022
  else
375
1023
  raise "internal error: x=#{x.inspect}"
@@ -382,7 +1030,7 @@ module ReVIEW
382
1030
  return IGNORE_NESTED_INLINE_COMMANDS.include?(tag_name)
383
1031
  end
384
1032
 
385
- IGNORE_NESTED_INLINE_COMMANDS = Set.new(['m', 'raw', 'embed'])
1033
+ IGNORE_NESTED_INLINE_COMMANDS = Set.new(['m', 'raw', 'embed', 'idx', 'hidx', 'term'])
386
1034
 
387
1035
  end
388
1036
 
@@ -424,22 +1072,22 @@ module ReVIEW
424
1072
 
425
1073
  class Book::ListIndex
426
1074
 
427
- ## '//program' と '//terminal' をサポートするよう拡張
1075
+ ## '//program' と '//terminal' と '//output' をサポートするよう拡張
428
1076
  def self.item_type # override
429
1077
  #'(list|listnum)' # original
430
- '(list|listnum|program|terminal)'
1078
+ '(list|listnum|program|terminal|output)'
431
1079
  end
432
1080
 
433
1081
  ## '//list' や '//terminal' のラベル(第1引数)を省略できるよう拡張
434
1082
  def self.parse(src, *args) # override
435
1083
  items = []
436
1084
  seq = 1
437
- src.grep(%r{\A//#{item_type}}) do |line|
1085
+ src.grep(%r{\A//#{item_type()}}) do |line|
438
1086
  if id = line.slice(/\[(.*?)\]/, 1)
439
1087
  next if id.empty? # 追加
440
- items.push item_class.new(id, seq)
1088
+ items.push item_class().new(id, seq)
441
1089
  seq += 1
442
- ReVIEW.logger.warn "warning: no ID of #{item_type} in #{line}" if id.empty?
1090
+ ReVIEW.logger.warn "warning: no ID of #{item_type()} in #{line}" if id.empty?
443
1091
  end
444
1092
  end
445
1093
  new(items, *args)
@@ -491,7 +1139,7 @@ module ReVIEW
491
1139
  end
492
1140
 
493
1141
  def equation_index
494
- @equation_index ||= Book::EquationIndex.parse(lines)
1142
+ @equation_index ||= Book::EquationIndex.parse(lines())
495
1143
  @equation_index
496
1144
  end
497
1145
 
@@ -500,8 +1148,10 @@ module ReVIEW
500
1148
  ## こうすると、重複しないラベルをいちいち指定しなくても、ソースコードや
501
1149
  ## ターミナルにリスト番号がつく。ただし @<list>{} での参照はできない。
502
1150
  unless @_done
503
- pat = Book::ListIndex.item_type # == '(list|listnum|program|terminal)'
504
- @content = @content.gsub(/^\/\/#{pat}\[\?\]/) { "//#{$1}[#{_random_label()}]" }
1151
+ pat1 = Book::ListIndex.item_type # == '(list|listnum|program|terminal|output)'
1152
+ pat2 = Book::TableIndex.item_type # == '(table|imgtable)'
1153
+ pat = "#{pat1[1..-2]}|#{pat2[1..-2]}"
1154
+ @content = @content.gsub(/^\/\/(#{pat})\[\?\]/) { "//#{$1}[#{_random_label()}]" }
505
1155
  ## 改行コードを「\n」に統一する
506
1156
  @content = @content.gsub(/\r\n/, "\n")
507
1157
  ## (experimental) 範囲コメント('#@+++' '#@---')を行コメント('#@#')に変換
@@ -514,9 +1164,12 @@ module ReVIEW
514
1164
  module_function
515
1165
 
516
1166
  def _random_label
517
- "_" + rand().to_s[2..10]
1167
+ #"_" + rand().to_s[2..10]
1168
+ "_" + RANDOM.rand().to_s[2..10]
518
1169
  end
519
1170
 
1171
+ RANDOM = Random.new(22360679)
1172
+
520
1173
  end
521
1174
 
522
1175