Class: ReVIEW::Builder

Inherits:
Object show all
Includes:
TextUtils
Defined in:
lib/review/builder.rb

Constant Summary collapse

CAPTION_TITLES =
%w[note memo tip info warning important caution notice].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from TextUtils

#add_space?, #defer_math_image, #join_lines_to_paragraph, #split_paragraph

Constructor Details

#initialize(strict = false, *args) ⇒ Builder

Returns a new instance of Builder.



35
36
37
38
39
40
41
42
# File 'lib/review/builder.rb', line 35

def initialize(strict = false, *args)
  @strict = strict
  @output = nil
  @logger = ReVIEW.logger
  @doc_status = {}
  @dictionary = {}
  builder_init(*args)
end

Instance Attribute Details

#doc_statusObject

Returns the value of attribute doc_status.



33
34
35
# File 'lib/review/builder.rb', line 33

def doc_status
  @doc_status
end

Instance Method Details

#bibpaper(lines, id, caption) ⇒ Object



362
363
364
365
366
367
368
369
# File 'lib/review/builder.rb', line 362

def bibpaper(lines, id, caption)
  bibpaper_header(id, caption)
  unless lines.empty?
    puts
    bibpaper_bibpaper(id, caption, lines)
  end
  puts
end

#bind(compiler, chapter, location) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/review/builder.rb', line 48

def bind(compiler, chapter, location)
  @compiler = compiler
  @chapter = chapter
  @location = location
  @output = StringIO.new
  if @chapter.present?
    @book = @chapter.book
  end
  @tabwidth = nil
  @tsize = nil
  if @book && @book.config
    if @book.config['words_file']
      load_words(@book.config['words_file'])
    end
    if @book.config['tabwidth']
      @tabwidth = @book.config['tabwidth']
    end

    if @book.config['join_lines_by_lang']
      begin
        require 'unicode/eaw'
      rescue LoadError
        warn 'not found unicode/eaw. disabled join_lines_by_lang feature.'
        @book.config['join_lines_by_lang'] = nil
      end
    end
  end
  builder_init_file
end

#blanklineObject

def footnote(id, str)

@footnotes.push [id, str]

end

def flush_footnote

footnote_begin
@footnotes.each do |id, str|
  footnote_item(id, str)
end
footnote_end

end



243
244
245
# File 'lib/review/builder.rb', line 243

def blankline
  puts ''
end

#captionblock(_type, _lines, _caption, _specialstyle = nil) ⇒ Object

Raises:

  • (NotImplementedError)


512
513
514
# File 'lib/review/builder.rb', line 512

def captionblock(_type, _lines, _caption, _specialstyle = nil)
  raise NotImplementedError
end

#compile_inline(s) ⇒ Object



247
248
249
# File 'lib/review/builder.rb', line 247

def compile_inline(s)
  @compiler.text(s)
end

#detab(str, num = nil) ⇒ Object

override TextUtils::detab



648
649
650
651
652
653
654
655
656
# File 'lib/review/builder.rb', line 648

def detab(str, num = nil)
  if num
    super(str, num)
  elsif @tabwidth
    super(str, @tabwidth)
  else
    super(str)
  end
end

#embed(lines, arg = nil) ⇒ Object



446
447
448
449
450
451
452
453
454
# File 'lib/review/builder.rb', line 446

def embed(lines, arg = nil)
  if arg
    builders = arg.gsub(/^\s*\|/, '').gsub(/\|\s*$/, '').gsub(/\s/, '').split(',')
    c = target_name
    print lines.join("\n") + "\n" if builders.include?(c)
  else
    print lines.join("\n") + "\n"
  end
end

#emtable(lines, caption = nil) ⇒ Object



227
228
229
# File 'lib/review/builder.rb', line 227

def emtable(lines, caption = nil)
  table(lines, nil, caption)
end

#error(msg) ⇒ Object



460
461
462
463
464
465
466
# File 'lib/review/builder.rb', line 460

