class Terse::Scan

This Scan class does the meat of the work TODO generate to_s, ==, initialize

Public Class Methods

gen_new_filename(f) click to toggle source

Rules : Keep base-name, change ext. to .rb, write to current working dir

# File lib/terse/scan.rb, line 276
def self.gen_new_filename f
    path = Dir.getwd
    name = File.basename_no_ext f
    ext = ".#{@settings.file_ext}"
    new_f = File.join(path, name) + ext

    if File.file?(new_f)
        puts "New file wll overwrite existing file " + new_f
        raise "Overwrite flag not specified; will not overwrite existing file " + new_f if !@overwrite
    end
    new_f
end
is_file?(f) click to toggle source
# File lib/terse/scan.rb, line 270
def self.is_file? f
    raise "Could not locate file at " + File.absolute_path(f) unless File.file?(f)
end
match_java_lines(lines) click to toggle source

Attempts to match Java lines against a keyword Allows multiple matches per line Need to proceed in a strict left-to-right manner across the line

# File lib/terse/scan.rb, line 155
def self.match_java_lines(lines)
    newlines = []
    needs_top_level_end = false # this needs to be outside lines.each
    lines.each do |line_to_be_matched|

        # If we detect the start of a new class/method/etc. before the last one has been closed
        # then insert an end-char-sequence to close it off
        needs_top_level_start = false
        needs_inner_start = false
        needs_inner_end = false

        needs_line_ending = false
    
        line = line_to_be_matched.chomp
        pv "\nNow checking line --------> " + line
        newline = line # keep the orginal line unless we detect a match
        newline_parts = []
        parts = line.split(" ")
        for i in 0 ... parts.size do
            potential_keyword = parts[i]
            matched = false
            newline_part = ""
            follow_on_match = false
            follow_on_part = ""
            @keywords.each do |k|
                if potential_keyword =~ k.regex          
                    matched = true              
                    pv "\nMatch found against line --> #{line}"
                    pv "Matched -------------------> " + $2

                    if k.needs_inner_start
                        needs_inner_start = true
                    end

                    if k.needs_top_level_start
                        needs_top_level_start = true
                    end

                    if k.needs_inner_end
                        needs_inner_end = true
                    end
                    
                    if k.needs_top_level_end
                        needs_top_level_end = true
                    end
                    
                    # Only add a line ending if no loop start or end is needed
                    if k.needs_line_ending
                        needs_line_ending = true
                    end
                    
                    newline_part = "#{$1}#{k.substitute}"

                    if k.try_follow_on_regex 
                        follow_on_line = parts[i .. -1].join
                        if follow_on_line =~ k.follow_on_regex
                            remainder = follow_on_line[$&.size .. -1]
                            follow_on = (eval k.follow_on_substitute).inspect
                            follow_on_part = "#{follow_on}#{remainder}"
                            follow_on_match = true
                        end
                    end
                    
                    break
                end # end if regex
            end # end keywords.each
            if matched
                newline_parts << newline_part
                matched = false # reset for next part
                newline_part = ""

                # We do not allow keywords to follow follow-on sections
                if follow_on_match
                    newline_parts << follow_on_part                            
                    break # follow_on_part should comprise the rest of the line, so there are no more parts to try to match
                end
            else
                newline_parts << potential_keyword
            end                
        end # end parts.each

        newline_parts << @settings.loop_start if needs_top_level_start
        
        needs_inner_start = true if !needs_line_ending && parts[-1] =~ /\)\s*$/
        newline_parts << @settings.loop_start if needs_inner_start
        unless newline_parts.empty?
            newline = newline_parts.join(" ")
        end
        # Reevaluate needs_line_ending once whole line has been processed
        # No line ending if needs_inner_start || needs_top_level_start # TODO possibly faulty logic? Seems to work reasonably well...
        if needs_inner_start || needs_top_level_start
            needs_line_ending = false
        end
        newline << @settings.line_ending if needs_line_ending
        pv "Line will become ----------> " + newline
        newlines << newline
        if needs_inner_end # add after newline, not before
            newlines << "" # deliberately want an empty line before the loop-ending
            newlines << @settings.loop_ending 
        end
    end # end lines.each
    # Add end lines to end of file if necessary
    # newlines << @settings.loop_ending if needs_inner_end
    if needs_top_level_end # add after newline, not before
        newlines << "" # deliberately want an empty line before the loop-ending
        newlines << @settings.loop_ending 
    end
    newlines
end
match_ruby_lines(lines) click to toggle source

Attempts to match Ruby lines against a keyword Only allows for one match per line TODO refactor this and match_java_lines

