review-retrovert 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
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