def error(msg)
  if msg =~ /:\d+: error: /
    raise ApplicationError, msg
  else
    raise ApplicationError, "#{@location}: error: #{msg}"
  end
end

#escape(str) ⇒ Object



658
659
660
# File 'lib/review/builder.rb', line 658

def escape(str)
  str
end

#extract_chapter_id(chap_ref) ⇒ Object



502
503
504
505
506
507
508
509
510
# File 'lib/review/builder.rb', line 502

def extract_chapter_id(chap_ref)
  m = /\A([\w+-]+)\|(.+)/.match(chap_ref)
  if m
    ch = @book.contents.detect { |chap| chap.id == m[1] }
    raise KeyError unless ch
    return [ch, m[2]]
  end
  [@chapter, chap_ref]
end

#firstlinenum(num) ⇒ Object

for //firstlinenum



124
125
126
# File 'lib/review/builder.rb', line 124

def firstlinenum(num)
  @first_line_num = num.to_i
end

#get_chap(chapter = @chapter) ⇒ Object



491
492
493
494
495
496
497
498
499
500
# File 'lib/review/builder.rb', line 491

def get_chap(chapter = @chapter)
  if @book.config['secnolevel'] > 0 && !chapter.number.nil? && !chapter.number.to_s.empty?
    if chapter.is_a?(ReVIEW::Book::Part)
      return I18n.t('part_short', chapter.number)
    else
      return chapter.format_number(nil)
    end
  end
  nil
end

#graph(lines, id, command, caption = '') ⇒ Object



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/review/builder.rb', line 524

def graph(lines, id, command, caption = '')
  c = target_name
  dir = File.join(@book.imagedir, c)
  FileUtils.mkdir_p(dir)
  file = "#{id}.#{image_ext}"
  file_path = File.join(dir, file)

  content = lines.join("\n") + "\n"

  tf = Tempfile.new('review_graph')
  tf.puts content
  tf.close
  begin
    file_path = send("graph_#{command}".to_sym, id, file_path, content, tf.path)
  ensure
    tf.unlink
  end
  @chapter.image_index.image_finder.add_entry(file_path)

  image(lines, id, caption)
end

#graph_aafigure(id, file_path, _line, tf_path) ⇒ Object



573
574
575
576
# File 'lib/review/builder.rb', line 573

def graph_aafigure(id, file_path, _line, tf_path)
  system_graph(id, 'aafigure', '-t', image_ext, '-o', file_path, tf_path)
  file_path
end

#graph_blockdiag(id, file_path, _line, tf_path) ⇒ Object



568
569
570
571
# File 'lib/review/builder.rb', line 568

def graph_blockdiag(id, file_path, _line, tf_path)
  system_graph(id, 'blockdiag', '-a', '-T', image_ext, '-o', file_path, tf_path)
  file_path
end

#graph_gnuplot(id, file_path, line, tf_path) ⇒ Object



556
557
558
559
560
561
562
563
564
565
566
# File 'lib/review/builder.rb', line 556

def graph_gnuplot(id, file_path, line, tf_path)
  File.open(tf_path, 'w') do |tf|
    tf.puts <<EOTGNUPLOT
set terminal #{image_ext == 'eps' ? 'postscript eps' : image_ext}
set output "#{file_path}"
#{line}
EOTGNUPLOT
  end
  system_graph(id, 'gnuplot', tf_path)
  file_path
end

#graph_graphviz(id, file_path, _line, tf_path) ⇒ Object



551
552
553
554
# File 'lib/review/builder.rb', line 551

def graph_graphviz(id, file_path, _line, tf_path)
  system_graph(id, 'dot', "-T#{image_ext}", "-o#{file_path}", tf_path)
  file_path
end

#graph_plantuml(id, file_path, _line, tf_path) ⇒ Object



578
579
580
581
582
583
584
585
586
587
# File 'lib/review/builder.rb', line 578

