lib/review/tocprinter.rb in review-5.0.0 vs lib/review/tocprinter.rb in review-5.1.0

- old
+ new

@@ -1,6 +1,6 @@ -# Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto +# Copyright (c) 2008-2021 Minero Aoki, Kenshi Muto # 1999-2007 Minero Aoki # # This program is free software. # You can distribute or modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. @@ -34,38 +34,49 @@ @blank_seen = true end end class TOCPrinter - def self.execute(*args) - Signal.trap(:INT) { exit 1 } - if RUBY_PLATFORM !~ /mswin(?!ce)|mingw|cygwin|bccwin/ - Signal.trap(:PIPE, 'IGNORE') + class Counter + def initialize(name: nil, level: nil, headline: nil, lines: nil, chars: nil, list_lines: nil, text_lines: nil, part: nil) + @name = name + @level = level + @headline = headline + @lines = lines + @chars = chars + @list_lines = list_lines + @text_lines = text_lines + @part = part end + + attr_accessor :name, :level, :headline, :lines, :chars, :list_lines, :text_lines, :part + end + + def self.execute(*args) new.execute(*args) - rescue Errno::EPIPE - exit 0 end def initialize @logger = ReVIEW.logger - @config = ReVIEW::Configure.values @yamlfile = 'config.yml' - @book = ReVIEW::Book::Base.new('.', config: @config) @upper = 4 @indent = true @buildonly = nil @detail = nil + @calc_char_width = nil end + attr_accessor :calc_char_width + def execute(*args) parse_options(args) + @config = ReVIEW::Configure.create(yamlfile: @yamlfile) + @book = ReVIEW::Book::Base.new('.', config: @config) unless File.readable?(@yamlfile) @logger.error("No such fiile or can't open #{@yamlfile}.") exit 1 end - @book.load_config(@yamlfile) I18n.setup(@config['language']) if @detail begin require 'unicode/eaw' @@ -82,31 +93,31 @@ def build_result_array result_array = [] begin @book.parts.each do |part| if part.name.present? && (@buildonly.nil? || @buildonly.include?(part.name)) - result_array.push({ part: 'start' }) + result_array.push(Counter.new(part: 'start')) if part.file? - result = build_chap(part) - result_array += parse_contents(part.name, @upper, result) + content = build_chap(part) + result_array.concat(parse_contents(part.name, @upper, content)) else title = part.format_number + I18n.t('chapter_postfix') + part.title - result_array += [ - { name: '', lines: 1, chars: title.size, list_lines: 0, text_lines: 1 }, - { level: 0, headline: title, lines: 1, chars: title.size, list_lines: 0, text_lines: 1 } - ] + result_array.push( + Counter.new(name: '', lines: 1, chars: title.size, list_lines: 0, text_lines: 1), + Counter.new(level: 0, headline: title, lines: 1, chars: title.size, list_lines: 0, text_lines: 1) + ) end end part.chapters.each do |chap| if @buildonly.nil? || @buildonly.include?(chap.name) - result = build_chap(chap) - result_array += parse_contents(chap.name, @upper, result) + content = build_chap(chap) + result_array.concat(parse_contents(chap.name, @upper, content)) end end if part.name.present? && (@buildonly.nil? || @buildonly.include?(part.name)) - result_array.push({ part: 'end' }) + result_array.push(Counter.new(part: 'end')) end end rescue ReVIEW::FileNotFound, ReVIEW::CompileError => e @logger.error e exit 1 @@ -115,62 +126,59 @@ result_array end def print_result(result_array) result_array.each do |result| - if result[:part] + if result.part next end - if result[:name] + if result.name # file information if @detail puts '=============================' - printf("%6dC %5dL %5dP %s\n", result[:chars], result[:lines], calc_pages(result).ceil, result[:name]) + printf("%6dC %5dL %5dP %s\n", result.chars, result.lines, calc_pages(result).ceil, result.name) puts '-----------------------------' end next end # section information if @detail - printf('%6dC %5dL %5.1fP ', result[:chars], result[:lines], calc_pages(result)) + printf('%6dC %5dL %5.1fP ', result.chars, result.lines, calc_pages(result)) end - if @indent && result[:level] - print ' ' * (result[:level] == 0 ? 0 : result[:level] - 1) + if @indent && result.level + print ' ' * (result.level == 0 ? 0 : result.level - 1) end - puts result[:headline] + puts result.headline end end def calc_pages(result) - p = 0 - p += result[:list_lines].to_f / @book.page_metric.list.n_lines - p += result[:text_lines].to_f / @book.page_metric.text.n_lines - p + (result.list_lines.to_f / @book.page_metric.list.n_lines) + + (result.text_lines.to_f / @book.page_metric.text.n_lines) end - def calc_linesize(l) - return l.size unless @calc_char_width - w = 0 - l.split('').each do |c| + def calc_linesize(line) + return line.size unless @calc_char_width + + line.each_char.inject(0) do |result, char| # XXX: should include A also? - if %i[Na H N].include?(Unicode::Eaw.property(c)) - w += 0.5 # halfwidth + if %i[Na H N].include?(Unicode::Eaw.property(char)) + result + 0.5 # halfwidth else - w += 1 + result + 1 end end - w end def parse_contents(name, upper, content) headline_array = [] - counter = { lines: 0, chars: 0, list_lines: 0, text_lines: 0 } + counter = Counter.new(lines: 0, chars: 0, list_lines: 0, text_lines: 0) listmode = nil - content.split("\n").each do |l| + content.each_line(chomp: true) do |l| if l.start_with?("\x01STARTLIST\x01") listmode = true next elsif l.start_with?("\x01ENDLIST\x01") listmode = nil @@ -178,69 +186,78 @@ elsif l =~ /\A\x01H(\d)\x01/ # headline level = $1.to_i l = $' if level <= upper - if counter[:chars] > 0 + if counter.chars > 0 headline_array.push(counter) end headline = l - counter = { + counter = Counter.new( level: level, headline: headline, lines: 1, chars: headline.size, list_lines: 0, text_lines: 1 - } + ) next end end - counter[:lines] += 1 - counter[:chars] += l.size + counter.lines += 1 + counter.chars += l.size if listmode # code list: calculate line wrapping - if l.size == 0 - counter[:list_lines] += 1 - else - counter[:list_lines] += (calc_linesize(l) - 1) / @book.page_metric.list.n_columns + 1 - end + counter.list_lines += calc_line_wrapping(l, mode: :list) else # normal paragraph: calculate line wrapping - if l.size == 0 - counter[:text_lines] += 1 - else - counter[:text_lines] += (calc_linesize(l) - 1) / @book.page_metric.text.n_columns + 1 - end + counter.text_lines += calc_line_wrapping(l, mode: :text) end end headline_array.push(counter) - total_lines = 0 - total_chars = 0 - total_list_lines = 0 - total_text_lines = 0 + total = calc_total_count(name, headline_array) + headline_array.unshift(total) + end + def calc_line_wrapping(line, mode:) + return 1 if line.size == 0 + + case mode + when :list + (calc_linesize(line) - 1) / @book.page_metric.list.n_columns + 1 + else # mode == :text + (calc_linesize(line) - 1) / @book.page_metric.text.n_columns + 1 + end + end + + def calc_total_count(name, headline_array) + total = Counter.new(name: name, + lines: 0, + chars: 0, + list_lines: 0, + text_lines: 0) + headline_array.each do |h| - next unless h[:lines] - total_lines += h[:lines] - total_chars += h[:chars] - total_list_lines += h[:list_lines] - total_text_lines += h[:text_lines] + next unless h.lines + + total.lines += h.lines + total.chars += h.chars + total.list_lines += h.list_lines + total.text_lines += h.text_lines end - headline_array.delete_if(&:empty?). - unshift({ name: name, lines: total_lines, chars: total_chars, list_lines: total_list_lines, text_lines: total_text_lines }) + total end def build_chap(chap) compiler = ReVIEW::Compiler.new(ReVIEW::PLAINTEXTTocBuilder.new) begin compiler.compile(@book.chapter(chap.name)) rescue ReVIEW::ApplicationError => e - @logger.error e + @logger.error e.message exit 1 end end def parse_options(args)