# -*- coding: UTF-8 -*- # # 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 = " (([「『〈《≪【〔―・※" AUTO_INDENT_THRESHOLD_RATIO = 0.6 # 全行のうちこの割合以上字下げされてなければ強制字下げする attr_writer :messages, :subtitle def self.read_messages(setting) inspect_log = File.join(setting.archive_path, INSPECT_LOG_NAME) if File.exists?(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(klass = ALL, target = $stderr) 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) dont_indent_line_count = data.scan(/^[^#{IGNORE_INDENT_CHAR}]/).count lines = data.lines # MEMO: Enumerable#size (via http://jp.rubyist.net/magazine/?0041-200Special-note#l11) line_count = (lines.respond_to?(:size) ? lines.size : lines.count) ratio = dont_indent_line_count / line_count.to_f return ratio > AUTO_INDENT_THRESHOLD_RATIO end end