# frozen_string_literal: true # # Copyright 2013 whiteleaf. All rights reserved. # # # 小説の状態を監視・検査する # class Inspector INSPECT_LOG_NAME = "調査ログ.txt" LINE_LENGTH_THRESHOLD = 400 BRACKETS_RETURN_COUNT_THRESHOLD = 7 END_TOUTEN_COUNT_THRESHOLD = 50 ERROR = 1 WARNING = 2 INFO = 4 ALL = ERROR | WARNING | INFO KLASS_TAG = { ERROR => "エラー", WARNING => "警告", INFO => "INFO" } IGNORE_INDENT_CHAR = "((「『〈《≪【〔―・※[〝\n" AUTO_INDENT_THRESHOLD_RATIO = 0.5 # 括弧等を除く全ての行のうちこの割合以上字下げされてなければ強制字下げする attr_writer :messages, :subtitle def self.read_messages(setting) inspect_log = File.join(setting.archive_path, INSPECT_LOG_NAME) if File.exist?(inspect_log) File.read(inspect_log) else nil end end def initialize(setting) @setting = setting @messages = [] @error = false @warning = false @info = false @subtitle = "" end def empty? @messages.empty? end def error? @error end def warning? @warning end def info? @info end def display_summary(target = $stdout) target.print "小説状態の調査結果を #{Inspector::INSPECT_LOG_NAME} に出力しました(" target.print KLASS_TAG.values.map { |klass_type| num = @messages.count { |msg| msg =~ /^\[#{klass_type}\]/ } "#{klass_type}:#{num}件" }.join("、") target.puts ")" end def display(klass = ALL, target = $stdout) target.puts @messages.map { |msg| if msg =~ /^\[(.+)\]/ key = KLASS_TAG.key($1) if key && (klass & key) != 0 next msg end end nil }.compact.join("\n\n") end def save(path = nil) path = File.join(@setting.archive_path, INSPECT_LOG_NAME) if path.nil? open(path, "w") do |fp| fp.puts "--- ログ出力 #{Time.now} ---" display(ALL, fp) end end def log(message) @messages << message end def info(message) log("[#{KLASS_TAG[INFO]}] #{message}") @info = true end def warning(message) log("[#{KLASS_TAG[WARNING]}] #{message}") @warning = true end def error(message) log("[#{KLASS_TAG[ERROR]}] #{message}") @error = true end def omit_message(strings) navigation = "in #{@subtitle}" if @subtitle.to_s.length > 0 "≫≫≫ 該当箇所 #{navigation}\n..." + strings[0...36].gsub("\n", "\\n") + "..." end # # 連結したかぎ括弧が正常かどうか # def validate_joined_inner_brackets(raw_strings, joined_strings, brackets) error_result = false case # 連結前の文章の改行具合を調べて、改行が閾値を超えた場合意図的な改行とみなす when raw_strings.count("\n") >= BRACKETS_RETURN_COUNT_THRESHOLD warning("改行が規定の回数を超えて検出されました。" + "作者による意図的な改行とみなし、連結を中止しました。\n" + omit_message(raw_strings)) error_result = true # 連結した文章があまりにも長い場合、特殊な用途で使われている可能性がある when joined_strings.length >= LINE_LENGTH_THRESHOLD warning("連結結果が長過ぎます。連結を中止しました。" + "特殊な用途(手紙形式)等でかぎ括弧が使われている可能性があります。\n" + omit_message(raw_strings)) error_result = true end error_result end # # かぎ括弧のとじ開きの異常部分を調査 # def inspect_invalid_openclose_brackets(data, brackets, stack) brackets.each do |bracket| buffer = data.dup while buffer =~ /#{bracket}/ match_before = $`.dup match_after = $'.dup before = ConverterBase.rebuild_brackets(match_before, stack) after = ConverterBase.rebuild_brackets(match_after, stack) error("かぎ括弧(#{bracket})が正しく閉じていません。\n" + omit_message((before[-15..-1] || before) + bracket + after)) buffer = match_before end end end # # 行末読点の状況を調べる # def inspect_end_touten_conditions(data) return if @setting.enable_auto_join_line num = 0 data.scan(/、\n /) do num += 1 end if num > 0 msg = +"#{num}個の行末読点を発見しました。" if num >= END_TOUTEN_COUNT_THRESHOLD msg << "作者による手動改行により改行が多くなっています。" + \ "setting.ini の enable_auto_join_line を true にすることをお薦めします。" end info(msg) end end # # カギ括弧内の改行状況を調べる # def countup_return_in_brackets(data) return if @setting.enable_auto_join_in_brackets max = 0 brackets_num = 0 brackets_num_over_threshould = 0 total = 0 ConverterBase::OPENCLOSE_REGEXPS.each do |openclose| data.scan(openclose) do |match| cnt = match[0].count("\n") brackets_num += 1 total += cnt next if cnt < BRACKETS_RETURN_COUNT_THRESHOLD brackets_num_over_threshould += 1 if cnt > max max = cnt end end end info("カギ括弧内の改行状況:\n" + "検出したカギ括弧数: #{brackets_num}、そのうち#{BRACKETS_RETURN_COUNT_THRESHOLD}個以上改行を含む数: #{brackets_num_over_threshould}\n" + "1つのカギ括弧内で最大の改行数: #{max}、全カギ括弧内での改行合計: #{total}") end # # 行頭字下げをするべきか調べる # def inspect_indent(data) target_line_count = 0 dont_indent_line_count = 0 data.scan(/^[^#{IGNORE_INDENT_CHAR}]/).tap { |a| target_line_count = a.size }.each { |line| head = line[0] if head != " " && head != " " dont_indent_line_count += 1 end } ratio = dont_indent_line_count / target_line_count.to_f return ratio > AUTO_INDENT_THRESHOLD_RATIO end end