Class: VivlioStarter::CLI::TextlintFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/vivlio_starter/cli/textlint_formatter.rb

Overview

textlint --format json 出力の集約フォーマッター

Constant Summary collapse

MAX_SHOWN_LINES =

集約表示で出現行を並べる最大件数(超過分は … で省略)

10
RULE_SUMMARIES =

メッセージが出現ごとに変動する(行番号・文字数を含む等)ルールの、集約用の要約ラベル。 これらは個別メッセージを使わず 1 つにまとめる(件数と出現行だけ見られれば十分)。

{
  'sentence-length' => '一文が長すぎます(最大文長を超過)'
}.freeze

Class Method Summary collapse

Class Method Details

.aggregate_json(json_string, base_dir: Dir.pwd, disabled_rules: [], trim_long_vowel: false) ⇒ Hash?

textlint --format json の出力を、メッセージ(先頭行)+ルール単位で集約する。 スペルチェック側(SpellChecker.aggregate)と同様、同じ指摘を 1 行へ畳んで見やすくする。

Parameters:

  • json_string (String)

    textlint --format json の生出力

  • base_dir (String) (defaults to: Dir.pwd)

    ファイルパスの相対化基準

  • disabled_rules (Array<String>) (defaults to: [])

    無効化するルール ID(短縮名・完全名の両対応)

  • trim_long_vowel (Boolean) (defaults to: false)

    true なら「X => Xー」(末尾長音を足す)系の指摘を抑止

Returns:

  • (Hash, nil)

    { files: [{ path:, rows: }], total:, fixable: } / JSON 解釈失敗時 nil rows: [{ count:, label:, lines: }](出現数の多い順。label は "[ルール] 指摘先頭行")



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 37

def self.aggregate_json(json_string, base_dir: Dir.pwd, disabled_rules: [], trim_long_vowel: false)
  data = JSON.parse(json_string.to_s)
  return nil unless data.is_a?(Array)

  drules = Array(disabled_rules).map(&:to_s)
  total = 0
  fixable = 0
  files = data.filter_map do |file|
    messages = Array(file['messages']).reject { |m| disabled_message?(m, drules, trim_long_vowel) }
    next if messages.empty?

    total += messages.size
    fixable += messages.count { |m| m['fix'] }
    { path: relative_md_path(file['filePath'], base_dir), rows: aggregate_messages(messages) }
  end
  { files: files, total: total, fixable: fixable }
rescue JSON::ParserError
  nil
end

.aggregate_messages(messages) ⇒ Object

メッセージ配列を [集約見出し, ルール] 単位で集約する。 通常はメッセージ先頭行ごと(prh の置換などは別グループ)だが、出現ごとに数値が変わる ルール(sentence-length 等)は要約ラベル+数字マスクで 1 つに畳む。



83
84
85
86
87
88
89
90
91
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 83

def self.aggregate_messages(messages)
  messages.group_by { |m| [grouping_head(m['message'], m['ruleId']), short_rule(m['ruleId'])] }
          .map do |(head, rule), items|
    lines = items.filter_map { it['line'] }.uniq.sort
    shown = lines.first(MAX_SHOWN_LINES).join(', ')
    shown += ', …' if lines.size > MAX_SHOWN_LINES
    { count: items.size, label: "[#{rule}] #{head}", lines: shown }
  end.sort_by { |row| -row[:count] }
end

.disabled_message?(message, disabled_rules, trim_long_vowel = false) ⇒ Boolean

book.yml の lint.disabled_rules / trim_long_vowel に該当する指摘か

Returns:

  • (Boolean)


58
59
60
61
62
63
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 58

def self.disabled_message?(message, disabled_rules, trim_long_vowel = false)
  rule = message['ruleId'].to_s
  return true if disabled_rules.include?(rule) || disabled_rules.include?(short_rule(rule))

  trim_long_vowel && long_vowel_addition?(message_head(message['message']))
end

.grouping_head(message, rule_id) ⇒ Object

集約見出し:出現ごとに数値が変わるルール(sentence-length 等)は要約ラベルで 1 つに畳み、 それ以外は先頭行そのまま("一つ => 1つ" の数字など、意味のある数値を保つ)。



95
96
97
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 95

def self.grouping_head(message, rule_id)
  RULE_SUMMARIES[short_rule(rule_id)] || message_head(message)
end

.long_vowel_addition?(head) ⇒ Boolean

「X => Xー」(末尾に長音記号を足すだけ)の表記揺れ指摘か。 技術者向けに「サーバ/パラメータ/フィルタ」等の末尾長音を省く文体を選べるようにする。

Returns:

  • (Boolean)


67
68
69
70
71
72
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 67

def self.long_vowel_addition?(head)
  m = head.match(/\A(.+?)\s*=>\s*(.+)\z/)
  return false unless m

  m[2].strip == "#{m[1].strip}"
end

.message_head(message) ⇒ Object

メッセージの先頭行(actionable な指摘部分。prh の置換や ja-spacing の本文)



100
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 100

def self.message_head(message) = message.to_s.lines.first.to_s.strip

.relative_md_path(path, base_dir) ⇒ Object

textlint の絶対パスを base_dir 相対へ



106
107
108
109
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 106

def self.relative_md_path(path, base_dir)
  rel = path.to_s.sub(%r{\A#{Regexp.escape(base_dir.to_s)}/?}, '')
  rel.empty? ? path.to_s : rel
end

.short_rule(rule_id) ⇒ Object

ルール ID を短縮("ja-spacing/ja-space-around-code" → "ja-space-around-code")



103
# File 'lib/vivlio_starter/cli/textlint_formatter.rb', line 103

def self.short_rule(rule_id) = rule_id.to_s.split('/').last.to_s