Module: VivlioStarter::CLI::Lint::SpellChecker
- Defined in:
- lib/vivlio_starter/cli/lint/spell_checker.rb
Overview
英語スペルチェックを実行し、結果を出力する
Constant Summary collapse
- MAX_SHOWN_LINES =
表示する出現行番号の最大件数(超過分は … で省略)
10
Class Method Summary collapse
-
.aggregate(errors) ⇒ Array<Hash>
エラー配列を語ごとに集約し、表示用の行情報へ整える.
-
.check(path, word_map, check_code_blocks: false) ⇒ Array<Hash>
ファイルのスペルチェックを実行する.
-
.find_suggestion(word, word_map) ⇒ String?
Levenshtein距離で最良の候補語を返す.
-
.print_errors(errors_by_file) ⇒ Boolean
複数ファイルのエラーを標準出力に表示する。 同じ語の指摘は 1 行に集約し、出現行と件数をまとめて見やすくする。.
-
.threshold_for(word) ⇒ Integer
単語長に応じた許容Levenshtein距離を返す.
Class Method Details
.aggregate(errors) ⇒ Array<Hash>
エラー配列を語ごとに集約し、表示用の行情報へ整える
55 56 57 58 59 60 61 62 63 |
# File 'lib/vivlio_starter/cli/lint/spell_checker.rb', line 55 def aggregate(errors) errors.group_by { |e| e[:word] }.map do |word, items| lines = items.map { |e| e[:line] }.uniq.sort shown = lines.first(MAX_SHOWN_LINES).join(', ') shown += ', …' if lines.size > MAX_SHOWN_LINES suggestion = items.first[:suggestion] { count: lines.size, label: suggestion ? "#{word} => #{suggestion}" : word, lines: shown } end.sort_by { |row| -row[:count] } end |
.check(path, word_map, check_code_blocks: false) ⇒ Array<Hash>
ファイルのスペルチェックを実行する
18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/vivlio_starter/cli/lint/spell_checker.rb', line 18 def check(path, word_map, check_code_blocks: false) content = File.read(path, encoding: 'UTF-8') tokens = Tokenizer.tokenize(content, check_code_blocks: check_code_blocks, path: path) tokens.filter_map do |word, line_no| next if word_map.key?(word.downcase) { line: line_no, word: word, suggestion: find_suggestion(word, word_map) } end rescue Errno::ENOENT => e Common.log_warn("[spellcheck] ファイルを読み込めませんでした: #{path} (#{e.})") [] end |
.find_suggestion(word, word_map) ⇒ String?
Levenshtein距離で最良の候補語を返す
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/vivlio_starter/cli/lint/spell_checker.rb', line 69 def find_suggestion(word, word_map) threshold = threshold_for(word) min_len = [word.length - threshold, 1].max max_len = word.length + threshold w_down = word.downcase best_word = nil best_dist = threshold + 1 word_map.each_value do |dict_word| next unless dict_word.length.between?(min_len, max_len) dist = DidYouMean::Levenshtein.distance(w_down, dict_word.downcase) if dist < best_dist best_dist = dist best_word = dict_word end end best_dist <= threshold ? best_word : nil end |
.print_errors(errors_by_file) ⇒ Boolean
複数ファイルのエラーを標準出力に表示する。 同じ語の指摘は 1 行に集約し、出現行と件数をまとめて見やすくする。
39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/vivlio_starter/cli/lint/spell_checker.rb', line 39 def print_errors(errors_by_file) return false if errors_by_file.empty? errors_by_file.each do |path, errors| Common.log_always "📄 #{path} (spellcheck)" aggregate(errors).each do |row| Common.log_always format(' %3d件 %-28s 行: %s', row[:count], row[:label], row[:lines]) end Common.log_always '' end true end |
.threshold_for(word) ⇒ Integer
単語長に応じた許容Levenshtein距離を返す
94 95 96 97 98 99 100 |
# File 'lib/vivlio_starter/cli/lint/spell_checker.rb', line 94 def threshold_for(word) case word.length when 1..4 then 1 when 5..8 then 2 else 3 end end |