Commit 56d6a113 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Extract BatchParser class from App.

Move all the messy parsing logic away from already overcomplicated App
class.  Now the App is left with pretty much just one method that
represents the flow of the entire app.  It's still too complicated
though.
parent 029f5e45
Loading
Loading
Loading
Loading
+12 −75
Original line number Diff line number Diff line
require 'rubygems'
require 'jsduck/aggregator'
require 'jsduck/source/file'
require 'jsduck/util/parallel'
require 'jsduck/util/json'
require 'jsduck/util/stdout'
require 'jsduck/source/writer'
require 'jsduck/exporter/api'
require 'jsduck/exporter/full'
require 'jsduck/exporter/app'
require 'jsduck/exporter/examples'
require 'jsduck/batch_parser'
require 'jsduck/batch_formatter'
require 'jsduck/class'
require 'jsduck/relations'
require 'jsduck/inherit_doc'
require 'jsduck/util/parallel'
require 'jsduck/logger'
require 'jsduck/assets'
require 'jsduck/util/json'
require 'jsduck/util/io'
require 'jsduck/importer'
require 'jsduck/return_values'
require 'jsduck/lint'
@@ -18,13 +19,8 @@ require 'jsduck/template_dir'
require 'jsduck/class_writer'
require 'jsduck/app_data'
require 'jsduck/index_html'
require 'jsduck/exporter/api'
require 'jsduck/exporter/full'
require 'jsduck/exporter/app'
require 'jsduck/exporter/examples'
require 'jsduck/inline_examples'
require 'jsduck/guide_writer'
require 'jsduck/util/stdout'
require 'fileutils'

module JsDuck
@@ -43,9 +39,9 @@ module JsDuck

    # Call this after input parameters set
    def run
      parsed_files = parallel_parse(@opts.input_files)
      result = aggregate(parsed_files)
      @relations = filter_classes(result)
      batch_parser = BatchParser.new(@opts)
      @relations = batch_parser.run

      InheritDoc.new(@relations).resolve_all
      Importer.import(@opts.imports, @relations, @opts.new_since)
      ReturnValues.auto_detect(@relations)
@@ -84,7 +80,7 @@ module JsDuck
        # between source files and classes. Therefore it MUST to be done
        # after writing sources which needs the links to work.
        if @opts.source
          source_writer = Source::Writer.new(parsed_files)
          source_writer = Source::Writer.new(batch_parser.parsed_files)
          source_writer.write(@opts.output_dir + "/source")
        end
        format_classes
@@ -103,65 +99,6 @@ module JsDuck
      end
    end

    # Parses the files in parallel using as many processes as available CPU-s
    def parallel_parse(filenames)
      Util::Parallel.map(filenames) do |fname|
        Logger.log("Parsing", fname)
        begin
          Source::File.new(Util::IO.read(fname), fname, @opts)
        rescue
          Logger.fatal_backtrace("Error while parsing #{fname}", $!)
          exit(1)
        end
      end
    end

    # Aggregates parsing results sequencially
    def aggregate(parsed_files)
      agr = Aggregator.new
      parsed_files.each do |file|
        Logger.log("Aggregating", file.filename)
        agr.aggregate(file)
      end
      agr.classify_orphans
      agr.create_global_class
      agr.remove_ignored_classes
      agr.create_accessors
      if @opts.ext4_events == true || (@opts.ext4_events == nil && agr.ext4?)
        agr.append_ext4_event_options
      end
      agr.process_enums
      # Ignore override classes after applying them to actual classes
      @opts.external_classes += agr.process_overrides.map {|o| o[:name] }
      agr.result
    end

    # Turns all aggregated data into Class objects.
    # Depending on --ignore-global either keeps or discards the global class.
    # Warnings for global members are printed regardless of that setting,
    # but of course can be turned off using --warnings=-global
    def filter_classes(docs)
      classes = []
      docs.each do |d|
        cls = Class.new(d)
        if d[:name] != "global"
          classes << cls
        else
          # add global class only if --ignore-global not specified
          classes << cls unless @opts.ignore_global

          # Print warning for each global member
          cls.all_local_members.each do |m|
            type = m[:tagname].to_s
            name = m[:name]
            file = m[:files][0]
            Logger.warn(:global, "Global #{type}: #{name}", file[:filename], file[:linenr])
          end
        end
      end
      Relations.new(classes, @opts.external_classes)
    end

    # Formats each class
    def format_classes
      BatchFormatter.format_all!(@relations, @assets, @opts)
+92 −0
Original line number Diff line number Diff line
require 'jsduck/util/parallel'
require 'jsduck/util/io'
require 'jsduck/source/file'
require 'jsduck/aggregator'
require 'jsduck/class'
require 'jsduck/relations'
require 'jsduck/logger'

module JsDuck

  # Performs the parsing of all input files.  Input files are read
  # from options object (originating from command line).
  class BatchParser
    def initialize(opts)
      @opts = opts
    end

    # Array of Source::File objects.
    # Available after calling the #run method.
    attr_reader :parsed_files

    # Parses the files and returns instance of Relations class.
    def run
      @parsed_files = parallel_parse(@opts.input_files)
      result = aggregate(@parsed_files)
      return filter_classes(result)
    end

    private

    # Parses the files in parallel using as many processes as available CPU-s
    def parallel_parse(filenames)
      Util::Parallel.map(filenames) do |fname|
        Logger.log("Parsing", fname)
        begin
          Source::File.new(Util::IO.read(fname), fname, @opts)
        rescue
          Logger.fatal_backtrace("Error while parsing #{fname}", $!)
          exit(1)
        end
      end
    end

    # Aggregates parsing results sequencially
    def aggregate(parsed_files)
      agr = Aggregator.new
      parsed_files.each do |file|
        Logger.log("Aggregating", file.filename)
        agr.aggregate(file)
      end
      agr.classify_orphans
      agr.create_global_class
      agr.remove_ignored_classes
      agr.create_accessors
      if @opts.ext4_events == true || (@opts.ext4_events == nil && agr.ext4?)
        agr.append_ext4_event_options
      end
      agr.process_enums
      # Ignore override classes after applying them to actual classes
      @opts.external_classes += agr.process_overrides.map {|o| o[:name] }
      agr.result
    end

    # Turns all aggregated data into Class objects.
    # Depending on --ignore-global either keeps or discards the global class.
    # Warnings for global members are printed regardless of that setting,
    # but of course can be turned off using --warnings=-global
    def filter_classes(docs)
      classes = []
      docs.each do |d|
        cls = Class.new(d)
        if d[:name] != "global"
          classes << cls
        else
          # add global class only if --ignore-global not specified
          classes << cls unless @opts.ignore_global

          # Print warning for each global member
          cls.all_local_members.each do |m|
            type = m[:tagname].to_s
            name = m[:name]
            file = m[:files][0]
            Logger.warn(:global, "Global #{type}: #{name}", file[:filename], file[:linenr])
          end
        end
      end
      Relations.new(classes, @opts.external_classes)
    end

  end

end