Class: VivlioStarter::CLI::LintCommands::LintRunner

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

Overview

text:lint 実行ロジックをまとめたランナー

Defined Under Namespace

Classes: TargetResolver

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(targets, options) ⇒ LintRunner

Returns a new instance of LintRunner.



87
88
89
90
# File 'lib/vivlio_starter/cli/lint.rb', line 87

def initialize(targets, options)
  @targets = Array(targets)
  @options = normalize_options(options)
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



85
86
87
# File 'lib/vivlio_starter/cli/lint.rb', line 85

def options
  @options
end

#targetsObject (readonly)

Returns the value of attribute targets.



85
86
87
# File 'lib/vivlio_starter/cli/lint.rb', line 85

def targets
  @targets
end

Instance Method Details

#callObject



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/vivlio_starter/cli/lint.rb', line 98

def call
  ensure_textlint_available! unless spellcheck_only?
  ensure_config_present! unless spellcheck_only?
  ensure_support_yaml_files! unless spellcheck_only?

  files = resolve_targets
  if files.empty?
    Common.log_warn('検査対象となる Markdown ファイルが見つかりません。')
    return 0
  end

  lint_info  = { exit: 0, lint_count: 0, fixable_count: 0, fixed_count: 0 }
  spell_info = { exit: 0, spell_count: 0 }

  lint_info  = run_textlint(files) unless spellcheck_only?
  spell_info = run_spellcheck(files) unless textlint_only?

  print_combined_summary(lint_info, spell_info)
  [lint_info[:exit], spell_info[:exit]].max
rescue LintError => e
  Common.log_error(e.message)
  1
end

#disabled_rulesObject

book.yml lint.disabled_rules(ルール ID で丸ごと無効化)



165
166
167
# File 'lib/vivlio_starter/cli/lint.rb', line 165

def disabled_rules
  Array(Common::CONFIG.lint.disabled_rules).map(&:to_s)
end


174
175
176
177
178
179
180
181
182
183
# File 'lib/vivlio_starter/cli/lint.rb', line 174

def print_textlint_aggregated(result)
  result[:files].each do |file|
    Common.log_always "📄 #{file[:path]}  (textlint)"
    file[:rows].each do |row|
      Common.log_always format('  %3d件  %s', row[:count], row[:label])
      Common.log_always format('         行: %s', row[:lines])
    end
    Common.log_always ''
  end
end

#run_textlint(files) ⇒ Object

textlint 本体を実行して結果サマリーを返す。 出力は常にルール単位の集約表示(textlint --format json を取得して整形)。

--fix 指定時は「修正パス → 解析パス」の 2 段構成を採る。修正パスは記法ガードを 通さない一時ファイルを textlint に直させて原稿へ書き戻し、解析パスはガード済みの 一時ファイルから残存指摘を集める。ガードによる記法の中和は非可逆なので、 ガード済みの内容を原稿へ書き戻すことはできない(両立させるための 2 パス)。 仕様: lint-notation-guard-spec.md §2.3



130
131
132
133
134
135
136
137
138
139
140
# File 'lib/vivlio_starter/cli/lint.rb', line 130

def run_textlint(files)
  fixed_count = options[:fix] ? apply_textlint_fixes!(files).size : 0

  converted_files = convert_vs_lint_comments(files)
  # textlint は一時ファイルを検査するため、出力の一時パスを元ファイル名へ戻すマップ
  path_map = files.zip(converted_files).to_h { |orig, tmp| [File.expand_path(tmp), orig] }
  run_textlint_aggregated(converted_files, path_map).merge(fixed_count: fixed_count)
ensure
  cleanup_temp_files(converted_files) if converted_files
  @runtime_config_tmp&.unlink
end

#run_textlint_aggregated(files, path_map = {}) ⇒ Object

ルール単位で集約した独自表示(--format json で取得して整形)



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/vivlio_starter/cli/lint.rb', line 143

def run_textlint_aggregated(files, path_map = {})
  command = build_command(files, format: 'json')
  stdout, stderr, status = Open3.capture3(*command)
  $stderr.print(stderr) unless stderr.nil? || stderr.empty?

  result = TextlintFormatter.aggregate_json(
    stdout, disabled_rules: disabled_rules, trim_long_vowel: trim_long_vowel?
  )
  if result.nil?
    # JSON 解釈に失敗(textlint 自体のエラー等)。生出力をそのまま見せる。
    $stdout.print(stdout) unless stdout.nil? || stdout.empty?
    return { exit: textlint_exit(status), lint_count: 0, fixable_count: 0 }
  end

  # 一時ファイルのパスを元ファイル名へ戻す
  result[:files].each { |f| f[:path] = path_map[File.expand_path(f[:path])] || f[:path] }
  print_textlint_aggregated(result)
  # 無効化で除外した分は問題数に数えない(残り 0 なら成功扱い)
  { exit: result[:total].positive? ? 1 : 0, lint_count: result[:total], fixable_count: result[:fixable] }
end

#spellcheck_only?Boolean

--spellcheck-only / --textlint-only / --register による実行範囲。 --register はスペルチェック専用の操作なので、暗黙に spellcheck 単独で動く (--spellcheck-only を併記する必要はない)。

Returns:

  • (Boolean)


95
# File 'lib/vivlio_starter/cli/lint.rb', line 95

def spellcheck_only? = options[:register] || options[:spellcheck_only]

#textlint_exit(status) ⇒ Object



185
# File 'lib/vivlio_starter/cli/lint.rb', line 185

def textlint_exit(status) = status.success? ? 0 : (status.exitstatus || 1)

#textlint_only?Boolean

Returns:

  • (Boolean)


96
# File 'lib/vivlio_starter/cli/lint.rb', line 96

def textlint_only?   = !options[:register] && options[:textlint_only]

#trim_long_vowel?Boolean

book.yml lint.trim_long_vowel(末尾長音を足す指摘を抑止:技術者向け文体)

Returns:

  • (Boolean)


170
171
172
# File 'lib/vivlio_starter/cli/lint.rb', line 170

def trim_long_vowel?
  Common.truthy?(Common::CONFIG.lint.trim_long_vowel)
end