Module: VivlioStarter::CLI::Import::PagebreakRestorer

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

Overview

//clearpage の復元

Constant Summary collapse

CLEARPAGE =
%r{\A//clearpage\s*\z}
REVIEW_COMMENT =
/\A\#@\#/
INLINE_MARKUP =

@<href>{url, テキスト} のような Re:VIEW のインライン記法

/@<[a-z]+>\{(.*?)\}/m
HEADING_MARK =

行頭の見出し記号・カラム記号(=== ====[column]

/\A=+(?:\[[^\]]*\])?\s*/
DECORATION =

照合のときに両側から落とす飾り

/[*_`#>\s]|\A-\s/
FENCE_OPEN =

囲みの開き(:::{.column} など)

/\A:::\{/

Class Method Summary collapse

Class Method Details

.clearpage_anchors(re_lines) ⇒ Object

//clearpage の直後にくる「素のテキスト」を出現順に返す



79
80
81
82
83
84
85
86
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 79

def clearpage_anchors(re_lines)
  re_lines.each_index.filter_map do |i|
    next unless CLEARPAGE.match?(re_lines[i].to_s.chomp)

    ((i + 1)...re_lines.size).filter_map { plain_text(re_lines[it]) }
                            .find { !it.empty? }
  end.compact
end

.comparable(text) ⇒ Object

飾りを落として突き合わせる(**題名**題名 を同じものと見る)



135
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 135

def comparable(text) = text.to_s.gsub(DECORATION, '')

.hoist_above_container(lines, index) ⇒ Object

囲みの中に落ちたときは、いちばん外側の囲みの開きまで持ち上げる。 空行は消費しない——消費すると、入れ直した空行と重なって余分な空きになる



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 104

def hoist_above_container(lines, index)
  point = index

  loop do
    previous = previous_content_index(lines, point)
    break unless previous && FENCE_OPEN.match?(lines[previous])

    point = previous
  end

  point
end

.insertion_points(lines, anchors, md_path) ⇒ Object

Markdown 側の挿入位置を求める



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 89

def insertion_points(lines, anchors, md_path)
  cursor = 0

  anchors.filter_map do |anchor|
    index = (cursor...lines.size).find { comparable(lines[it]).include?(comparable(anchor)) }
    next warn_unplaced(md_path, anchor) unless index

    point = hoist_above_container(lines, index)
    cursor = index + 1
    point
  end
end

.plain_text(line) ⇒ Object

Re:VIEW の 1 行から素のテキストを取り出す。命令・コメントは対象外



125
126
127
128
129
130
131
132
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 125

def plain_text(line)
  text = line.to_s.strip
  return '' if text.empty? || text.start_with?('//') || REVIEW_COMMENT.match?(text)

  text.sub(HEADING_MARK, '')
      .gsub(INLINE_MARKUP) { Regexp.last_match(1).to_s.split(',').last.to_s.strip }
      .strip
end

.previous_content_index(lines, from) ⇒ Object

直前の「空行でない行」の位置。無ければ nil



118
119
120
121
122
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 118

def previous_content_index(lines, from)
  index = from - 1
  index -= 1 while index >= 0 && lines[index].strip.empty?
  index.negative? ? nil : index
end

.restore!(temp_dir, starter_dir) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 46

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("  改ページ(@pagebreak)を #{restored} 件復元しました") if restored.positive?
end

.restore_chapter!(md_path, re_path) ⇒ Object

1 章ぶんを復元し、入れられた件数を返す



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 59

def restore_chapter!(md_path, re_path)
  anchors = clearpage_anchors(File.readlines(re_path, encoding: 'utf-8'))
  return 0 if anchors.empty?

  lines = File.readlines(md_path, encoding: 'utf-8')
  points = insertion_points(lines, anchors, md_path)
  return 0 if points.empty?

  # 行番号がずれないよう後ろから入れる。直前が囲みの閉じなどで詰まっている
  # ときは空行を足す——@pagebreak は独立した 1 段落として置く記法のため
  points.sort.reverse_each do |point|
    lead = point.positive? && !lines[point - 1].strip.empty? ? "\n" : ''
    lines.insert(point, "#{lead}@pagebreak\n\n")
  end

  File.write(md_path, lines.join, encoding: 'utf-8')
  points.size
end

.warn_unplaced(md_path, anchor) ⇒ Object



137
138
139
140
141
142
143
# File 'lib/vivlio_starter/cli/import/pagebreak_restorer.rb', line 137

def warn_unplaced(md_path, anchor)
  Common.log_warn(
    "  #{File.basename(md_path)}: //clearpage の位置を決められませんでした(目印: #{anchor[0, 20]})。",
    detail: '対処: 改ページしたい位置に @pagebreak と 1 行書いてください(22 章「改ページ」)。'
  )
  nil
end