Module: VivlioStarter::CLI::Guards::ContainerScanner

Defined in:
lib/vivlio_starter/cli/guards/container_scanner.rb

Overview

contents/*.md の ::: コンテナ記法(開始行・終了行)を、行番号つきで拾い出す。 ContainerFenceCheck(開閉バランス)と ContainerClassCheck(未知クラス名)が共有する。

なぜ Masking.each_prose_line なのか:

フェンス内の `:::`(記法解説のコード例)を数えてはならない一方、報告する行番号は
原稿そのものの行番号でなければならない。each_prose_line は「コード行を飛ばしつつ
通し行番号を維持する」唯一の実装であり、この二要件を同時に満たす。
Masking.protect_code は使えない。フェンスをプレースホルダ 1 個に畳んで行番号を崩し、
さらに INLINE_CODE_SPAN が /m 付きのため孤立したバッククォート対が複数行を飲み込む。
(実測: protect_code 経由だと contents/41-book-yml.md が「閉じ忘れ」と誤検出された)

Defined Under Namespace

Classes: Directive

Constant Summary collapse

OPEN =

開始行。:::{.a .b} / ::: {.a scale=60%} の両方を受ける。 経路 B(組み込み置換ルール ReplacementRules)は行頭の空白を問わないため lstrip 後に判定する。

/\A:{3,}\s*\{(?<body>[^}]*)\}/
CLOSE =

終了行。::: / :::: のみの行。

/\A:{3,}\s*\z/

Class Method Summary collapse

Class Method Details

.comment_state(in_comment, line) ⇒ Array(Boolean, Boolean)

HTML コメント(<!-- … -->)内かどうかを 1 行ずつ遷移させる。 会話文記法の TODO(contents/22-extentions.md)が :::{.talk} をコメント内に抱えており、 これを実在の記法として数えると偽陽性になる。

Returns:

  • (Array(Boolean, Boolean))

    次行へ持ち越す状態と、この行を読み飛ばすか



52
53
54
55
56
57
# File 'lib/vivlio_starter/cli/guards/container_scanner.rb', line 52

def comment_state(in_comment, line)
  return [!line.include?('-->'), true] if in_comment
  return [!line.include?('-->'), true] if line.include?('<!--')

  [false, false]
end

.parse(stripped, lineno) ⇒ Object

行頭が ::: なら Directive を返す(そうでなければ nil)。



60
61
62
63
64
65
66
67
# File 'lib/vivlio_starter/cli/guards/container_scanner.rb', line 60

def parse(stripped, lineno)
  if (matched = stripped.match(OPEN))
    classes, attributes = split_tokens(matched[:body])
    Directive.new(line_number: lineno, kind: :open, classes:, attributes:)
  elsif stripped.match?(CLOSE)
    Directive.new(line_number: lineno, kind: :close, classes: [], attributes: [])
  end
end

.scan(path) ⇒ Array<Directive>

Returns 出現順の走査結果.

Parameters:

  • path (String)

    contents 配下の Markdown ファイルパス

Returns:

  • (Array<Directive>)

    出現順の走査結果



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/vivlio_starter/cli/guards/container_scanner.rb', line 33

def scan(path)
  directives = []
  in_comment = false

  Masking.each_prose_line(File.read(path, encoding: 'utf-8')) do |line, lineno|
    in_comment, skip = comment_state(in_comment, line)
    next if skip

    directive = parse(line.lstrip, lineno)
    directives << directive if directive
  end

  directives
end

.split_tokens(body) ⇒ Object

{…} の中身をクラス名と属性トークンに分ける。 先頭の . は任意(経路 B の正規表現が \.? で両方通すため)。 scale=60% / shift-y=20% は経路 A が解する属性であり、クラス名ではない。



72
73
74
75
# File 'lib/vivlio_starter/cli/guards/container_scanner.rb', line 72

def split_tokens(body)
  attributes, class_tokens = body.split.partition { it.include?('=') }
  [class_tokens.map { it.delete_prefix('.') }, attributes]
end