Module: VivlioStarter::CLI::PreProcessCommands::CrossReferenceProcessor

Defined in:
lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb

Overview

クロスリファレンス処理モジュール rubocop:disable Metrics/ModuleLength

Defined Under Namespace

Classes: CaptionedBlockTransformer, Label, LabelCollectorContext, ReferenceReplacer

Constant Summary collapse

LABEL_TYPE_NAMES =

ラベル種別の日本語名(sec は見出しラベル・at-directive-tier1-spec.md §2.4.1)

{ list: 'リスト', table: '', fig: '', sec: '' }.freeze
CAPTION_PATTERN =
/^\*\*\s*(.+?)\s+@([-\w]+)\s*\*\*\s*$/
HEADING_LABEL_PATTERN =

見出し行末の @id(見出しラベル)。紙面には出さずアンカーだけを残す。

/^(\#{1,6})\s+(.+?)\s+@([-\w]+)\s*$/
RESERVED_IDS =

自動採番用の予約ID(キャプションで @auto / @omakase / @id と書くと type-chapter-N 形式に採番される)

%w[auto omakase id].freeze
RESERVED_MACRO_IDS =

組み込み置換ルール(ReplacementRules)・ビルド生成物(QrTransformer)の マクロ名(完全一致で予約)。これは @ID 参照ではなくシステム予約のマクロなので、 未定義のラベルIDとして警告せず後段(post_process / pre_process)へ素通しする。 (@nega/@posi の後方互換別名・@comment/@commend の編集者コメントは廃止済み)

%w[vspace hspace pagebreak pageref version today title qr].freeze
IMAGE_PATTERN =
/^!\[[^\]]*\]\([^)]+\)(?:\{[^}]+\})?$/
MAIN_CHAPTER_RANGE =
PostProcessCommands::HeadingProcessor::MAIN_CHAPTER_RANGE

Class Method Summary collapse

Class Method Details

.appendix_letter_for(filename) ⇒ Object

付録ファイル(90〜98)の図表番号プレフィックスに使う付録レター("A".."I")を返す。 付録の見出し(付録 D)・節番号(D-1)と図表番号(表 D-1)を一致させるため、 付録では章番号ではなくレターを用いる。本文章・前後付では nil を返し、 各呼び出し元の既存挙動(章番号 / 表示番号)を維持する。



138
139
140
141
142
143
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 138

def appendix_letter_for(filename)
  num = extract_chapter_number(filename).to_i
  return nil unless (90..98).cover?(num)

  Common.appendix_number_to_letter(num)&.upcase
end

.build_labels_map_with_duplicates_check(all_labels) ⇒ Hash

ラベルマップ構築(重複チェック付き)

Returns:

  • (Hash)

    labels_map と duplicates_by_id を含む duplicates_by_id: { id => [Label, ...] }(先勝ちで labels_map に載る)



251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 251

def build_labels_map_with_duplicates_check(all_labels)
  map = {}
  # IDごとに全ラベルを蓄積する(先勝ちで map に登録)
  all_occurrences = Hash.new { |h, k| h[k] = [] }

  all_labels.each do |label|
    all_occurrences[label.id] << label
    map[label.id] ||= label
  end

  duplicates_by_id = all_occurrences.select { |_, labels| labels.size > 1 }
  { labels_map: map, duplicates_by_id: }
end

.code_line_numbers(content) ⇒ Object

コード(フェンス区切り行・内容行)とみなす行番号(1 始まり)の集合を Masking で判定する。 各内部クラスのフェンス追跡(自前の状態機械)を Masking(唯一の実装)へ一元化するための述語。 可変長フェンス・入れ子・~~~・```include: 除外に一貫して追従する。



76
77
78
79
80
81
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 76

def code_line_numbers(content)
  prose = Set.new
  Masking.each_prose_line(content) { |_line, lineno| prose << lineno }
  total = content.each_line.count
  (1..total).reject { prose.include?(it) }.to_set
end

.collect_labels(content, source_file, chapter_number) ⇒ Object

ラベル収集



146
147
148
149
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 146

def collect_labels(content, source_file, chapter_number)
  collector = LabelCollectorContext.new(source_file, chapter_number)
  collector.collect(content)
end

.detect_block_type(lines, idx) ⇒ Object



100
101
102
103
104
105
106
107
108
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 100

def detect_block_type(lines, idx)
  ((idx + 1)...lines.size).each do |index|
    line = lines[index].strip
    next if line.empty? || line.start_with?(':::{')

    return detect_type_from_line(line)
  end
  nil
end

.display_chapter_number_for_filename(filename) ⇒ Object



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

def display_chapter_number_for_filename(filename)
  num = extract_chapter_number(filename).to_i
  return num.to_s unless MAIN_CHAPTER_RANGE.include?(num)

  token = File.basename(filename, File.extname(filename))
  idx = main_chapter_order.index(token)
  idx ? (idx + 1).to_s : (num - 10).to_s
end

.extract_caption_label(line) ⇒ Object



83
84
85
86
87
88
89
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 83

def extract_caption_label(line)
  match = line.match(CAPTION_PATTERN)
  return nil unless match

  { title: match[1].strip, id: match[2].strip,
    auto: RESERVED_IDS.include?(match[2].strip) }
end

.extract_chapter_number(filename) ⇒ Object

章番号関連



120
121
122
123
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 120

def extract_chapter_number(filename)
  match = File.basename(filename, '.*').match(/^(\d+)/)
  match ? match[1] : '0'
end

.extract_heading_label(line) ⇒ Object

見出し行末の @id を取り出す(## インストール @install)。 見出しラベルは @pageref / @id 参照の飛び先になる(at-directive-tier1-spec.md §1.1)。



93
94
95
96
97
98
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 93

def extract_heading_label(line)
  match = line.match(HEADING_LABEL_PATTERN)
  return nil unless match

  { level: match[1].length, title: match[2].strip, id: match[3].strip }
end

.replace_references(content, labels_map, filename = nil) ⇒ Object

参照置換



244
245
246
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 244

def replace_references(content, labels_map, filename = nil)
  ReferenceReplacer.new(content, labels_map, filename).replace
end

.reserved_id?(label_id) ⇒ Boolean

予約IDの判定を一元化する。 RESERVED_IDS: auto / omakase / id RESERVED_MACRO_IDS: vspace / hspace / pagebreak / …(完全一致)

Returns:

  • (Boolean)


50
51
52
53
54
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 50

def self.reserved_id?(label_id)
  return true if RESERVED_IDS.include?(label_id)

  RESERVED_MACRO_IDS.include?(label_id)
end

.transform_captioned_blocks(content, filename, labels_map) ⇒ Object

キャプション付きブロック変換



239
240
241
# File 'lib/vivlio_starter/cli/pre_process/cross_reference_processor.rb', line 239

def transform_captioned_blocks(content, filename, labels_map)
  CaptionedBlockTransformer.new(content, filename, labels_map).transform
end