Module: VivlioStarter::CLI::Masking

Defined in:
lib/vivlio_starter/cli/masking.rb

Overview

Markdown のコード領域解釈の唯一実装。

Constant Summary collapse

FENCE =

行頭フェンス(``` または ~~~ を 3 連以上)のマーカー。

/\A(?:`{3,}|~{3,})/
CODE_SPAN_PLACEHOLDER_PREFIX =

protect_code が用いるプレースホルダの接頭辞(既存 MarkdownUtils と互換)。

'__VS_CODE_SPAN__'
INLINE_CODE_SPAN =

インラインコードスパン(N 個の連続バッククォート同士の対)。 (?<!) / (?!) で開き・閉じの両端が「ちょうど N 個のラン」であることを担保し、 foo`bar のように内部にバッククォートを含むケースも 1 スパンとして保護する。

/(?<!`)(`+)(?!`).+?(?<!`)\1(?!`)/m

Class Method Summary collapse

Class Method Details

.each_prose_line(text) ⇒ Object

コード(フェンス区切り行・フェンス内容行)を除いた「地の文」の行だけを 行番号(1 始まり)つきで yield する。ブロック未指定なら Enumerator を返す。 行番号は入力テキスト全体に対する通し番号で、コード行を飛ばしても維持される。



43
44
45
46
47
48
49
# File 'lib/vivlio_starter/cli/masking.rb', line 43

def each_prose_line(text)
  return enum_for(:each_prose_line, text) unless block_given?

  scan_lines(text) do |line, lineno, in_code|
    yield line, lineno unless in_code
  end
end

.protect_code(text) ⇒ Array(String, Hash)

コードフェンスブロックとインラインコードスパンを一時プレースホルダへ退避し、 後続のテキスト変形処理から除外できるようにする。 フェンス判定は状態機械へ統一(可変長・入れ子に追従)。

Returns:

  • (Array(String, Hash))

    退避後テキストと { placeholder => 原文 } の対応表



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/vivlio_starter/cli/masking.rb', line 92

def protect_code(text)
  spans = {}
  counter = 0

  # 第 2 引数はフェンス経由でのみ渡る行番号(gsub 経由の呼び出しでは省略される)
  alloc = lambda do |chunk, _lineno = nil|
    key = "#{CODE_SPAN_PLACEHOLDER_PREFIX}#{counter}__"
    spans[key] = chunk
    counter += 1
    key
  end

  # まずフェンスドコードブロック全体を退避(インラインコードより先に処理)。
  protected_text = replace_fenced_blocks(text, &alloc)

  # 次にインラインコードスパンを退避。
  protected_text = protected_text.gsub(INLINE_CODE_SPAN, &alloc)

  [protected_text, spans]
end

.replace_top_level_fences(text) {|block, lineno| ... } ⇒ String

トップレベルのフェンスドコードブロック(開始区切り〜終了区切り)を 1 つずつ yield し、戻り値で置き換える。nil を返したブロックは原文のまま残す。 yield には開始フェンス行の行番号(1 始まり)も渡す——著者向け警告に 「ファイル:行」を添えるため(warning-messages の流儀)。

コード保護(protect_code)より前段で特定言語のフェンスを横取りする変換 (例: ```mermaid の図化)のための公開 API。フェンス解釈(可変長・入れ子・

独自の再実装を作らせないための入り口でもある(P1「唯一の実装」)。

Parameters:

  • text (String)

    処理対象テキスト

Yield Parameters:

  • block (String)

    フェンスブロック全体(開始行〜終了行。末尾改行は含まない)

  • lineno (Integer)

    開始フェンス行の行番号(1 始まり)

Yield Returns:

  • (String, nil)

    置換文字列(nil なら置換しない)

Returns:

  • (String)

    置換後のテキスト



84
# File 'lib/vivlio_starter/cli/masking.rb', line 84

def replace_top_level_fences(text, &) = replace_fenced_blocks(text, &)

.restore_code(text, spans) ⇒ Object

protect_code で退避したコードを元に戻す。 後から退避したインラインの原文が、先に退避したフェンスのプレースホルダを 内包しうる(行を跨ぐバッククォート対がフェンス置換後のプレースホルダを 巻き込むケース)。この入れ子を正しく巻き戻すため、挿入の逆順(LIFO / reverse_each)で外側→内側の順に開く。FIFO だと未復元のプレースホルダが残留する。



118
119
120
121
122
123
124
# File 'lib/vivlio_starter/cli/masking.rb', line 118

def restore_code(text, spans)
  restored = text.to_s
  spans.reverse_each do |placeholder, original|
    restored = restored.gsub(placeholder) { original }
  end
  restored
end

.strip_code(text) ⇒ Object

フェンス・インラインコードを取り除いたテキストを返す。 フェンス(区切り行・内容行)は空行に置換して行数を保つため、周辺文脈の 行対応は崩れない。インラインコードは空白 1 文字へ潰す。



56
57
58
59
60
61
62
# File 'lib/vivlio_starter/cli/masking.rb', line 56

def strip_code(text)
  stripped = +''
  scan_lines(text) do |line, _lineno, in_code|
    stripped << (in_code ? "\n" : line)
  end
  strip_inline_code(stripped)
end

.strip_inline_code(text) ⇒ Object

インラインコード ... を空白に置き換える。



65
# File 'lib/vivlio_starter/cli/masking.rb', line 65

def strip_inline_code(text) = text.gsub(/`[^`\n]+`/, ' ')