Module: VivlioStarter::CLI::Import::VerbatimRestorer

Defined in:
lib/vivlio_starter/cli/import/verbatim_restorer.rb

Overview

//output・//cmd の囲みの復元

Constant Summary collapse

CODE_DIRECTIVES =

1 命令=1 フェンスを出す Re:VIEW の逐語系ブロック(出現順の照合に使う)

%w[list listnum emlist emlistnum source cmd output terminal].freeze
WRAP_CLASSES =

囲み直す種別。list 系はソースコードなので素のフェンスのままでよい

{ 'output' => 'output', 'cmd' => 'terminal', 'terminal' => 'terminal' }.freeze
DIRECTIVE_LINE =
%r{\A//([a-z]+)(?:\[|\{)}
FENCE_OPEN =
/\A(`{3,})\S/
REVIEW_COMMENT =
/\A\#@\#/

Class Method Summary collapse

Class Method Details

.aligned?(names, fences, md_path) ⇒ Boolean

数が合わなければ当て推量をせず、章を丸ごと見送る

Returns:

  • (Boolean)


101
102
103
104
105
106
107
108
109
110
# File 'lib/vivlio_starter/cli/import/verbatim_restorer.rb', line 101

def aligned?(names, fences, md_path)
  return true if names.size == fences.size

  Common.log_warn(
    "  #{File.basename(md_path)}: 逐語ブロックの数が原稿と合わないため(原稿 #{names.size} / 変換後 #{fences.size})、" \
    '実行結果の囲みを復元できませんでした。',
    detail: '対処: 実行結果のブロックを :::{.output} … ::: で囲んでください(22 章「実行結果」)。'
  )
  false
end

.directive_names(re_lines) ⇒ Object

.re に現れた逐語系命令の名前を出現順に返す



71
72
73
74
75
76
77
78
# File 'lib/vivlio_starter/cli/import/verbatim_restorer.rb', line 71

def directive_names(re_lines)
  re_lines.filter_map do |line|
    next if REVIEW_COMMENT.match?(line)

    name = DIRECTIVE_LINE.match(line.to_s.chomp)&.[](1)
    name if CODE_DIRECTIVES.include?(name)
  end
end

.fence_spans(lines) ⇒ Object

.md のフェンスを [開き行, 閉じ行] の並びで返す



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/vivlio_starter/cli/import/verbatim_restorer.rb', line 81

def fence_spans(lines)
  spans = []
  index = 0

  while index < lines.size
    match = /\A(`{3,})/.match(lines[index].chomp)
    unless match
      index += 1
      next
    end

    close = MarkdownConverter.closing_fence_index(lines, index + 1, match[1])
    spans << [index, [close, lines.size - 1].min]
    index = close + 1
  end

  spans
end

.restore!(temp_dir, starter_dir) ⇒ Object

temp/ の Markdown に対して逐語ブロックの囲みを復元する



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/vivlio_starter/cli/import/verbatim_restorer.rb', line 44

def restore!(temp_dir, starter_dir)
  re_dir = SideimageRestorer.review_contents_dir(starter_dir)
  return unless Dir.exist?(re_dir)

  restored = Dir.glob(File.join(temp_dir, '*.md')).sum do |md_path|
    re_path = File.join(re_dir, "#{File.basename(md_path, '.md')}.re")
    File.exist?(re_path) ? restore_chapter!(md_path, re_path) : 0
  end

  Common.log_info("  実行結果・端末の囲みを #{restored} 件復元しました") if restored.positive?
end

.restore_chapter!(md_path, re_path) ⇒ Object

1 章ぶんを復元し、復元できた件数を返す



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/vivlio_starter/cli/import/verbatim_restorer.rb', line 57

def restore_chapter!(md_path, re_path)
  names = directive_names(File.readlines(re_path, encoding: 'utf-8'))
  return 0 if names.none? { WRAP_CLASSES.key?(it) }

  lines = File.readlines(md_path, encoding: 'utf-8')
  fences = fence_spans(lines)
  return 0 unless aligned?(names, fences, md_path)

  wrap_fences!(lines, names, fences).tap do |count|
    File.write(md_path, lines.join, encoding: 'utf-8') if count.positive?
  end
end

.wrap_fences!(lines, names, fences) ⇒ Object

行番号がずれないよう後ろから囲む



113
114
115
116
117
118
119
120
121
122
# File 'lib/vivlio_starter/cli/import/verbatim_restorer.rb', line 113

def wrap_fences!(lines, names, fences)
  targets = names.each_with_index.filter_map { |name, i| [fences[i], WRAP_CLASSES[name]] if WRAP_CLASSES[name] }

  targets.reverse_each do |(open_index, close_index), klass|
    lines[close_index] = "#{lines[close_index].chomp}\n:::\n"
    lines.insert(open_index, ":::{.#{klass}}\n")
  end

  targets.size
end