def graph_plantuml(id, file_path, _line, tf_path)
  ext = image_ext
  if ext == 'pdf'
    ext = 'eps'
    file_path.sub!(/\.pdf\Z/, '.eps')
  end
  system_graph(id, 'java', '-jar', 'plantuml.jar', "-t#{ext}", '-charset', 'UTF-8', tf_path)
  FileUtils.mv("#{tf_path}.#{ext}", file_path)
  file_path
end

#handle_metric(str) ⇒ Object



468
469
470
# File 'lib/review/builder.rb', line 468

def handle_metric(str)
  str
end

#highlight?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/review/builder.rb', line 83

def highlight?
  false
end

#image(lines, id, caption, metric = nil) ⇒ Object



159
160
161
162
163
164
165
166
# File 'lib/review/builder.rb', line 159

def image(lines, id, caption, metric = nil)
  if @chapter.image_bound?(id)
    image_image(id, caption, metric)
  else
    warn "image not bound: #{id}" if @strict
    image_dummy(id, caption, lines)
  end
end

#image_extObject

Raises:

  • (NotImplementedError)


589
590
591
# File 'lib/review/builder.rb', line 589

def image_ext
  raise NotImplementedError
end

#inline_balloon(arg) ⇒ Object



411
412
413
# File 'lib/review/builder.rb', line 411

def inline_balloon(arg)
  "#{arg}"
end

#inline_bou(str) ⇒ Object



329
330
331
# File 'lib/review/builder.rb', line 329

def inline_bou(str)
  text(str)
end

#inline_chap(id) ⇒ Object



257
258
259
260
261
# File 'lib/review/builder.rb', line 257

def inline_chap(id)
  @book.chapter_index.number(id)
rescue KeyError
  error "unknown chapter: #{id}"
end

#inline_chapref(id) ⇒ Object



251
252
253
254
255
# File 'lib/review/builder.rb', line 251

def inline_chapref(id)
  compile_inline(@book.chapter_index.display_string(id))
rescue KeyError
  error "unknown chapter: #{id}"
end

#inline_column(id) ⇒ Object



385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/review/builder.rb', line 385

def inline_column(id)
  m = /\A([^|]+)\|(.+)/.match(id)
  if m && m[1]
    chapter = @book.chapters.detect { |chap| chap.id == m[1] }
  end
  if chapter
    inline_column_chap(chapter, m[2])
  else
    inline_column_chap(@chapter, id)
  end
rescue KeyError
  error "unknown column: #{id}"
end

#inline_column_chap(chapter, id) ⇒ Object



399
400
401
# File 'lib/review/builder.rb', line 399

def inline_column_chap(chapter, id)
  I18n.t('column', chapter.column(id).caption)
end

#inline_embed(args) ⇒ Object



630
631
632
633
634
635
636
637
638
639
640
641
# File 'lib/review/builder.rb', line 630

def inline_embed(args)
  if matched = args.match(/\|(.*?)\|(.*)/)
    builders = matched[1].split(',').map { |i| i.gsub(/\s/, '') }
    if builders.include?(target_name)
      matched[2]
    else
      ''
    end
  else
    args
  end
end

#inline_eq(id) ⇒ Object



312
313
314
315
316
317
318
319
320
321
# File 'lib/review/builder.rb', line 312