# File lib/terse/scan.rb, line 82
def self.match_ruby_lines(lines)
    newlines = []

    # If we detect the start of a new class/method/etc. before the last one has been closed
    # then insert an "end" to close it off
    needs_top_level_end = false
    needs_inner_end = false
    insert_end = false
    
    lines.each do |line_to_be_matched|
        line = line_to_be_matched.chomp
        newline = line # keep the orginal line unless we detect a match
        @keywords.each do |k|
            if newline =~ k.regex                        
                pv "\nMatch found against line --> #{line}"            
                pv "Matched -------------------> " + $2
                if k.is_end
                    if needs_inner_end
                        needs_inner_end = false
                    elsif needs_top_level_end
                        needs_top_level_end = false
                    end
                    insert_end = false
                end
                
                if k.needs_inner_end
                    if needs_inner_end
                        newlines << @settings.loop_ending
                        # keep needs_inner_end true as we need to watch for an end for this new match
                    else
                        needs_inner_end = true # set in readiness for the next match
                    end
                end
                
                if k.needs_top_level_end
                    if needs_inner_end
                        newlines << @settings.loop_ending
                        needs_inner_end = false
                    end
                    if needs_top_level_end
                        newlines << @settings.loop_ending
                        # keep needs_top_level_end true as we need to watch for an end for this new match
                    else
                        needs_top_level_end = true # set in readiness for the next match
                    end
                end
                
                if k.try_follow_on_regex && line =~ k.follow_on_regex
                    # $& is the entire match, not just the bits in ( )
                    remainder = line[$&.size .. -1]
                    follow_on = (eval k.follow_on_substitute).inspect
                    newline = "#{$1}#{k.substitute} #{follow_on}#{remainder}"
                else
                    remainder = line[($1.size + $2.size) .. -1]
                    newline = "#{$1}#{k.substitute}#{remainder}"
                end
                
                pv "Line will become ----------> " + newline
                break
            end # end if regex
        end # end keywords.each
        newlines << newline
    end # end lines.each
    # Add end lines to end of file if necessary
    newlines << @settings.loop_ending if needs_inner_end
    newlines << @settings.loop_ending if needs_top_level_end
    newlines
end
plural_or_not(item) click to toggle source

Returns an ā€œsā€ string if the size of the item (item.size) is > 1; returns ā€œā€ otherwise

# File lib/terse/scan.rb, line 266
def self.plural_or_not item
    item.size == 1 ? "" : "s"
end
pv(s) click to toggle source
# File lib/terse/scan.rb, line 289
def self.pv s
    puts s if @verbose
end
scan_files(settings, argv) click to toggle source

The main method. ARGV from the invokation are passed in to here

# File lib/terse/scan.rb, line 13
def self.scan_files(settings, argv)
    @settings = settings # TODO less bad please!
    if @settings == nil || @settings.class != Terse::Settings
        raise "Settings object was not supplied"
    end

    if @settings.lang == nil || @settings.lang.class != Symbol
        raise "Target language not specified; options are :ruby, :java"
    end

    case @settings.lang 
    when :ruby
        @keywords = Terse::KeywordRuby.generate_keywords
    when :java
        @keywords = Terse::KeywordJava.generate_keywords
    else
        raise "Unrecognised language #{settings.lang.inspect} specified"
    end
    flags = argv.options
    @verbose = flags.include_any?("v", "verbose")
    @overwrite = flags.include_any?("o", "overwrite", "w", "write")
    @no_formatting = flags.include_any?("n", "noformatting")

    files = argv.values

    if files.size < 1
        puts "No files supplied"
    else
        files.each do |f|
            is_file? f
        end
        pv "Will scan #{files.size} file#{plural_or_not files}"
        files.each do |f|
            is_file? f
            filename = File.basename f
            new_filename = gen_new_filename f
            lines = File.readlines f
            pv "File #{filename} has #{lines.size} line#{plural_or_not lines}"
            
            case @settings.lang 
            when :ruby
                newlines = match_ruby_lines(lines)
            when :java
                newlines = match_java_lines(lines)
            else
                raise "Unrecognised language #{settings.lang.inspect} specified"
            end
            
            # Add newlines between the end of a method and the start of the next, if needed
            newlines = Terse::Format.space_lines(newlines, @keywords, @settings) unless @no_formatting
            
            # Apply indentation
            newlines = Terse::Format.indent(newlines, @settings) unless @no_formatting
            
            # Write out the new file
            File.open(new_filename, "w") do |new_f|
                newlines.each do |l|
                    new_f.puts l
                end
            end
            puts "\nWrote file #{new_filename}"
        end # end else
    end # end ARGV.size
    puts "\nTerse #{settings.lang.to_s} expansion finished"
end