Module: VivlioStarter::CLI::Masking
- Defined in:
- lib/vivlio_starter/cli/masking.rb
Overview
Markdown のコード領域解釈の唯一実装。
Constant Summary collapse
- FENCE =
行頭フェンス(``` または ~~~ を 3 連以上)のマーカー。
/\A(?:`{3,}|~{3,})/- BLOCKQUOTE_PREFIX =
引用(
>)の行頭記号。CommonMark はブロック引用の中のフェンスドコード ブロックを認めており、GitHub 等も描画する。剥がさないと> ```htmlが 地の文と見なされ、索引付与などがコード内へ食い込む(実測: 引用で示した AI との対話例のコードに<span class="index-term">が注入された)。 入れ子引用(> >)と、>前の 3 文字までの字下げを許す。 /\A[ ]{0,3}(?:>[ ]?)+/- CODE_SPAN_PLACEHOLDER_PREFIX =
protect_code が用いるプレースホルダの接頭辞(既存 MarkdownUtils と互換)。
'__VS_CODE_SPAN__'- INLINE_CODE_SPAN =
インラインコードスパン(N 個の連続バッククォート同士の対)。 (?<!
) / (?!) で開き・閉じの両端が「ちょうど N 個のラン」であることを担保し、foo`barのように内部にバッククォートを含むケースも 1 スパンとして保護する。 /(?<!`)(`+)(?!`).+?(?<!`)\1(?!`)/m- HTML_INLINE_CODE =
HTML 化されたインラインコード(
…)。 前処理が表やコンテナを生 HTML へ変換したあとはバッククォートが残らないため、 INLINE_CODE_SPAN では保護できない——索引スキャンは変換の後に走るので、 早見表のセルに書いた[用語|読み]が裸のマークアップに見えてしまう (index-html-code-protection-spec.md §1)。属性つきも受ける。非貪欲であること。 貪欲だと 1 行に複数ある
の最初の開始から 最後の終了までを 1 つと見なし、間の地の文まで保護して索引が静かに減る。 %r{<code\b[^>]*>.*?</code>}m- EMPHASIS_PAIR =
強調・打ち消し線の記号(記号のすぐ内側に非空白がある対だけ)。 長い記号を先に置く(順序が逆だと
***強***が*強*になって内側が残る)。アンダースコアだけ前後に ASCII の 英数字を許さない。
snake_case_nameを 壊さないためで、和文は制限しない——判断の根拠は VFM の実際の出力である。 厳密な CommonMark なら__太字__の語は強調にならないが、VFM は<strong>太字</strong>の語を出す。仕様書ではなくレンダラに合わせる。記号の内側に空白がある形(
2 * 3 * 4)は外さない。VFM はここも強調と解釈するが、 markdown-it の flanking 規則の再実装になるうえ、外さなくても失うのは 「まれな取りこぼし」だけで、誤って外すより安全側に倒れる。 / (?:(\*\*\*|\*\*|\*|~~)(?=\S)(.+?)(?<=\S)\1 |(?<![A-Za-z0-9_])(___|__|_)(?=\S)(.+?)(?<=\S)\3(?![A-Za-z0-9_])) /xm
Class Method Summary collapse
-
.each_prose_line(text) ⇒ Object
コード(フェンス区切り行・フェンス内容行)を除いた「地の文」の行だけを 行番号(1 始まり)つきで yield する。ブロック未指定なら Enumerator を返す。 行番号は入力テキスト全体に対する通し番号で、コード行を飛ばしても維持される。.
-
.protect_code(text) ⇒ Array(String, Hash)
コードフェンスブロックとインラインコードスパンを一時プレースホルダへ退避し、 後続のテキスト変形処理から除外できるようにする。 フェンス判定は状態機械へ統一(可変長・入れ子に追従)。.
-
.replace_top_level_fences(text) {|block, lineno| ... } ⇒ String
トップレベルのフェンスドコードブロック(開始区切り〜終了区切り)を 1 つずつ yield し、戻り値で置き換える。nil を返したブロックは原文のまま残す。 yield には開始フェンス行の行番号(1 始まり)も渡す——著者向け警告に 「ファイル:行」を添えるため(warning-messages の流儀)。.
-
.restore_code(text, spans) ⇒ Object
protect_code で退避したコードを元に戻す。 後から退避したインラインの原文が、先に退避したフェンスのプレースホルダを 内包しうる(行を跨ぐバッククォート対がフェンス置換後のプレースホルダを 巻き込むケース)。この入れ子を正しく巻き戻すため、挿入の逆順(LIFO / reverse_each)で外側→内側の順に開く。FIFO だと未復元のプレースホルダが残留する。.
-
.strip_code(text) ⇒ Object
フェンス・インラインコードを取り除いたテキストを返す。 フェンス(区切り行・内容行)は空行に置換して行数を保つため、周辺文脈の 行対応は崩れない。インラインコードは空白 1 文字へ潰す。.
-
.strip_emphasis(line) ⇒ Array(String, Array<Integer>)
強調記法を外した文字列と、元の行への添字表を返す。.
-
.strip_inline_code(text) ⇒ Object
インラインコード
...を空白に置き換える。.
Class Method Details
.each_prose_line(text) ⇒ Object
コード(フェンス区切り行・フェンス内容行)を除いた「地の文」の行だけを 行番号(1 始まり)つきで yield する。ブロック未指定なら Enumerator を返す。 行番号は入力テキスト全体に対する通し番号で、コード行を飛ばしても維持される。
60 61 62 63 64 65 66 |
# File 'lib/vivlio_starter/cli/masking.rb', line 60 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)
コードフェンスブロックとインラインコードスパンを一時プレースホルダへ退避し、 後続のテキスト変形処理から除外できるようにする。 フェンス判定は状態機械へ統一(可変長・入れ子に追従)。
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/vivlio_starter/cli/masking.rb', line 109 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「唯一の実装」)。
101 |
# File 'lib/vivlio_starter/cli/masking.rb', line 101 def replace_top_level_fences(text, &) = replace_fenced_blocks(text, &) |
.restore_code(text, spans) ⇒ Object
protect_code で退避したコードを元に戻す。 後から退避したインラインの原文が、先に退避したフェンスのプレースホルダを 内包しうる(行を跨ぐバッククォート対がフェンス置換後のプレースホルダを 巻き込むケース)。この入れ子を正しく巻き戻すため、挿入の逆順(LIFO / reverse_each)で外側→内側の順に開く。FIFO だと未復元のプレースホルダが残留する。
135 136 137 138 139 140 141 |
# File 'lib/vivlio_starter/cli/masking.rb', line 135 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 文字へ潰す。
73 74 75 76 77 78 79 |
# File 'lib/vivlio_starter/cli/masking.rb', line 73 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_emphasis(line) ⇒ Array(String, Array<Integer>)
強調記法を外した文字列と、元の行への添字表を返す。
なぜ要るのか。 校正の辞書は「読者が見る文字列」に当てなければならない。
生の行に当てると、語の途中に入った強調で両方向に壊れる——
結**合し**直した は 合 の直前が * なのでガード (?<![一-龥])合し を
すり抜けて誤検出になり、だ**円** は語が割れて当たらなくなる。
仕様: inline-emphasis-word-split-spec.md
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/vivlio_starter/cli/masking.rb', line 172 def strip_emphasis(line) plain = +'' map = [] pos = 0 while (matched = EMPHASIS_PAIR.match(line, pos)) head = matched.begin(0) plain << line[pos...head] map.concat((pos...head).to_a) inner_group = matched[2] ? 2 : 4 inner_start = matched.begin(inner_group) inner, inner_map = strip_emphasis(matched[inner_group]) plain << inner map.concat(inner_map.map { it + inner_start }) pos = matched.end(0) end plain << line[pos..].to_s map.concat((pos...line.size).to_a) [plain, map] end |
.strip_inline_code(text) ⇒ Object
インラインコード ... を空白に置き換える。
82 |
# File 'lib/vivlio_starter/cli/masking.rb', line 82 def strip_inline_code(text) = text.gsub(/`[^`\n]+`/, ' ') |