def inline_eq(id)
  chapter, id = extract_chapter_id(id)
  if get_chap(chapter)
    %Q(#{I18n.t('equation')}#{I18n.t('format_number', [get_chap(chapter), chapter.equation(id).number])})
  else
    %Q(#{I18n.t('equation')}#{I18n.t('format_number_without_chapter', [chapter.equation(id).number])})
  end
rescue KeyError
  error "unknown equation: #{id}"
end

#inline_fn(id) ⇒ Object



323
324
325
326
327
# File 'lib/review/builder.rb', line 323

def inline_fn(id)
  @chapter.footnote(id).content
rescue KeyError
  error "unknown footnote: #{id}"
end

#inline_hd(id) ⇒ Object



371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/review/builder.rb', line 371

def inline_hd(id)
  m = /\A([^|]+)\|(.+)/.match(id)
  if m && m[1]
    chapter = @book.contents.detect { |chap| chap.id == m[1] }
  end
  if chapter
    inline_hd_chap(chapter, m[2])
  else
    inline_hd_chap(@chapter, id)
  end
rescue KeyError
  error "unknown headline: #{id}"
end

#inline_href(arg) ⇒ Object



349
350
351
352
353
354
355
356
# File 'lib/review/builder.rb', line 349

def inline_href(arg)
  url, label = *arg.scan(/(?:(?:(?:\\\\)*\\,)|[^,\\]+)+/).map(&:lstrip)
  url = url.gsub(/\\,/, ',').strip
  if label
    label = label.gsub(/\\,/, ',').strip
  end
  compile_href(url, label)
end

#inline_img(id) ⇒ Object



280
281
282
283
284
285
286
287
288
289
# File 'lib/review/builder.rb', line 280

def inline_img(id)
  chapter, id = extract_chapter_id(id)
  if get_chap(chapter)
    %Q(#{I18n.t('image')}#{I18n.t('format_number', [get_chap(chapter), chapter.image(id).number])})
  else
    %Q(#{I18n.t('image')}#{I18n.t('format_number_without_chapter', [chapter.image(id).number])})
  end
rescue KeyError
  error "unknown image: #{id}"
end

#inline_imgref(id) ⇒ Object



291
292
293
294
295
296
297
298
299
# File 'lib/review/builder.rb', line 291

def inline_imgref(id)
  img = inline_img(id)

  if @chapter.image(id).caption
    "#{img}#{I18n.t('image_quote', @chapter.image(id).caption)}"
  else
    img
  end
end

#inline_include(file_name) ⇒ Object



593
594
595
# File 'lib/review/builder.rb', line 593

def inline_include(file_name)
  compile_inline(File.read(file_name, mode: 'rt:BOM|utf-8').chomp)
end

#inline_kw(arg) ⇒ Object



344
345
346
347
# File 'lib/review/builder.rb', line 344

def inline_kw(arg)
  word, alt = *arg.split(',', 2)
  compile_kw(word, alt)
end

#inline_list(id) ⇒ Object



269
270
271
272
273
274
275
276
277
278
# File 'lib/review/builder.rb', line 269

def inline_list(id)
  chapter, id = extract_chapter_id(id)
  if get_chap(chapter)
    %Q(#{I18n.t('list')}#{I18n.t('format_number', [get_chap(chapter), chapter.list(id).number])})
  else
    %Q(#{I18n.t('list')}#{I18n.t('format_number_without_chapter', [chapter.list(id).number])})
  end
rescue KeyError
  error "unknown list: #{id}"
end

#inline_pageref(id) ⇒ Object



403
404
405
# File 'lib/review/builder.rb', line 403

def inline_pageref(id)
  "[link:#{id}]"
end

#inline_raw(args) ⇒ Object



616
617
618
619
620
621
622
623
624
625
626
627
628
# File 'lib/review/builder.rb', line 616

def inline_raw(args)
  if matched = args.match(/\|(.*?)\|(.*)/)
    builders = matched[1].split(',').map { |i| i.gsub(/\s/, '') }
    c = self.class.to_s.gsub('ReVIEW::', '').gsub('Builder', '').downcase
    if builders.include?(c)
      matched[2].gsub('\\n', "\n")
    else
      ''
    end
  else
    args.gsub('\\n', "\n")
  end
end

#inline_ruby(arg) ⇒ Object



333
334
335
336
337
338
339
340
341
342
# File 'lib/review/builder.rb', line 333

def inline_ruby(arg)
  base, *ruby = *arg.scan(/(?:(?:(?:\\\\)*\\,)|[^,\\]+)+/)
  if base
    base = base.gsub(/\\,/, ',')
  end
  if ruby
    ruby = ruby.join(',').gsub(/\\,/, ',')
  end
  compile_ruby(base, ruby)
end

#inline_table(id) ⇒ Object



301
302
303
304
305
306
307
308
309
310
# File 'lib/review/builder.rb', line 301

def inline_table(id)
  chapter, id = extract_chapter_id(id)
  if get_chap(chapter)
    %Q(#{I18n.t('table')}#{I18n.t('format_number', [get_chap(chapter), chapter.table(id).number])})
  else
    %Q(#{I18n.t('table')}#{I18n.t('format_number_without_chapter', [chapter.table(id).number])})
  end
rescue KeyError
  error "unknown table: #{id}"
end

#inline_tcy(arg) ⇒ Object



407
408
409
# File 'lib/review/builder.rb', line 407

def inline_tcy(arg)
  "#{arg}[rotate 90 degree]"
end

#inline_title(id) ⇒ Object



263
264
265
266
267
# File 'lib/review/builder.rb', line 263

def inline_title(id)
  compile_inline(@book.chapter_index.title(id))
rescue KeyError
  error "unknown chapter: #{id}"
end

#inline_w(s) ⇒ Object



415
416
417
418
419
420
421
422
423
# File 'lib/review/builder.rb', line 415

def inline_w(s)
  translated = @dictionary[s]
  if translated
    escape(translated)
  else
    warn "word not bound: #{s}"
    escape("[missing word: #{s}]")
  end
end

#inline_wb(s) ⇒ Object



425
426
427
428
429
430
431
432
# File 'lib/review/builder.rb', line 425

def inline_wb(s)
  translated = @dictionary[s]
  if translated
    inline_b(translated)
  else
    inline_b("[missing word: #{s}]")
  end
end

#line_numObject



128
129
130
131
132
133
134
# File 'lib/review/builder.rb', line 128

def line_num
  return 1 unless @first_line_num
  line_n = @first_line_num
  @first_line_num = nil

  line_n
end

#list(lines, id, caption, lang = nil) ⇒ Object



136
137
138
139
140
141
142
143
# File 'lib/review/builder.rb', line 136

def list(lines, id, caption, lang = nil)
  begin
    list_header(id, caption, lang)
  rescue KeyError
    error "no such list: #{id}"
  end
  list_body(id, lines, lang)
end

#listnum(lines, id, caption, lang = nil) ⇒ Object



145
146
147
148
149
150
151
152
# File 'lib/review/builder.rb', line 145

def listnum(lines, id, caption, lang = nil)
  begin
    list_header(id, caption, lang)
  rescue KeyError
    error "no such list: #{id}"
  end
  listnum_body(lines, lang)
end

#load_words(file) ⇒ Object



105
106
107
108
109
110
111
112
113
# File 'lib/review/builder.rb', line 105

def load_words(file)
  if File.exist?(file)
    if file =~ /\.csv\Z/i
      CSV.foreach(file) do |row|
        @dictionary[row[0]] = row[1]
      end
    end
  end
end

#over_secnolevel?(n) ⇒ Boolean

Returns:

  • (Boolean)


643
644
645
# File 'lib/review/builder.rb', line 643

def over_secnolevel?(n)
  @book.config['secnolevel'] >= n.to_s.split('.').size
end

#parse_metric(type, metric) ⇒ Object



476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/review/builder.rb', line 476

def parse_metric(type, metric)
  return '' if metric.blank?
  params = metric.split(/,\s*/)
  results = []
  params.each do |param|
    if param =~ /\A.+?::/
      next unless param =~ /\A#{type}::/
      param.sub!(/\A#{type}::/, '')
    end
    param2 = handle_metric(param)
    results.push(param2)
  end
  result_metric(results)
end

#parse_table_rows(lines) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/review/builder.rb', line 182

def parse_table_rows(lines)
  sepidx = nil
  rows = []
  lines.each_with_index do |line, idx|
    if /\A[\=\-]{12}/ =~ line || /\A[\=\{\-\}]{12}/ =~ line
      sepidx ||= idx
      next
    end
    rows.push(line.strip.split(/\t+/).map { |s| s.sub(/\A\./, '') })
  end
  rows = adjust_n_cols(rows)
  error 'no rows in the table' if rows.empty?
  [sepidx, rows]
end

#post_paragraphObject



29
30
31
# File 'lib/review/builder.rb', line 29

def post_paragraph
  nil
end

#pre_paragraphObject



25
26
27
# File 'lib/review/builder.rb', line 25

def pre_paragraph
  nil
end


93
94
95
# File 'lib/review/builder.rb', line 93

def print(*s)
  @output.print(*s)
end

#puts(*s) ⇒ Object



97
98
99
# File 'lib/review/builder.rb', line 97

def puts(*s)
  @output.puts(*s)
end

#raw(str) ⇒ Object



434
435
436
437
438
439
440
441
442
443
444
# File 'lib/review/builder.rb', line 434

def raw(str)
  if matched = str.match(/\|(.*?)\|(.*)/)
    builders = matched[1].split(',').map { |i| i.gsub(/\s/, '') }
    c = target_name
    if builders.include?(c)
      print matched[2].gsub('\\n', "\n")
    end
  else
    print str.gsub('\\n', "\n")
  end
end

#resultObject Also known as: raw_result



87
88
89
# File 'lib/review/builder.rb', line 87

def result
  @output.string
end

#result_metric(array) ⇒ Object



472
473
474
# File 'lib/review/builder.rb', line 472

def result_metric(array)
  array.join(',')
end

#source(lines, caption, lang = nil) ⇒ Object



154
155
156
157
# File 'lib/review/builder.rb', line 154

def source(lines, caption, lang = nil)
  source_header(caption)
  source_body(lines, lang)
end

#system_graph(id, *args) ⇒ Object



546
547
548
549
# File 'lib/review/builder.rb', line 546

def system_graph(id, *args)
  @logger.info args.join(' ')
  Kernel.system(*args) or @logger.error("failed to run command for id #{id}: #{args.join(' ')}")
end

#table(lines, id = nil, caption = nil) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/review/builder.rb', line 168

def table(lines, id = nil, caption = nil)
  sepidx, rows = parse_table_rows(lines)
  begin
    if caption.present?
      table_header(id, caption)
    end
  rescue KeyError
    error "no such table: #{id}"
  end
  table_begin(rows.first.size)
  table_rows(sepidx, rows)
  table_end
end

#table_rows(sepidx, rows) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/review/builder.rb', line 197

def table_rows(sepidx, rows)
  if sepidx
    sepidx.times do
      tr(rows.shift.map { |s| th(s) })
    end
    rows.each do |cols|
      tr(cols.map { |s| td(s) })
    end
  else
    rows.each do |cols|
      h, *cs = *cols
      tr([th(h)] + cs.map { |s| td(s) })
    end
  end
end

#target_nameObject



101
102
103
# File 'lib/review/builder.rb', line 101

def target_name
  self.class.to_s.gsub(/ReVIEW::/, '').gsub(/Builder/, '').downcase
end

#text(str) ⇒ Object



358
359
360
# File 'lib/review/builder.rb', line 358

def text(str)
  str
end

#tsize(str) ⇒ Object



604
605
606
607
608
609
610
611
612
613
614
# File 'lib/review/builder.rb', line 604

def tsize(str)
  if matched = str.match(/\A\|(.*?)\|(.*)/)
    builders = matched[1].split(',').map { |i| i.gsub(/\s/, '') }
    c = self.class.to_s.gsub('ReVIEW::', '').gsub('Builder', '').downcase
    if builders.include?(c)
      @tsize = matched[2]
    end
  else
    @tsize = str
  end
end

#ul_item_begin(lines) ⇒ Object



597
598
599
# File 'lib/review/builder.rb', line 597

def ul_item_begin(lines)
  ul_item(lines)
end

#ul_item_endObject



601
602
# File 'lib/review/builder.rb', line 601

def ul_item_end
end

#warn(msg) ⇒ Object



456
457
458
# File 'lib/review/builder.rb', line 456

def warn(msg)
  @logger.warn "#{@location}: #{msg}"
end