<commit_before>require 'json'
require 'tempfile'
require 'version_sorter'
require 'rocco'
require 'docurium/layout'
require 'docurium/cparser'
require 'pp'
require 'rugged'

class Docurium
  Version = VERSION = '0.0.5'

  attr_accessor :branch, :output_dir, :data

  def initialize(config_file)
    raise "You need to specify a config file" if !config_file
    raise "You need to specify a valid config file" if !valid_config(config_file)
    @sigs = {}
    @groups = {}
    repo_path = Rugged::Repository.discover('.')
    @repo = Rugged::Repository.new(repo_path)
    clear_data
  end

  def clear_data(version = 'HEAD')
    @data = {:files => [], :functions => {}, :globals => {}, :types => {}, :prefix => ''}
    @data[:prefix] = option_version(version, 'input', '')
  end

  def option_version(version, option, default = nil)
    if @options['legacy']
      if valhash = @options['legacy'][option]
        valhash.each do |value, versions|
          return value if versions.include?(version)
        end
      end
    end
    opt = @options[option]
    opt = default if !opt
    opt
  end

  def generate_docs
    out "* generating docs"
    output_index = Rugged::Index.new
    write_site(output_index)
    versions = get_versions
    versions << 'HEAD'
    versions.each do |version|
      out "  - processing version #{version}"
      index = @repo.index
      index.clear
      clear_data(version)
      read_subtree(index, version, @data[:prefix])
      parse_headers(index)
      tally_sigs(version)

      tf = File.expand_path(File.join(File.dirname(__FILE__), 'docurium', 'layout.mustache'))
      if ex = option_version(version, 'examples')
        if subtree = find_subtree(version, ex) # check that it exists
          index.read_tree(subtree)
          out "  - processing examples for #{version}"

          files = []
          index.each do |entry|
            next unless entry[:path].match(/\.c$/)
            files << entry[:path]
          end

          files.each do |file|
            out "    # #{file}"

            # highlight, roccoize and link
            rocco = Rocco.new(file, files, {:language => 'c'}) do
              ientry = index[file]
              blob = @repo.lookup(ientry[:oid])
              blob.content
            end
            rocco_layout = Rocco::Layout.new(rocco, tf)
            rocco_layout.version = version
            rf = rocco_layout.render

            rf_path = File.basename(file).split('.')[0..-2].join('.') + '.html'
            rel_path = "ex/#{version}/#{rf_path}"

            # look for function names in the examples and link
            id_num = 0
            @data[:functions].each do |f, fdata|
              rf.gsub!(/#{f}([^\w])/) do |fmatch|
                extra = $1
                id_num += 1
                name = f + '-' + id_num.to_s
                # save data for cross-link
                @data[:functions][f][:examples] ||= {}
                @data[:functions][f][:examples][file] ||= []
                @data[:functions][f][:examples][file] << rel_path + '#' + name
                "<a name=\"#{name}\" class=\"fnlink\" href=\"../../##{version}/group/#{fdata[:group]}/#{f}\">#{f}</a>#{extra}"
              end
            end

            # write example to the repo
            sha = @repo.write(rf, :blob)
            output_index.add(:path => rel_path, :oid => sha, :mode => 0100644)

            @data[:examples] ||= []
            @data[:examples] << [file, rel_path]
          end
        end

        if version == 'HEAD'
          show_warnings
        end
      end

      sha = @repo.write(@data.to_json, :blob)
      output_index.add(:path => "#{version}.json", :oid => sha, :mode => 0100644)
    end

    project = {
      :versions => versions.reverse,
      :github   => @options['github'],
      :name     => @options['name'],
      :signatures => @sigs,
      :groups   => @groups
    }
    sha = @repo.write(project.to_json, :blob)
    output_index.add(:path => "project.json", :oid => sha, :mode => 0100644)

    if br = @options['branch']
      out "* writing to branch #{br}"
      refname = "refs/heads/#{br}"
      tsha = output_index.write_tree(@repo)
      puts "\twrote tree   #{tsha}"
      ref = Rugged::Reference.lookup(@repo, refname)
      user = { :name => @repo.config['user.name'], :email => @repo.config['user.email'], :time => Time.now }
      options = {}
      options[:tree] = tsha
      options[:author] = user
      options[:committer] = user
      options[:message] = 'generated docs'
      options[:parents] = ref ? [ref.target] : []
      options[:update_ref] = refname
      csha = Rugged::Commit.create(@repo, options)
      puts "\twrote commit #{csha}"
      puts "\tupdated #{br}"
    else
      final_dir = File.join(@project_dir, @options['output'] || 'docs')
      out "* output html in #{final_dir}"
      FileUtils.mkdir_p(final_dir)
      Dir.chdir(final_dir) do
        FileUtils.cp_r(File.join(outdir, '.'), '.') 
      end
    end
  end

  def show_warnings
    out '* checking your api'

    # check for unmatched paramaters
    unmatched = []
    @data[:functions].each do |f, fdata|
      unmatched << f if fdata[:comments] =~ /@param/
    end
    if unmatched.size > 0
      out '  - unmatched params in'
      unmatched.sort.each { |p| out ("\t" + p) }
    end

    # check for changed signatures
    sigchanges = []
    @sigs.each do |fun, data|
      if data[:changes]['HEAD']
        sigchanges << fun
      end
    end
    if sigchanges.size > 0
      out '  - signature changes in'
      sigchanges.sort.each { |p| out ("\t" + p) }
    end
  end<commit_msg>Raise an error if there is no branch configured<commit_after>require 'json'
require 'tempfile'
require 'version_sorter'
require 'rocco'
require 'docurium/layout'
require 'docurium/cparser'
require 'pp'
require 'rugged'

class Docurium
  Version = VERSION = '0.0.5'

  attr_accessor :branch, :output_dir, :data

  def initialize(config_file)
    raise "You need to specify a config file" if !config_file
    raise "You need to specify a valid config file" if !valid_config(config_file)
    @sigs = {}
    @groups = {}
    repo_path = Rugged::Repository.discover('.')
    @repo = Rugged::Repository.new(repo_path)
    clear_data
  end

  def clear_data(version = 'HEAD')
    @data = {:files => [], :functions => {}, :globals => {}, :types => {}, :prefix => ''}
    @data[:prefix] = option_version(version, 'input', '')
  end

  def option_version(version, option, default = nil)
    if @options['legacy']
      if valhash = @options['legacy'][option]
        valhash.each do |value, versions|
          return value if versions.include?(version)
        end
      end
    end
    opt = @options[option]
    opt = default if !opt
    opt
  end

  def generate_docs
    out "* generating docs"
    output_index = Rugged::Index.new
    write_site(output_index)
    versions = get_versions
    versions << 'HEAD'
    versions.each do |version|
      out "  - processing version #{version}"
      index = @repo.index
      index.clear
      clear_data(version)
      read_subtree(index, version, @data[:prefix])
      parse_headers(index)
      tally_sigs(version)

      tf = File.expand_path(File.join(File.dirname(__FILE__), 'docurium', 'layout.mustache'))
      if ex = option_version(version, 'examples')
        if subtree = find_subtree(version, ex) # check that it exists
          index.read_tree(subtree)
          out "  - processing examples for #{version}"

          files = []
          index.each do |entry|
            next unless entry[:path].match(/\.c$/)
            files << entry[:path]
          end

          files.each do |file|
            out "    # #{file}"

            # highlight, roccoize and link
            rocco = Rocco.new(file, files, {:language => 'c'}) do
              ientry = index[file]
              blob = @repo.lookup(ientry[:oid])
              blob.content
            end
            rocco_layout = Rocco::Layout.new(rocco, tf)
            rocco_layout.version = version
            rf = rocco_layout.render

            rf_path = File.basename(file).split('.')[0..-2].join('.') + '.html'
            rel_path = "ex/#{version}/#{rf_path}"

            # look for function names in the examples and link
            id_num = 0
            @data[:functions].each do |f, fdata|
              rf.gsub!(/#{f}([^\w])/) do |fmatch|
                extra = $1
                id_num += 1
                name = f + '-' + id_num.to_s
                # save data for cross-link
                @data[:functions][f][:examples] ||= {}
                @data[:functions][f][:examples][file] ||= []
                @data[:functions][f][:examples][file] << rel_path + '#' + name
                "<a name=\"#{name}\" class=\"fnlink\" href=\"../../##{version}/group/#{fdata[:group]}/#{f}\">#{f}</a>#{extra}"
              end
            end

            # write example to the repo
            sha = @repo.write(rf, :blob)
            output_index.add(:path => rel_path, :oid => sha, :mode => 0100644)

            @data[:examples] ||= []
            @data[:examples] << [file, rel_path]
          end
        end

        if version == 'HEAD'
          show_warnings
        end
      end

      sha = @repo.write(@data.to_json, :blob)
      output_index.add(:path => "#{version}.json", :oid => sha, :mode => 0100644)
    end

    project = {
      :versions => versions.reverse,
      :github   => @options['github'],
      :name     => @options['name'],
      :signatures => @sigs,
      :groups   => @groups
    }
    sha = @repo.write(project.to_json, :blob)
    output_index.add(:path => "project.json", :oid => sha, :mode => 0100644)

    if br = @options['branch']
      out "* writing to branch #{br}"
      refname = "refs/heads/#{br}"
      tsha = output_index.write_tree(@repo)
      puts "\twrote tree   #{tsha}"
      ref = Rugged::Reference.lookup(@repo, refname)
      user = { :name => @repo.config['user.name'], :email => @repo.config['user.email'], :time => Time.now }
      options = {}
      options[:tree] = tsha
      options[:author] = user
      options[:committer] = user
      options[:message] = 'generated docs'
      options[:parents] = ref ? [ref.target] : []
      options[:update_ref] = refname
      csha = Rugged::Commit.create(@repo, options)
      puts "\twrote commit #{csha}"
      puts "\tupdated #{br}"
    else
      raise "This version only supports writing to a branch"
    end
  end

  def show_warnings
    out '* checking your api'

    # check for unmatched paramaters
    unmatched = []
    @data[:functions].each do |f, fdata|
      unmatched << f if fdata[:comments] =~ /@param/
    end
    if unmatched.size > 0
      out '  - unmatched params in'
      unmatched.sort.each { |p| out ("\t" + p) }
    end

    # check for changed signatures
    sigchanges = []
    @sigs.each do |fun, data|
      if data[:changes]['HEAD']
        sigchanges << fun
      end
    end
    if sigchanges.size > 0
      out '  - signature changes in'
      sigchanges.sort.each { |p| out ("\t" + p) }
    end
  end
{'fraction_non_alphanumeric': 0.14937347877039575, 'fraction_numerical': 0.005589110249707023, 'mean_word_length': 2.154392948535684, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1724895', 'n_tokens_mistral': 3543, 'n_tokens_neox': 3433, 'n_words': 838}