Class: VivlioStarter::CLI::UnifiedIndexManager
- Inherits:
-
Object
- Object
- VivlioStarter::CLI::UnifiedIndexManager
- Defined in:
- lib/vivlio_starter/cli/index/unified_index_manager.rb
Constant Summary collapse
- ASCII_SHORT_TERM_PATTERN =
R9: [用語](読みなし)記法で ASCII 可視文字のみ 2 文字以下の語は登録しない。 単位 [eV] [Hz] やフラグ解説 [g] が索引語として誤登録されるのを防ぐ ([g] は pattern /\bg\b/ となり本文中の英字 g 全部にタグが付く)。 意図的に登録したい場合は読み付き [eV|いーぶい] を使う。
/\A[\x21-\x7E]{1,2}\z/
Instance Attribute Summary collapse
-
#markdown_generator ⇒ Object
readonly
Returns the value of attribute markdown_generator.
-
#queue_manager ⇒ Object
readonly
Returns the value of attribute queue_manager.
-
#terms_manager ⇒ Object
readonly
Returns the value of attribute terms_manager.
Instance Method Summary collapse
-
#apply_markdown_review! ⇒ Object
Markdownから承認・リジェクトを適用 仕様: vs index:apply は辞書を更新するだけで、索引ページの生成は行わない (生成はビルドパイプラインの責務).
-
#auto_process!(chapters) ⇒ Object
全自動索引候補抽出 → _index_review.md 生成.
-
#build_glossary! ⇒ Object
用語集ページを生成(後方互換 - 単独呼び出し用).
-
#build_index!(chapters) ⇒ Object
索引・用語集ページを生成(内部用 - vs build から呼ばれる).
-
#full_catalog_scope?(build_targets) ⇒ Boolean
走査対象が catalog の全章を覆っているか(R4 の前提条件)。 catalog を読めない場合は従来どおり検査する(判定材料が無いのに黙るのは危険)。.
-
#glossary_enabled? ⇒ Boolean
用語集機能が有効か.
-
#initialize ⇒ UnifiedIndexManager
constructor
A new instance of UnifiedIndexManager.
-
#list_rejected_terms ⇒ Object
リジェクト済み候補の一覧表示.
-
#plan!(chapters) ⇒ Object
索引の下見(
vs index:plan)。現況と候補の分布を表示するだけで、 辞書もレビューファイルも書かない。表示は auto_process! と同一にする ——見え方が実行ごとに変わると、下見の意味がなくなる(§6.2)。. -
#report_reference_style(builder) ⇒ Object
参照を絞ったことをビルド末尾で報告する(R6・no silent caps)。.
-
#reset_rejected! ⇒ Object
リジェクト履歴をクリア.
-
#strip_markdown(text) ⇒ Object
Markdown装飾を除去してプレーンテキストを取得.
-
#unreject_term!(term_or_number) ⇒ Object
リジェクト解除.
-
#validate_glossary_definitions!(terms) ⇒ Object
用語集の説明文バリデーション require_definition: true の場合、説明文が空ならエラー max_definition_length を超過している場合は警告.
-
#warn_missing_main_references(chapters) ⇒ Object
広く散らばっているのに主要参照が未指定の索引語を促す(R7)。.
-
#warn_unmatched_glossary_terms(glossary_terms, glossary_backlinks, chapters) ⇒ Object
R4: ビルド対象章に 1 回も出現しない用語集語を警告する(掲載自体は維持)。 catalog 未登録の原稿に出現があるならその章名を添え、どこにも無ければその旨を伝える。 除外したい場合の判断(-g フラグ)は著者に委ねる——定義は書籍の語彙資産のため。.
-
#warn_unscanned_chapters(chapters) ⇒ Object
R7: ビルド対象のうち index:auto が未走査の章があれば、ビルド末尾の案内へ積む。 旧辞書(scanned_chapters キーなし)では判定しない——誤警告を避けるため.
Constructor Details
#initialize ⇒ UnifiedIndexManager
Returns a new instance of UnifiedIndexManager.
53 54 55 56 57 58 59 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 53 def initialize @terms_manager = UnifiedTermsManager.new @queue_manager = ReviewQueueManager.new @markdown_generator = ReviewMarkdownGenerator.new @config = load_index_config @glossary_config = load_glossary_config end |
Instance Attribute Details
#markdown_generator ⇒ Object (readonly)
Returns the value of attribute markdown_generator.
51 52 53 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 51 def markdown_generator @markdown_generator end |
#queue_manager ⇒ Object (readonly)
Returns the value of attribute queue_manager.
51 52 53 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 51 def queue_manager @queue_manager end |
#terms_manager ⇒ Object (readonly)
Returns the value of attribute terms_manager.
51 52 53 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 51 def terms_manager @terms_manager end |
Instance Method Details
#apply_markdown_review! ⇒ Object
Markdownから承認・リジェクトを適用 仕様: vs index:apply は辞書を更新するだけで、索引ページの生成は行わない (生成はビルドパイプラインの責務)
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 162 def apply_markdown_review! unless @markdown_generator.exists? Common.log_warn('_index_glossary_review.md が見つかりません') Common.log_info('先に vs index:auto を実行してください') return end # --- Phase: 索引処理 --- index_approved = @markdown_generator.parse_index_approved index_rejected = @markdown_generator.parse_index_rejected # --- Phase: 用語集処理 --- glossary_approved = @markdown_generator.parse_glossary_approved glossary_rejected = @markdown_generator.parse_glossary_rejected # --- Phase: 共通処理 --- both_rejected = @markdown_generator.parse_rejected unreject = @markdown_generator.parse_unreject yomi_changes = @markdown_generator.parse_yomi_changes main_references = @markdown_generator.parse_main_references changes_made = false index_count = 0 glossary_count = 0 # --- Phase: 索引承認 --- if index_approved.any? @terms_manager.merge_terms!(index_approved, flags: 'i', source: 'auto_extracted') index_count = index_approved.size changes_made = true end # --- Phase: 用語集承認 --- if glossary_approved.any? validate_glossary_definitions!(glossary_approved) @terms_manager.merge_terms!(glossary_approved, flags: 'g', source: 'review') glossary_count = glossary_approved.size changes_made = true end # [ig] → [i] に変更された場合: g フラグを除去 glossary_approved_names = glossary_approved.map { it['term'] } index_only = index_approved.reject { glossary_approved_names.include?(it['term']) } index_only.each do |term| next unless @terms_manager.glossary_term_names.include?(term['term']) @terms_manager.remove_flag!(term['term'], 'g') Common.log_info("用語集フラグを除去しました(索引のみ): #{term['term']}") changes_made = true end # --- Phase: 索引のみリジェクト([-i]) --- if index_rejected.any? index_rejected.each { @terms_manager.remove_flag!(it['term'], 'i') } @queue_manager.save_rejected_terms(index_rejected) changes_made = true end # --- Phase: 用語集のみリジェクト([-g]) --- if glossary_rejected.any? glossary_rejected.each { @terms_manager.remove_flag!(it['term'], 'g') } @queue_manager.save_rejected_terms(glossary_rejected) changes_made = true end # --- Phase: 両方リジェクト([r]) --- if both_rejected.any? both_rejected.each { @terms_manager.remove_term!(it['term']) } @queue_manager.save_rejected_terms(both_rejected) changes_made = true end # --- Phase: リジェクト解除 + 直接登録 --- if unreject.any? unreject.each do |entry| @queue_manager.unreject_term_by_name!(entry['term']) flag = entry['flag'] || 'i' term_data = { 'term' => entry['term'], 'yomi' => entry['yomi'] } flags = case flag when 'i', 'x' then 'i' when 'g' then 'g' when 'ig', 'gi' then 'ig' else 'i' end @terms_manager.merge_terms!([term_data], flags:, source: 'unreject') index_count += 1 if flags.include?('i') glossary_count += 1 if flags.include?('g') Common.log_info("リジェクト解除 → [#{flag}] 登録: #{entry['term']}") end changes_made = true end # --- Phase: 読み変更 --- if yomi_changes.any? @terms_manager.update_yomi!(yomi_changes) changes_made = true end # --- Phase: 主要参照 --- changes_made = true if apply_main_references!(main_references) # --- Phase: 孤立データ除去 --- index_approved_names = index_approved.map { it['term'] } glossary_approved_names_all = glossary_approved.map { it['term'] } unreject_index_names = unreject.select { %w[i x ig gi].include?(it['flag']) }.map { it['term'] } unreject_glossary_names = unreject.select { %w[g ig gi].include?(it['flag']) }.map { it['term'] } # 明示的にリジェクトされた用語は孤立除去の対象外 # ([-i] で i を除去した後に残る g を誤って除去しないため) explicitly_rejected = (index_rejected + glossary_rejected + both_rejected).map { it['term'] }.uniq # 索引フラグの孤立除去 stale_index = @terms_manager.index_term_names - index_approved_names - unreject_index_names - explicitly_rejected stale_index.each do |term_name| @terms_manager.remove_flag!(term_name, 'i') Common.log_info("索引フラグを除去: #{term_name}") changes_made = true end # 用語集フラグの孤立除去 stale_glossary = @terms_manager.glossary_term_names - glossary_approved_names_all - unreject_glossary_names - explicitly_rejected stale_glossary.each do |term_name| @terms_manager.remove_flag!(term_name, 'g') Common.log_info("用語集フラグを除去: #{term_name}") changes_made = true end # --- Phase: Section 4 同期処理 --- rejected_section_all = @markdown_generator.parse_rejected_section_all unreject_names = unreject.map { it['term'] } confirmed_rejected = rejected_section_all.select { ['', ' '].include?(it['flag']) } .reject { unreject_names.include?(it['term']) } if confirmed_rejected.any? rejected_count = 0 confirmed_rejected.each do |entry| term_name = entry['term'] next unless @terms_manager.term_names.include?(term_name) @terms_manager.remove_term!(term_name) Common.log_info("除外済みリストに基づき登録を解除: #{term_name}") rejected_count += 1 end @queue_manager.save_rejected_terms(confirmed_rejected) changes_made = true if rejected_count.positive? || confirmed_rejected.any? end if changes_made rejected_total = both_rejected.size + (confirmed_rejected&.size || 0) # 何をどれだけ適用したかを既定ログレベルでも 1 行で報告する Common.log_result("辞書を更新しました(索引 #{index_count} 件・用語集 #{glossary_count} 件・" \ "リジェクト #{rejected_total} 件)", status: :success) Common.log_info("読み変更: #{yomi_changes.size}件") if yomi_changes.any? Common.log_info('ページ生成は vs build 実行時に行われます') else Common.log_warn('変更がありませんでした') Common.log_info('_index_glossary_review.md でフラグを編集してください') end # _index_glossary_review.md は残す(再編集の可能性があるため) # vs build の clean 処理で削除される changes_made end |
#auto_process!(chapters) ⇒ Object
全自動索引候補抽出 → _index_review.md 生成
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 78 def auto_process!(chapters) auto_discovery = @config.fetch(:auto_discovery, true) Common.log_action('索引の自動処理を開始します...') # R8: 辞書へ書いた登録内容を種別ごとに集め、既定ログレベルで要約表示する dictionary_writes = {} # 1. 手動マークアップを検出して統合辞書に登録 manual_terms = extract_manual_markup_terms(chapters) if manual_terms.any? added = @terms_manager.merge_terms!(manual_terms, flags: 'i', source: 'manual_markup') dictionary_writes['手動マークアップ'] = added if added.any? Common.log_info("手動マークアップから #{manual_terms.size} 件の用語を登録しました") end # auto_discovery が無効の場合、自動候補抽出をスキップ unless auto_discovery @terms_manager.record_scanned_chapters!(chapters) report_dictionary_writes(dictionary_writes) Common.log_info('auto_discovery: false のため、自動候補抽出をスキップします') Common.log_info('手動マークアップのみが索引に反映されます') return end # 2. 候補抽出 candidates = extract_candidates(chapters) Common.log_info("候補抽出: #{candidates.size}件") # 3. 既に辞書にある語とリジェクト済みの語を落とす(=選べる候補だけ残す) selectable, rejected_count_in_candidates = selectable_candidates(candidates) # 4. 登録語と同じ土俵で並べ、推奨候補/一般候補/見直し候補に分ける。 # スコアの絶対値では切らない——閾値は書籍の規模で意味が変わるうえ、 # 「目安に達しているから推奨は 0 件」という誤った判断を生む(§3.4-1)。 bands = build_bands(@terms_manager.index_terms, selectable, current_estimate(chapters), @extractor) by_name = selectable.to_h { [it['term'], it] } high_candidates = review_entries(bands&.recommended, by_name) low_candidates = review_entries(bands&.general, by_name) # 5. 自動承認は既定で行わない。旧既定(スコア 300 以上を無条件登録)が # 「頻出の一般語ばかりが辞書に入る」現状を作った張本人である。 auto_approved = @config[:auto_approve] == true ? high_candidates : [] if auto_approved.any? added = @terms_manager.merge_terms!(auto_approved, flags: 'i', source: 'auto_extracted') dictionary_writes['自動承認'] = added if added.any? end # 6. 登録済み用語(索引+用語集すべて)に文脈を付与。 # 見直し候補(順位が目安語数の外に出た登録語)も渡し、レビューで一覧できるようにする terms_with_context = enrich_terms_with_context( @terms_manager.load_terms, chapters, scores: candidate_scores_by_name(selectable, candidates), review_terms: bands&.review&.map(&:term)&.to_set || Set[] ) # 7. リジェクト済み用語に文脈とスコアを付与 # candidatesからスコアを復元できるように渡す rejected_with_context = enrich_rejected_with_context(candidates) # 8. _index_review.md を生成 @markdown_generator.generate!( terms: terms_with_context, high_candidates: high_candidates, low_candidates: low_candidates, rejected: rejected_with_context ) # 9. 走査した章集合を辞書へ記録(R7: ビルド時の章追加検知に使う) @terms_manager.record_scanned_chapters!(chapters) # 10. 結果レポート report_dictionary_writes(dictionary_writes) # 現況と候補の分布は vs index:plan と同じ画面を出す(§6.3)。 # 辞書を書き換えた後なので、登録語数は更新後の値になる。 @terms_manager.clear_cache! build_plan_reporter(chapters, selectable, extractor: @extractor).render report_auto_results(auto_approved, high_candidates, low_candidates, rejected_count_in_candidates, rejected_with_context.size) end |
#build_glossary! ⇒ Object
用語集ページを生成(後方互換 - 単独呼び出し用)
414 415 416 417 418 419 420 421 422 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 414 def build_glossary! return unless glossary_enabled? @terms_manager.clear_cache! glossary = @terms_manager.glossary_terms builder = UnifiedPageBuilder.new(glossary_config: @glossary_config) result = builder.build_glossary!(glossary) Common.log_success('用語集ページを生成しました') if result end |
#build_index!(chapters) ⇒ Object
索引・用語集ページを生成(内部用 - vs build から呼ばれる)
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 374 def build_index!(chapters) Common.log_action('索引・用語集ページを生成しています...') # 本文スキャン(索引タグ付け+用語集リンク生成) scanner = IndexMatchScanner.new(defer_warnings: true) scanner.scan_all_chapters!(chapters, read_only: false) # UnifiedPageBuilder で索引+用語集を生成 builder = UnifiedPageBuilder.new(glossary_config: @glossary_config) # 索引ページ生成 builder.build_index! report_reference_style(builder) # 用語集ページ生成(glossary_enabled かつ g フラグの用語がある場合) # スキャンは辞書を書かない(R1)ためリロード不要 if glossary_enabled? glossary = @terms_manager.glossary_terms builder.build_glossary!(glossary) warn_unmatched_glossary_terms(glossary, scanner.glossary_backlinks, chapters) end Common.log_success('索引・用語集ページの生成が完了しました') # R7: 索引候補の抽出(vs index:auto)が未実施の章を検出して案内 warn_unscanned_chapters(chapters) # 主要参照が未指定で広く散らばっている語を要約 1 行で促す warn_missing_main_references(chapters) return unless scanner.config_missing || scanner.no_matches IndexCommands.(IndexCommands::INDEX_TERMS_MISSING_MESSAGE) # 画面に 🟡 を出す以上は集計にも載せる(出ているのに「良好」と総括しない)。 PreProcessCommands::IssueRegistry.record( severity: :warn, category: :index, message: '索引語辞書がありません(vs index:auto → vs index:apply で作成できます)' ) end |
#full_catalog_scope?(build_targets) ⇒ Boolean
走査対象が catalog の全章を覆っているか(R4 の前提条件)。 catalog を読めない場合は従来どおり検査する(判定材料が無いのに黙るのは危険)。
555 556 557 558 559 560 561 562 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 555 def full_catalog_scope?(build_targets) catalog = Build::CatalogLoader.load_existing_basenames return true if catalog.empty? (catalog - build_targets).empty? rescue StandardError true end |
#glossary_enabled? ⇒ Boolean
用語集機能が有効か
425 426 427 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 425 def glossary_enabled? @glossary_config[:enabled] == true end |
#list_rejected_terms ⇒ Object
リジェクト済み候補の一覧表示
565 566 567 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 565 def list_rejected_terms @queue_manager.list_rejected_terms end |
#plan!(chapters) ⇒ Object
索引の下見(vs index:plan)。現況と候補の分布を表示するだけで、
辞書もレビューファイルも書かない。表示は auto_process! と同一にする
——見え方が実行ごとに変わると、下見の意味がなくなる(§6.2)。
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 65 def plan!(chapters) Common.log_action('索引の現況を確認しています...') candidates = @config.fetch(:auto_discovery, true) ? extract_candidates(chapters) : [] # auto_process! と同じく「選べる候補」だけを渡す。生の候補には登録済みの語も # 棄却済みの語も混ざっており、そのまま出すと下見だけが多く見え、しかも # 著者が自分で外した語を推奨してしまう(実測: 2,880 件 対 2,644 件)。 selectable, = selectable_candidates(candidates) build_plan_reporter(chapters, selectable, extractor: @extractor).render(dry_run: true) 0 end |
#report_reference_style(builder) ⇒ Object
参照を絞ったことをビルド末尾で報告する(R6・no silent caps)。
索引が短くなった理由が設定にあると分からないと、著者は「索引語が 消えた」と読む。何語をどの設定で絞ったかまで書く。
456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 456 def report_reference_style(builder) limitation = builder.reference_limitation return unless limitation.any? = if limitation.style == 'main_only' "💡 索引を主要参照のみで組みました(index.reference_style: main_only・#{limitation.size} 語)" else "💡 索引の副次参照を #{limitation.size} 語で #{limitation.limit} 件までに絞りました" \ "(index.max_sub_references: #{limitation.limit})" end IndexCommands.() end |
#reset_rejected! ⇒ Object
リジェクト履歴をクリア
576 577 578 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 576 def reset_rejected! @queue_manager.reset_rejected! end |
#strip_markdown(text) ⇒ Object
Markdown装飾を除去してプレーンテキストを取得
360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 360 def strip_markdown(text) text .gsub(/\*\*(.+?)\*\*/, '\1') # **bold** .gsub(/\*(.+?)\*/, '\1') # *italic* .gsub(/`(.+?)`/, '\1') # `code` .gsub(/\[(.+?)\]\(.+?\)/, '\1') # [link](url) .gsub(/^#+\s*/, '') # # heading .gsub(/^\s*[-*]\s+/, '') # - list item .gsub(/\n+/, ' ') # newlines to space .strip end |
#unreject_term!(term_or_number) ⇒ Object
リジェクト解除
571 572 573 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 571 def unreject_term!(term_or_number) @queue_manager.unreject_term!(term_or_number) end |
#validate_glossary_definitions!(terms) ⇒ Object
用語集の説明文バリデーション require_definition: true の場合、説明文が空ならエラー max_definition_length を超過している場合は警告
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 331 def validate_glossary_definitions!(terms) max_length = @glossary_config[:max_definition_length] # 説明文の長さチェック(Markdown装飾を除去した文字数) terms.each do |term| definition = term['definition'].to_s next if definition.strip.empty? plain_text = strip_markdown(definition) next unless plain_text.length > max_length Common.log_warn( "用語「#{term['term']}」の説明文が #{max_length} 文字を超過しています " \ "(#{plain_text.length} 文字)" ) end return unless @glossary_config[:require_definition] missing = terms.select { it['definition'].to_s.strip.empty? } return if missing.empty? missing.each do |term| Common.log_error("用語「#{term['term']}」に説明文がありません") end raise "用語集の説明文が必須ですが、#{missing.size}件の用語に説明文がありません" end |
#warn_missing_main_references(chapters) ⇒ Object
広く散らばっているのに主要参照が未指定の索引語を促す(R7)。
要約 1 行だけにする。実測で該当は 30〜38 語あり、語ごとに 1 行ずつ出すと ビルドログが埋まる。しかもそこから修正には進めない——直す場は レビューファイルなので、導線はそちらへ向ける(語ごとの候補は R2 が出す)。
部分ビルドでは黙る。出現章数の比率は全章を走査したときにしか意味を持たず、
1 章だけを対象にすると全語が「広い」判定になる
(warn_unmatched_glossary_terms と同じ立場)。
reference_style: all で機能を切っている著者にも促さない。
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 480 def warn_missing_main_references(chapters) return if main_reference_disabled? build_targets = chapters.map { File.basename(it.to_s, '.md') } return unless full_catalog_scope?(build_targets) pending = @terms_manager.index_terms.reject { it['main'] } return if pending.empty? spreads = IndexCommands::TermSpread.measure(pending, chapters) wide = IndexCommands::TermSpread.common_terms(spreads, ratio: MAIN_REFERENCE_HINT_RATIO) return if wide.empty? IndexCommands.( "🟡 主要参照が未指定の索引語が #{wide.size} 語あります(索引が引きにくくなります)\n" \ '🟡 vs index:auto を実行すると、章の候補付きでレビューファイルに一覧できます' ) # 特定の章の欠陥ではないので chapter は付けない。件数は語ごとではなく # **1 件**だけ積む——30 件積むと章別サマリーが索引の話で埋まる。 PreProcessCommands::IssueRegistry.record( severity: :warn, category: :index, message: "主要参照が未指定の索引語が #{wide.size} 語あります(vs index:auto で候補を確認できます)" ) end |
#warn_unmatched_glossary_terms(glossary_terms, glossary_backlinks, chapters) ⇒ Object
R4: ビルド対象章に 1 回も出現しない用語集語を警告する(掲載自体は維持)。 catalog 未登録の原稿に出現があるならその章名を添え、どこにも無ければその旨を伝える。 除外したい場合の判断(-g フラグ)は著者に委ねる——定義は書籍の語彙資産のため。
判定材料は「今回のスキャン結果」なので、章を絞った実行では出現しないのが当然になる (1 章だけを対象にすると用語集語がほぼ全滅して誤検知の山になる)。 全章を走査したときだけ意味を持つ検査なので、部分実行では黙る。 詳細 → preflight-glossary-warning-scope-report.md
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 516 def warn_unmatched_glossary_terms(glossary_terms, glossary_backlinks, chapters) build_targets = chapters.map { File.basename(it.to_s, '.md') } return unless full_catalog_scope?(build_targets) missing = glossary_terms.reject { glossary_backlinks.key?(it['term']) } return if missing.empty? all_contents = Dir.glob(File.join(Common::CONTENTS_DIR, '*.md')).to_h do |path| [File.basename(path, '.md'), File.read(path, encoding: 'utf-8')] end missing.each do |term| name = term['term'] found = all_contents.keys.select { all_contents[it].include?(name) } outside = found - build_targets hint = if outside.any? # 走査は catalog 全章を覆っている(上のガード)ため、ここに来るのは # contents/ にはあるが catalog.yml に載っていない原稿に限られる "catalog 未登録の #{outside.join(', ')} に出現" elsif found.any? "#{found.join(', ')} に文字列出現はあるがリンク化されていません(コード内・タグ内等)" else '原稿のどこにも出現しません(語の変更・削除?)' end Common.log_warn("用語集語がビルド対象章に出現しません: #{name}(#{hint})") # 章別サマリーへブリッジする。全章走査時にしか到達しないので(上のガード) # ここに来る指摘は「辞書に残った死語」=原稿とのズレであり、集計に載せる価値がある。 # 特定の章の欠陥ではないため chapter は付けない(章別サマリーには出ず、 # 最終行の総括にだけ効く。直し方は上のメッセージが言い切っている)。 PreProcessCommands::IssueRegistry.record( severity: :warn, category: :index, message: "用語集語が原稿に出現しません: #{name}" ) end end |
#warn_unscanned_chapters(chapters) ⇒ Object
R7: ビルド対象のうち index:auto が未走査の章があれば、ビルド末尾の案内へ積む。 旧辞書(scanned_chapters キーなし)では判定しない——誤警告を避けるため
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/vivlio_starter/cli/index/unified_index_manager.rb', line 432 def warn_unscanned_chapters(chapters) scanned = @terms_manager.scanned_chapters return if scanned.nil? unscanned = chapters.map { File.basename(it.to_s, '.md') } - scanned return if unscanned.empty? IndexCommands.( "🟡 索引候補の抽出が未実施の章があります: #{unscanned.join(', ')} → vs index:auto を実行してください" ) # 章別サマリーへブリッジする。どの章が未走査かは特定できているので章ごとに積む。 unscanned.each do |chapter| PreProcessCommands::IssueRegistry.record( chapter: chapter, severity: :warn, category: :index, message: '索引候補の抽出が未実施です(vs index:auto を実行してください)' ) end end |