stevenhallen/document_generator

View on GitHub
lib/document_generator/diff_file.rb

Summary

Maintainability
B
4 hrs
Test Coverage
require 'cgi'

module DocumentGenerator
  class DiffFile
    attr_accessor :git_diff_file

    def type
      git_diff_file.type
    end

    def initialize(git_diff_file)
      @git_diff_file = git_diff_file
    end

    def patch_heading
      "#{action_type} `#{git_diff_file.path}`"
    end

    def git_diff_file_hunks
      hunks = git_diff_file.patch.split(/@@.*@@.*\n/)
      hunks.shift # Shift to pop first element off array which is just git diff header info
      hunks
    end

    def git_diff_lines_for(hunk)
      hunk.split("\n")
    end

    def content
      if type == 'deleted'
        return "####{patch_heading}\n\n"
      end

      temp = []
      temp << "####{patch_heading}"

      outputs = markdown_outputs
      if outputs.any?
        outputs.each do |output|
          if output.escaped_content.length > 0
            temp << "\n\n"
            temp << "#####{output.description}"
            temp << "\n```\n"
            if output.description == "Becomes"
              temp << output.content.join("\n") + "\n"
            else
              temp << output.escaped_content
            end
            temp << "\n```\n"
          end
        end

      end

      temp << "\n\n"

      temp.join
    end

    def ending_code
      clean_hunks = []
      git_diff_file_hunks.each do |hunk|
        clean_hunks << ending_code_for(hunk).join("\n")
      end
      clean_hunks.join("\n")
    end

    def ending_code_for(hunk)
      clean_lines = []

      git_diff_lines_for(hunk).each_with_index do |line, index|
        if (line[0]) == "-" || ignore_line?(line)
          next
        end

        if (line[0]) == "+"
          line = remove_first_character(line)
        end
        clean_lines << line
      end
      clean_lines
    end

    def action_type
      { new: 'Create file',
        modified: 'Update file',
        deleted: 'Remove file' }.fetch(type.to_sym, type)
    end

    def markdown_outputs
      outputs = []
      git_diff_file_hunks.each do |hunk|
        outputs << markdown_outputs_for(hunk)
      end
      outputs.flatten
    end

    def markdown_outputs_for(hunk) # returns an array of outputs for a particular hunk
      outputs = []
      last_line = -1
      git_diff_lines_for(hunk).each_with_index do |line, index|
        next if index <= last_line
        case line.strip[0]

        when "+"
          last_line = last_same_line(index, hunk)
          output = Output.new
          output.description = "Add"
          output.content = line_block(index, last_line, hunk)
          outputs << output
        when "-"
          if line_sign(index + 1, hunk) == "+"
            output = Output.new
            output.description = "Change"
            output.content = line_block(index, last_same_line(index, hunk), hunk)
            outputs << output
            last_line = last_same_line(last_same_line(index, hunk) + 1, hunk)

            output = Output.new
            output.description = "To"
            output.content = line_block(last_same_line(index, hunk) + 1, last_line, hunk)
            outputs << output
            last_line = last_same_line(last_same_line(index, hunk) + 1, hunk)
          else
            output = Output.new
            output.description = "Remove"
            last_line = last_same_line(index, hunk)
            output.content = line_block(index, last_line, hunk)
            outputs << output
          end
        end
      end

      if git_diff_file.type == 'modified'
        output = Output.new
        output.description = "Becomes"
        output.content = ending_code_for(hunk)
        outputs << output
      end

      outputs
    end



    private

    def ignore_line?(line)
      line.strip == 'No newline at end of file'
    end

    def last_same_line(line_index, hunk)
      starting_sign = line_sign(line_index, hunk)

      git_diff_lines_for(hunk)[line_index..-1].each_with_index do |line, index|
        if line_sign(index + 1 + line_index, hunk) != starting_sign
          return (index + line_index)
        end
      end
    end

    def line_block(beginning, ending, hunk)
      lines = []
      git_diff_lines_for(hunk)[beginning..ending].each do |line|
        if ["+", "-"].include?(line[0..0])
          line = remove_first_character(line)
        end
        if !ignore_line?(line)
          lines << line
        end
      end
      lines
    end

    def line_sign(line_number, hunk)
      (git_diff_lines_for(hunk)[line_number] || '').strip[0]
    end

    def remove_first_character(line)
      " " + line[1..-1]
    end
  end
end