Skip to content

Instantly share code, notes, and snippets.

@NonLogicalDev
Created October 16, 2015 00:52
Show Gist options
  • Select an option

  • Save NonLogicalDev/39d494f156add49a18a1 to your computer and use it in GitHub Desktop.

Select an option

Save NonLogicalDev/39d494f156add49a18a1 to your computer and use it in GitHub Desktop.
#!/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