Module: Vivlio::Starter::CLI::Lint::Tokenizer

Defined in:
lib/vivlio/starter/cli/lint/tokenizer.rb

Overview

Markdownファイルから英単語トークンを抽出する

Constant Summary collapse

FENCE_PATTERN =
/^```/
FRONTMATTER_SEP =
/^---\s*$/
VS_LINT_DISABLE =

vs-lint コメント記法の定義

/^\s*<!--\s*vs-lint-disable\s*-->\s*$/
VS_LINT_ENABLE =
/^\s*<!--\s*vs-lint-enable\s*-->\s*$/
VS_LINT_DISABLE_NEXT_LINE =
/^\s*<!--\s*vs-lint-disable-next-line\s*-->\s*$/

Class Method Summary collapse

Class Method Details

.build_excluded_lines(content) ⇒ Array(Set<Integer>, Integer?)

vs-lint コメントに基づいて除外すべき行番号のセットを構築する

Parameters:

  • content (String)

    Markdownファイル全体の内容

Returns:

  • (Array(Set<Integer>, Integer?))

    除外行番号セットと、未クローズ disable ブロックの開始行番号(クローズ済みなら nil)



69
70
71
72
73
74
75
76
77
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
# File 'lib/vivlio/starter/cli/lint/tokenizer.rb', line 69

def build_excluded_lines(content)
  excluded_lines = Set.new
  disable_opened_at = nil
  line_no = 0

  content.each_line do |line|
    line_no += 1

    # vs-lint-disable コメント行自体を除外
    if line.match?(VS_LINT_DISABLE)
      disable_opened_at ||= line_no
      excluded_lines.add(line_no)
      next
    end

    # vs-lint-enable コメント行自体を除外
    if line.match?(VS_LINT_ENABLE)
      disable_opened_at = nil
      excluded_lines.add(line_no)
      next
    end

    # vs-lint-disable-next-line コメント行自体を除外し、次の行も除外
    if line.match?(VS_LINT_DISABLE_NEXT_LINE)
      excluded_lines.add(line_no)
      excluded_lines.add(line_no + 1)
      next
    end

    # disable ブロック内の行を除外
    excluded_lines.add(line_no) if disable_opened_at
  end

  [excluded_lines, disable_opened_at]
end

.extract_words(line) ⇒ Array<String>

Returns 抽出された英単語の配列.

Parameters:

  • line (String)

    1行のMarkdownテキスト

Returns:

  • (Array<String>)

    抽出された英単語の配列



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/vivlio/starter/cli/lint/tokenizer.rb', line 117

def extract_words(line)
  cleaned = line.dup
  cleaned.gsub!(/`[^`]*`/, ' ')                          # インラインコードを除去
  cleaned.gsub!(/<[^>]+>/, ' ')                          # HTMLタグを除去
  cleaned.gsub!(/\{[^}]*\}/, ' ')                        # Vivliostyle拡張記法 {.aki} 等
  cleaned.gsub!(/!?\[([^\]]*)\]\([^)]*\)/, '\1') # Markdownリンク・画像
  cleaned.gsub!(/!?\[([^\]]*)\]\[[^\]]*\]/, '\1')        # 参照リンク
  cleaned.gsub!(%r{https?://\S+}, ' ')                   # URLを除去
  cleaned.gsub!(/^#+\s*/, '')                            # 見出し記号を除去

  cleaned.scan(/[a-zA-Z]+(?:-[a-zA-Z]+)*/).select { it.length >= 2 }
end

.tokenize(content, check_code_blocks: false, path: nil) ⇒ Array<[String, Integer]>

Returns [word, line_no] のペア配列.

Parameters:

  • content (String)

    Markdownファイル全体の内容

  • check_code_blocks (Boolean) (defaults to: false)

    コードブロック内もチェックするか

  • path (String, nil) (defaults to: nil)

    警告メッセージに含めるファイルパス(省略可)

Returns:

  • (Array<[String, Integer]>)
    word, line_no

    のペア配列



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/vivlio/starter/cli/lint/tokenizer.rb', line 23

def tokenize(content, check_code_blocks: false, path: nil)
  tokens           = []
  in_code_fence    = false
  in_frontmatter   = false
  line_no          = 0

  # vs-lint コメントによる除外行番号セットを構築
  excluded_lines, unclosed_disable_at = build_excluded_lines(content)
  warn_unclosed_disable(path, unclosed_disable_at) if unclosed_disable_at

  content.each_line do |line|
    line_no += 1

    # YAMLフロントマター(先頭 --- 〜 --- )をスキップ
    if line_no == 1 && line.match?(FRONTMATTER_SEP)
      in_frontmatter = true
      next
    end

    if in_frontmatter
      in_frontmatter = false if line.match?(FRONTMATTER_SEP)
      next
    end

    # コードフェンスのトグル
    if line.match?(FENCE_PATTERN)
      in_code_fence = !in_code_fence
      next
    end

    # コードブロック内のスキップ
    next if in_code_fence && !check_code_blocks

    # vs-lint コメントによる除外
    next if excluded_lines.include?(line_no)

    extract_words(line).each { |word| tokens << [word, line_no] }
  end

  tokens
end

.warn_unclosed_disable(path, opened_at) ⇒ Object

vs-lint-disable が閉じられないままファイル末尾に達した場合に警告を出す。著者が誤って enable を書き忘れたケースを検知するためのガード。

Parameters:

  • path (String, nil)

    ファイルパス(警告メッセージ用)

  • opened_at (Integer)

    disable が開始された行番号



109
110
111
112
113
# File 'lib/vivlio/starter/cli/lint/tokenizer.rb', line 109

def warn_unclosed_disable(path, opened_at)
  location = path ? "#{path}:#{opened_at}" : "line #{opened_at}"
  warn "[vs-lint] 警告: #{location} の <!-- vs-lint-disable --> が " \
       '<!-- vs-lint-enable --> で閉じられていません。ファイル末尾まで lint が無効化されます。'
end