Class: VivlioStarter::CLI::IndexCommands::IndexMatchScanner::LineMask
- Inherits:
-
Object
- Object
- VivlioStarter::CLI::IndexCommands::IndexMatchScanner::LineMask
- Defined in:
- lib/vivlio_starter/cli/index/index_match_scanner.rb
Overview
用語を当ててはいけない領域(既にタグ付けされた要素・HTML タグ・振り仮名・ インラインコード)を退避してから用語を順に当て、最後にまとめて戻す。
退避を用語ごとにやり直さないのが要点。従来は 1 行あたり用語数(153 語)だけ 4 本の保護 gsub と Regexp 生成を繰り返しており、これがスキャン時間の大半を 占めていた(地の文 7,421 行 × 153 語 = 113 万反復)。
生成したタグは即座に退避するため、後続の用語が先行タグの中身や属性 (class="index-term" 等)へ食い込むことはない——従来の「用語ごとに保護し直す」 方式と遮蔽の効果は同じで、結果は 1 バイトも変わらない。
Constant Summary collapse
- SENTINEL =
退避トークンは NUL 区切り。原稿に現れず、用語パターン(/\bTERM\b/)が 退避内容へ食い込む余地もない(従来の
[[HTML_TOKEN_0]]形式は文字列 "HTML" を含み、直後の_が単語文字であるおかげで偶然無事だった)。 "\u0000VSIDX"
Instance Method Summary collapse
-
#initialize(text) ⇒ LineMask
constructor
A new instance of LineMask.
-
#protect!(pattern) ⇒ Object
パターンに一致する箇所を退避する.
-
#restore ⇒ Object
退避を戻して完成した行を返す。 入れ子(インラインコード
vs build <章名>は HTML タグを内包する)を正しく 巻き戻すため挿入の逆順(LIFO)で展開する。順方向だと内側のトークンが 未展開のまま表面化して残留する。 置換文字列中の\\や\1を特殊扱いさせないため、必ずブロック形式で戻す。. -
#stash(content) ⇒ Object
文字列を退避してトークンを返す(生成したタグを後続の用語から隠すため).
-
#substitute!(pattern) ⇒ Object
退避済みテキストに対して用語を置換する(ブロックはマッチ文字列を受け取る).
-
#substitute_match!(pattern) ⇒ Object
捕捉グループが要る置換。ブロックへ MatchData を渡す。.
- #token?(text) ⇒ Boolean
Constructor Details
#initialize(text) ⇒ LineMask
Returns a new instance of LineMask.
434 435 436 437 |
# File 'lib/vivlio_starter/cli/index/index_match_scanner.rb', line 434 def initialize(text) @text = text @stash = [] end |
Instance Method Details
#protect!(pattern) ⇒ Object
パターンに一致する箇所を退避する
440 441 442 |
# File 'lib/vivlio_starter/cli/index/index_match_scanner.rb', line 440 def protect!(pattern) @text = @text.gsub(pattern) { |match| stash(match) } end |
#restore ⇒ Object
退避を戻して完成した行を返す。
入れ子(インラインコード vs build <章名> は HTML タグを内包する)を正しく
巻き戻すため挿入の逆順(LIFO)で展開する。順方向だと内側のトークンが
未展開のまま表面化して残留する。
置換文字列中の \\ や \1 を特殊扱いさせないため、必ずブロック形式で戻す。
469 470 471 472 |
# File 'lib/vivlio_starter/cli/index/index_match_scanner.rb', line 469 def restore @stash.reverse_each { |token, content| @text = @text.gsub(token) { content } } @text end |
#stash(content) ⇒ Object
文字列を退避してトークンを返す(生成したタグを後続の用語から隠すため)
456 457 458 459 460 |
# File 'lib/vivlio_starter/cli/index/index_match_scanner.rb', line 456 def stash(content) token = "#{SENTINEL}#{@stash.size}\u0000" @stash << [token, content] token end |
#substitute!(pattern) ⇒ Object
退避済みテキストに対して用語を置換する(ブロックはマッチ文字列を受け取る)
445 |
# File 'lib/vivlio_starter/cli/index/index_match_scanner.rb', line 445 def substitute!(pattern, &) = @text = @text.gsub(pattern, &) |
#substitute_match!(pattern) ⇒ Object
捕捉グループが要る置換。ブロックへ MatchData を渡す。
substitute! では足りない——Regexp.last_match($~)はフレームローカルで、 gsub がここで立てる値は「呼び出し側で定義されたブロック」からは見えず常に nil になる。索引マークアップ [用語|読み] は読みの分離に捕捉グループが要る ので、MatchData を明示的に手渡してその制約を越える。
453 |
# File 'lib/vivlio_starter/cli/index/index_match_scanner.rb', line 453 def substitute_match!(pattern) = @text = @text.gsub(pattern) { yield ::Regexp.last_match } |
#token?(text) ⇒ Boolean
462 |
# File 'lib/vivlio_starter/cli/index/index_match_scanner.rb', line 462 def token?(text) = text.include?(SENTINEL) |