summaryrefslogtreecommitdiffstats
path: root/indenters/examples/ruby_formatter.rb
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2024-09-18 14:21:27 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2024-09-18 14:21:27 +0900
commit8d6255b84d9fb6a585d010119f289baff0794df9 (patch)
tree5e2a9bb3ea6f85217a9e01cee5634dca5170ca50 /indenters/examples/ruby_formatter.rb
parent3db895919a4cd36972dd5241f480a68fe0f4eb4d (diff)
downloaduniversal-indent-gui-tqt-8d6255b84d9fb6a585d010119f289baff0794df9.tar.gz
universal-indent-gui-tqt-8d6255b84d9fb6a585d010119f289baff0794df9.zip
Added indenter examples
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'indenters/examples/ruby_formatter.rb')
-rwxr-xr-xindenters/examples/ruby_formatter.rb283
1 files changed, 283 insertions, 0 deletions
diff --git a/indenters/examples/ruby_formatter.rb b/indenters/examples/ruby_formatter.rb
new file mode 100755
index 0000000..acd3150
--- /dev/null
+++ b/indenters/examples/ruby_formatter.rb
@@ -0,0 +1,283 @@
+#!/usr/bin/ruby -w
+#
+# == Synopsis
+#
+# Simple Ruby Formatter
+#
+# Created by: Stephen Becker IV
+# Contributions: Andrew Nutter-Upham
+# Contact: sbeckeriv@gmail.com
+# SVN: http://svn.stephenbeckeriv.com/code/ruby_formatter/
+#
+# Its been done before RadRails did,
+# http://vim.sourceforge.net/tips/tip.php?tip_id = 1368 that guy did it, but I did
+# not look for a ruby formatter until i was done.
+#
+# It is called simple formatting because it is. I have the concept of 3 differnt
+# indent actions In, Out and Both. I have mixed the concept of indenting and
+# outdenting. Out means you have added white space and in means you remove a layer
+# of white space.
+#
+# Basic logic
+# Decrease current depth if
+# ((the it is not a one line if unless statment
+# (need to lookfor more oneline blocks) and it ends with end
+# or if the } count is larger then {)
+# or
+# the first word is in the both list)
+#
+# and
+# depth is larger then zero
+#
+# Increase current depth if
+# It is not a one liner
+# and
+# (the word is in the out list
+# or
+# the word is in the both list
+# or
+# it looks like a start block)
+# and
+# temp_depth is nil (used for = comment blocks)
+#
+#
+# Sure there are some regx's and a crap load of gsubs, but it still simple. Its
+# not like its a pychecker (http://www.metaslash.com/brochure/ipc10.html)
+#
+# == Usage
+#
+# ruby [options] filelist
+#
+# options:
+# -s # will change the indent to a space count of # per level
+# by default we space with 1 tab per level
+# -b # create a backup file
+#
+# examples:
+# ruby simple_formatter.rb -s 3 -b /moo/cluck/cow.rb
+# runs with the indent of 3 spaces,creates a backup file, and formats moo/cluck/cow.rb
+#
+#
+# Tested with random files off of koders.com
+#
+#
+::DEBUG_ME = false
+require 'getoptlong'
+require "fileutils"
+require "pp"
+$escape_strings = {:regex=>"EsCaPedReGex",:string=>"EsCaPeDStRiNg"}
+begin
+ require 'rdoc/usage'
+rescue Exception => e
+ #eat the no load of rdocs?
+end
+opts = GetoptLong.new(
+[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
+[ '--spaces', '-s', GetoptLong::OPTIONAL_ARGUMENT ],
+[ '--debug', '-d', GetoptLong::NO_ARGUMENT ],
+[ '--backup', '-b', GetoptLong::NO_ARGUMENT ]
+)
+space_count = nil
+backup = false
+files = []
+opts.each do | opt, arg|
+ case opt
+ when '--help'
+ begin
+ RDoc::usage
+ rescue Exception =>e
+ puts "If you want to use rdocs you need to install it"
+ exit(-1)
+ end
+ when '--spaces'
+ space_count = arg.to_i
+ when '--backup'
+ backup = true
+ when '--debug'
+ ::DEBUG_ME = true
+ end
+end
+require "profile" if ::DEBUG_ME
+if ARGV.length < 1
+ puts "Missing filelist argument (try --help)"
+ exit 0
+end
+array_loc = ARGV
+#find if the string is a start block
+#return true if it is
+#rules
+# does not include end at the end
+# and ( { out number the } or it includes do
+DO_RX = /\sdo\s*$/
+def start_block?(string)
+ return true if string.gsub(/\|.*\|/, "").match(DO_RX) || (string.scan(/\{/).size > string.scan(/\}/).size)
+ false
+end
+#is this an end block?
+#rules
+#its not a one liner
+#and it ends with end
+#or } out number {
+CHECK_ENDS_RX = /end$|end\s+while/
+def check_ends?(string)
+ #check for one liners end and }
+ #string = just_the_string_please(string)
+ return true if (string.scan(/\{/).size < string.scan(/\}/).size) || string.match(CHECK_ENDS_RX)
+ false
+end
+IN_OUTS_RX = /^(def|class|module|begin|case|if|unless|loop|while|until|for)/
+#look at first work does it start with one of the out works
+def in_outs?(string)
+ string.sub!(/\(.*\)/, "")
+ return true if string.lstrip.match(IN_OUTS_RX) && string.strip.size == $1.strip.size
+ false
+end
+IN_BOTH_RX = /^(elsif|else|when|rescue|ensure)/
+#look at first work does it start with one of the both words?
+def in_both?(string)
+ return true if string.lstrip.match(IN_BOTH_RX) && string.strip.size == $1.strip.size
+ false
+end
+#extra formatting for the line
+#we wrap = with spaces
+#JUST_STRING_PLEASE_RX = /^#.*|\/.*\/|"([^"])*"|'([^'])*'/
+LINE_CLEAN_UP_RX = /[a-zA-Z\]\'\"{\d]+=[a-zA-Z\[\'\"{\d]+/
+def line_clean_up(x)
+ #this formatts strings and regexs remove and add in replacement works
+
+ x.gsub!(/\\\//,$escape_strings[:regex])
+ strings = x.scan(/#.*|["'\/].*?["'\/]/)
+ strings.each { | str |
+ x.sub!(str, $escape_strings[:string])
+ }
+ #lofted code from java formatter #{add in link}
+ # replace "){" with ") {"
+ x.sub!(/\)\s*\{/, ") {")
+ # replace "return(" with "return ("
+ # replace "if(" with "if ("
+ # replace "while(" with "while ("
+ # replace "switch(" with "switch (" ruby does not have a switch
+ # replace "catch(" with "catch ("
+ x.sub!(/\b(return|if|elsif|while|case|catch)\s*\(/, '\1 (')
+ # replace " ;" with ";"
+ # replace " ," with ","
+ x.gsub!(/\s+([\;\,])/, '\1')
+ #replace ",abc" with ", abc"
+ x.gsub!(/\,(\w+)/, ', \1')
+
+ x.gsub!(/(\)|"|\w)\s*([\+\-\*\/\&\|\^\%]|\&\&|\|\||[\>\<]|\>\=|\<\=|\=\=|\!\=|\<\<|\>\>|\>\>\>)\s*(?=(\w | "))/, '\1 \2 ')
+ # a space before and after AssignmentOperator
+ x.gsub!(/(\w)\s*(\+\=|\-\=|\*\=|\/\=|\&\=|\|\=|\^\=|\%\=|\<\<\=|\>\>\=|\>\>\>\=)\s*(?=(\w))/, '\1 \2 ')
+ # do not trim spaces
+ x.gsub!(/(\w)\=\s*(?=(\w|"))/, '\1 = ')
+ x.gsub!(/(\w)\s*\=(?=(\w|"))/, '\1 = ')
+ #becker format
+ #not complete list but alot of the common ones.
+ x.sub!(/(\.each|\.collect[!]*|\.map[!]*|\.delete_if|\.sort[!]*|\.each_[pair|key|value|byte|with_index|line|option]|\.reject[!]*|\.reverse_each|\.detect|\.find[_all]*|\.select|\.module_eval|\.all_waits|loop|proc|lambda|fork|at_exit)\s*\{/, '\1 {')
+ x.sub!(/def\s(\w*)?(\(.*?\))/, 'def \1\2') if x.match(/def\s+?(\w*)?\(.*?\)/)
+ x.sub!(/^for\s+(\w*)?\s+in\s+?(.*)$/, 'for \1 in \2') if x.match(/^for\s+(\w*)?\s*?in\s*?(.*)$/)
+ x.gsub!(/(\w)\=>\s*(?=(\w|"|:))/, '\1 => ')
+ x.gsub!(/(\w)\s*\=>(?=(\w|"|:))/, '\1 => ')
+ x.strip!
+ x.gsub!($escape_strings[:string]) {
+ strings.shift
+ }
+ x.gsub!($escape_strings[:regex], "\\\/")
+ return x
+end
+
+JUST_STRING_PLEASE_RX = /\/.*\/|"([^"])*" | '([^']) * '|#.*/
+def just_the_string_please(org_string)
+ string = String.new(org_string)
+ #remove escaped chars
+ string.gsub!(/\\\/|\\"|\\'/, "")
+ string.gsub!(JUST_STRING_PLEASE_RX, "")
+ string = string.strip
+ string.sub!(/\b(return|if|while|case|catch)\s*\(/, '\1 (')
+ puts "clean string: #{string}" if ::DEBUG_ME
+ string
+end
+ONE_LINER_RX = /(unless|if).*(then).*end|(begin).*(rescue|ensure|else).*end/
+def one_liner?(string)
+ return true if string.match(ONE_LINER_RX)
+ false
+end
+
+array_loc.each {|file_loc|
+ f = File.open(file_loc, "r")
+ text = f.read
+ f.close
+ if File.expand_path(file_loc) == File.expand_path($0)
+ $escape_strings = {:regex=>"EsCaPedReGex#{rand(200)}",:string=>"EsCaPeDStRiNg#{rand(200)}"}
+ end
+ new_text = ""
+ current_depth = 0
+ spaces = " " * space_count if space_count
+ here_doc_ending = nil
+ indenter = spaces || "\t"
+ temp_depth = nil
+ line_count = 1
+
+ text.split("\n").each { |x|
+ #comments
+ #The first idea was to leave them alone.
+ #after running a few test i did not like the way it looked
+ if temp_depth
+ puts "In temp_depth #{x} line ♯ #{line_count} here:#{here_doc_ending}" if ::DEBUG_ME
+ new_text << x << "\n"
+ #block comments, its going to get ugly
+ if !x.lstrip.scan(/^\=end/).empty? || (here_doc_ending && x.strip == here_doc_ending.strip)
+ #swap and set
+ puts "swap and set #{x} line # #{line_count}" if ::DEBUG_ME
+ current_depth = temp_depth
+ temp_depth = nil
+ here_doc_ending = nil
+ end
+ line_count += 1
+ next
+ end
+ #block will always be 0 depth
+ #block comments, its going to get ugly
+ unless x.lstrip.scan(/^\=begin/).empty?
+ #swap and set
+ puts "Looking for begin #{x} #{line_count}" if ::DEBUG_ME
+ temp_depth = current_depth
+ current_depth = 0
+ end
+ #here docs have same type of logic for block comments
+ unless x.lstrip.scan(/<<-/).empty?
+ #swap and set
+ here_doc_ending = x.lstrip.split(/<<-/).last.strip
+ temp_depth = current_depth
+ end
+ #whats the first word?
+ text_node = x.split.first || ""
+ just_string = just_the_string_please(x)
+ in_both = in_both?(text_node)
+ one_liner = one_liner?(just_string)
+ #check if its in end or both and that the current_depth is >0
+ #maybe i should raise if it goes negative ?
+ puts "minus one #{line_count} #{x} statement:#{(check_ends?(just_string) || in_both) && current_depth > 0} check_ends:#{check_ends?(just_string)} in_both:#{in_both} current_depth:#{ current_depth }" if ::DEBUG_ME
+ if (check_ends?(just_string) || in_both) && !one_liner
+ puts "We have a Negative depth count. This was caused around line:#{line_count}\nCheck for if( it should be if (" if current_depth == 0
+ current_depth -= 1 unless current_depth == 0
+ end
+ clean_string = line_clean_up(x)
+ current_indent = clean_string.size>0 ? indenter*current_depth : ""
+ new_text << current_indent << clean_string << "\n"
+ #we want to kick the indent out one
+ # x.match(/(unless|if).*(then).*end/): we use this match one liners for if statements not one-line blocks
+ # in_outs? returns true if the first work is in the out array
+ # in_both? does the same for the both array
+ # start_block looks for to not have an end at the end and {.count > }.count and if the word do is in there
+ # temp_depth is used when we hit the = comments should be nil unless you are in a comment
+ puts "plus one match:#{line_count} #{x} not a one liner:#{!(one_liner)} or statements:#{(in_outs?(text_node) || in_both?(text_node) || start_block?(x))} in_outs#{in_outs?(text_node)} in_both:#{ in_both?(text_node)} start_block:#{ start_block?(x)} temp_depth:#{temp_depth}" if ::DEBUG_ME
+ current_depth += 1 if ((in_outs?(text_node) || start_block?(just_string) || in_both || x.lstrip.slice(/\w*\s=\s(unless|if|case)/)) && !one_liner && !temp_depth)
+ line_count += 1
+ }
+ FileUtils.cp("#{file_loc}","#{file_loc}.bk.#{Time.now.to_s.gsub(/\s|:/,"_")}") if backup
+ f = File.open("#{file_loc}","w+")
+ f.puts new_text
+ f.close
+ puts "Done!"
+} \ No newline at end of file