Created
October 16, 2015 00:52
-
-
Save NonLogicalDev/39d494f156add49a18a1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| require 'mustache' | |
| require 'diffy' | |
| require 'colorize' | |
| require 'pry' | |
| require 'optparse' | |
| STUDENT_NAME = "Oleg Utkin" | |
| STUDENT_LOGIN = "cs11whw" | |
| def file_header( filename ) | |
| string = <<-eos | |
| /** | |
| * Name: #{STUDENT_NAME} | |
| * Login: #{STUDENT_LOGIN} | |
| * File: {{filename}} | |
| * Sources of Help: API doc, and my brains | |
| * TODO: Fill this out | |
| */ | |
| eos | |
| options = { | |
| filename: filename | |
| } | |
| Mustache.render(string, options) | |
| end | |
| def class_header( spacing, classname ) | |
| string = <<-eos | |
| /** | |
| * Class: {{classname}} | |
| * Purpose: TODO: fill this out | |
| */ | |
| eos | |
| string = string.lines.each.collect( &->l{ spacing + l } ).join | |
| options = { | |
| classname: classname | |
| } | |
| Mustache.render(string, options) | |
| end | |
| def interface_header( spacing, classname ) | |
| string = <<-eos | |
| /** | |
| * Interface: {{classname}} | |
| * Purpose: TODO: fill this out | |
| */ | |
| eos | |
| string = string.lines.each.collect( &->l{ spacing + l } ).join | |
| options = { | |
| classname: classname | |
| } | |
| Mustache.render(string, options) | |
| end | |
| def method_header( spacing, methodname, returnt, parameters = [] ) | |
| string = <<-eos | |
| /** | |
| * Name: {{methodname}} | |
| * Purpose: TODO: fill this out | |
| {{#has_parameters}} | |
| * Parameters: | |
| {{/has_parameters}} | |
| {{#parameters}} | |
| * {{align}}[{{name}}] - TODO: Fill this out | |
| {{/parameters}} | |
| {{#has_return}} | |
| * Return: TODO: fill this out | |
| {{/has_return}} | |
| */ | |
| eos | |
| string = string.lines.each.collect( &->l { spacing + l } ).join | |
| max_length = parameters.collect( &->x { x.size } ).max | |
| options = { | |
| methodname: methodname, | |
| has_parameters: !(parameters.empty?), | |
| parameters: parameters.collect { |p| { name: p, align: " "*(max_length-p.size) } }, | |
| has_return: !(returnt.nil?)&&returnt!="void" | |
| } | |
| Mustache.render(string, options) | |
| end | |
| def find_closest_comment_block( lines ) | |
| comment_triggered = false | |
| cstart = nil | |
| cend = nil | |
| i = 0 | |
| while i < lines.length do | |
| if (not comment_triggered) && (lines[i] =~ %r{^\s*/\*}) | |
| comment_triggered = true | |
| cstart = i | |
| end | |
| if (lines[i] =~ /\S/) && !comment_triggered | |
| return nil | |
| end | |
| if comment_triggered | |
| if lines[i] =~ %r{\*/} | |
| comment_triggered = false | |
| cend = i | |
| break; | |
| end | |
| end | |
| i += 1 | |
| end | |
| if cstart && cend | |
| return (cstart..cend) | |
| else | |
| return nil | |
| end | |
| end | |
| def find_closest_comment_block_back( lines ) | |
| comment_triggered = false | |
| cstart = nil | |
| cend = nil | |
| i = lines.length - 1 | |
| while i > 0 do | |
| if (not comment_triggered) && (lines[i] =~ %r{\*/}) | |
| comment_triggered = true | |
| cend = i | |
| end | |
| if (lines[i] =~ /\S/) && !comment_triggered | |
| return nil | |
| end | |
| if comment_triggered | |
| if lines[i] =~ %r{^\s*/\*} | |
| cstart = i | |
| comment_triggered = false | |
| break; | |
| end | |
| end | |
| i -= 1 | |
| end | |
| if cstart && cend | |
| return (cstart..cend) | |
| else | |
| return nil | |
| end | |
| end | |
| def merge( string1, string2, verbose = false ) | |
| string1, string2 = string1.join("\n"), string2.join("\n") | |
| diffs = Diffy::Diff.new(string1, string2).to_s.split("\n")[0..-2] | |
| if diffs.empty? | |
| puts " Already has a template header".yellow if verbose | |
| diffs = string1.split("\n") | |
| else | |
| puts diffs.join("\n").yellow if verbose | |
| diffs.reject!(&->l{/^-.*/===l}) | |
| diffs.map!(&->l{l[1..-1]}) | |
| end | |
| # puts diffs.join("\n") if verbose | |
| return diffs | |
| end | |
| def process_comment( lines, index, direction, message, verbose = false, &generate_header ) | |
| generated_header = generate_header.call().split("\n"); | |
| if( direction == :forward ) | |
| comment = find_closest_comment_block( lines[index..-1] ) | |
| else | |
| comment = find_closest_comment_block_back( lines[0..index-1] ) | |
| end | |
| if comment | |
| cbegin, cend = comment.begin, comment.end | |
| file_header = lines[cbegin..cend] | |
| new_header = merge generated_header, file_header, verbose | |
| if cbegin-1 >= 0 | |
| lines = lines[0..cbegin-1] + new_header + lines[cend+1..-1] | |
| else | |
| lines = new_header + lines[cend+1..-1] | |
| end | |
| index += new_header.size | |
| else | |
| puts message if verbose | |
| puts generated_header if verbose | |
| if index-1 >= 0 | |
| lines = lines[0..index-1] + generated_header + lines[index..-1] | |
| else | |
| lines = generated_header + lines[index..-1] | |
| end | |
| index += generated_header.size | |
| end | |
| return [lines, index] | |
| end | |
| def process_lines( lines, options, isFile, file = nil ) | |
| # Taking care of file header | |
| if isFile | |
| preamble = [lines, 0, :forward] | |
| message = "File header missing. Adding a file header." | |
| generator = -> { file_header(file) } | |
| lines, index = process_comment( *preamble, message, options[:verbose], &generator ) | |
| # saving header sparately so that we will not ever stumble upon it | |
| # while searching backwards | |
| file_header = lines[0..index-1] | |
| lines = lines[index..-1] | |
| index = 0 | |
| else | |
| index = 0 | |
| end | |
| while index < lines.size | |
| line = lines[index] | |
| # Look for classes | |
| # public abstract class CSE11_Line extends Shape { | |
| if matcher = line.match(/^(?<spacing>\s*)((private|public|static|protected|abstract) +)+(?<type>interface|class)\s+(?<name>[_\w]+)/) | |
| spacing, type, name = matcher[:spacing], matcher[:type], matcher[:name] | |
| preamble = [lines, index, :backward] | |
| message = "#{type.capitalize} header missing. Adding a class header." | |
| generator = -> do | |
| puts "#{index + file_header.size}: Found #{type}: #{name}".green if options[:verbose] | |
| method("#{type}_header".to_sym).call( spacing, name ) | |
| end | |
| lines, index = process_comment( *preamble, message, options[:verbose], &generator ) | |
| # Look for methods | |
| elsif matcher = line.match(/^(?<spacing>\s*)((public|private|static|protected|abstract) +)+((?<return>[\w\[\]]+) +)?(?<name>[_\w]+)\(/) | |
| spacing, name, returnt = matcher[:spacing], matcher[:name], matcher[:return] | |
| parameters = nil | |
| parameter_string = "" | |
| # we have parameters on one line | |
| if matcher = line.match(/\((.*)\)/) | |
| parameter_string = matcher[1] | |
| # We have a multiline declaration | |
| else | |
| floating_index = index | |
| full_declaration = [] | |
| while floating_index < lines.size | |
| full_declaration.push lines[floating_index] | |
| break if lines[floating_index] =~ /\)/ | |
| floating_index += 1 | |
| end | |
| parameter_string = full_declaration.join.match(/\((.*)\)/m)[1] | |
| end | |
| parameters = parameter_string.scan(/([\w\[\]><]+) +(\w+),?/) | |
| parameters.map!(&->m{m[1]}) unless parameters.empty? | |
| directivepresent = false | |
| if lines[index-1] =~ /^\s+@\w+/ | |
| preamble = [lines, index-1, :backward] | |
| directivepresent = true | |
| else | |
| preamble = [lines, index, :backward] | |
| end | |
| message = "Method header missing. Adding a method header." | |
| generator = -> do | |
| puts "#{index}: Found method: #{name}".blue if options[:verbose] | |
| method_header( spacing, name, returnt, parameters ) | |
| end | |
| lines, index = process_comment( *preamble, message, options[:verbose], &generator ) | |
| if directivepresent | |
| index+=1 | |
| end | |
| end | |
| index += 1 | |
| end | |
| # joining header with the rest of the file | |
| if isFile | |
| lines = file_header + lines | |
| end | |
| filestring = lines.join("\n") | |
| end | |
| def main( options ) | |
| # puts options | |
| hasStdinInput = $stdin.stat.size > 0 | |
| if hasStdinInput | |
| options[:verbose] = false | |
| options[:all] = false | |
| options[:check] = false | |
| end | |
| if not hasStdinInput | |
| if options[:all] | |
| java_files = Dir.glob('*.java') | |
| else | |
| java_files = ARGV | |
| end | |
| java_files.each do |file| | |
| index = 0 | |
| puts ">> #{file.ljust(20)}#{'-'*20}".magenta if options[:verbose] | |
| lines = File.readlines(file).each { |line| line.rstrip! } | |
| lines = process_lines( lines, options, true, file ) | |
| if not options[:check] | |
| file = File.open(file, 'w') | |
| file.write(lines) | |
| end | |
| puts "<< #{'-'*20}#{'-'*20}".magenta if options[:verbose] | |
| end | |
| else | |
| lines = $stdin.readlines | |
| lines = lines.each { |line| line.rstrip! } | |
| lines = process_lines( lines, options, false ) | |
| puts lines | |
| end | |
| end | |
| options = {} | |
| OptionParser.new do |opts| | |
| opts.banner = "Usage: cs11InitDir.rb [options]" | |
| opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| | |
| options[:verbose] = v | |
| end | |
| opts.on("-a", "--all", "Run on all files in the directory") do |v| | |
| options[:all] = v | |
| end | |
| opts.on("-c", "--check", "Run on all files in the directory") do |c| | |
| options[:check] = c | |
| end | |
| end.parse! | |
| main(options) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment