This repository is private.
All pages are served over SSL and all pushing and pulling is done over SSH.
No one may fork, clone, or view it unless they are added as a member.
Every repository with this icon (
) is private.
Every repository with this icon (
This repository is public.
Anyone may fork, clone, or view it.
Every repository with this icon (
) is public.
Every repository with this icon (
Vincent Roy (author)
Tue Sep 29 18:28:34 -0700 2009
commit 111d7cb5c0614d6aed943f096dca69de36e08330
tree 540e03faa6c26f932c1eecc0058b4c87922040b1
parent 11ed18f48f5fee362308f3c52c9fd83e1eaeaaef
tree 540e03faa6c26f932c1eecc0058b4c87922040b1
parent 11ed18f48f5fee362308f3c52c9fd83e1eaeaaef
ramaze-book / xmpfilter.rb
| 27be08b6 » | manveru | 2009-04-13 | 1 | #!/usr/bin/env ruby | |
| 2 | # Copyright (c) 2005-2006 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org | ||||
| 3 | # rubikitch <rubikitch@ruby-lang.org> | ||||
| 4 | # Use and distribution subject to the terms of the Ruby license. | ||||
| 5 | |||||
| 6 | class XMPFilter | ||||
| 7 | VERSION = "0.4.0" | ||||
| 8 | |||||
| 9 | MARKER = "!XMP#{Time.new.to_i}_#{Process.pid}_#{rand(1000000)}!" | ||||
| 10 | XMP_RE = Regexp.new("^" + Regexp.escape(MARKER) + '\[([0-9]+)\] (=>|~>|==>) (.*)') | ||||
| 11 | VAR = "_xmp_#{Time.new.to_i}_#{Process.pid}_#{rand(1000000)}" | ||||
| 12 | WARNING_RE = /.*:([0-9]+): warning: (.*)/ | ||||
| 13 | |||||
| 14 | RuntimeData = Struct.new(:results, :exceptions, :bindings) | ||||
| 15 | |||||
| 16 | INITIALIZE_OPTS = {:interpreter => "ruby", :options => [], :libs => [], | ||||
| 17 | :include_paths => [], :warnings => true, | ||||
| 18 | :use_parentheses => true} | ||||
| 19 | def initialize(opts = {}) | ||||
| 20 | options = INITIALIZE_OPTS.merge opts | ||||
| 21 | @interpreter = options[:interpreter] | ||||
| 22 | @options = options[:options] | ||||
| 23 | @libs = options[:libs] | ||||
| 24 | @evals = options[:evals] || [] | ||||
| 25 | @include_paths = options[:include_paths] | ||||
| 26 | @output_stdout = options[:output_stdout] | ||||
| 27 | @dump = options[:dump] | ||||
| 28 | @warnings = options[:warnings] | ||||
| 29 | @parentheses = options[:use_parentheses] | ||||
| 30 | |||||
| 31 | @postfix = "" | ||||
| 32 | end | ||||
| 33 | |||||
| 34 | def add_markers(code, min_codeline_size = 50) | ||||
| 35 | maxlen = code.map{|x| x.size}.max | ||||
| 36 | maxlen = [min_codeline_size, maxlen + 2].max | ||||
| 37 | ret = "" | ||||
| 38 | code.each do |l| | ||||
| 39 | l = l.chomp.gsub(/ # (=>|!>).*/, "").gsub(/\s*$/, "") | ||||
| 40 | ret << (l + " " * (maxlen - l.size) + " # =>\n") | ||||
| 41 | end | ||||
| 42 | ret | ||||
| 43 | end | ||||
| 44 | |||||
| 45 | def annotate(code) | ||||
| 46 | idx = 0 | ||||
| 47 | newcode = code.gsub(/^(.*) # =>.*/){|l| prepare_line($1, idx += 1) } | ||||
| 48 | if @dump | ||||
| 49 | File.open(@dump, "w"){|f| f.puts newcode} | ||||
| 50 | end | ||||
| 51 | stdout, stderr = execute(newcode) | ||||
| 52 | output = stderr.readlines | ||||
| 53 | runtime_data = extract_data(output) | ||||
| 54 | idx = 0 | ||||
| 55 | annotated = code.gsub(/^(.*) # =>.*/) do |l| | ||||
| 56 | expr = $1 | ||||
| 57 | if /^\s*#/ =~ l | ||||
| 58 | l | ||||
| 59 | else | ||||
| 60 | annotated_line(l, expr, runtime_data, idx += 1) | ||||
| 61 | end | ||||
| 62 | end.gsub(/ # !>.*/, '').gsub(/# (>>|~>)[^\n]*\n/m, ""); | ||||
| 63 | ret = final_decoration(annotated, output) | ||||
| 64 | if @output_stdout and (s = stdout.read) != "" | ||||
| 65 | ret << s.inject(""){|s,line| s + "# >> #{line}".chomp + "\n" } | ||||
| 66 | end | ||||
| 67 | ret | ||||
| 68 | end | ||||
| 69 | |||||
| 70 | def annotated_line(line, expression, runtime_data, idx) | ||||
| 71 | "#{expression} # => " + (runtime_data.results[idx].map{|x| x[1]} || []).join(", ") | ||||
| 72 | end | ||||
| 73 | |||||
| 74 | def prepare_line_annotation(expr, idx) | ||||
| 75 | v = "#{VAR}" | ||||
| 76 | blocal = "__#{VAR}" | ||||
| 77 | blocal2 = "___#{VAR}" | ||||
| 78 | # rubikitch: oneline-ized | ||||
| 79 | # <<EOF.chomp | ||||
| 80 | # ((#{v} = (#{expr}); $stderr.puts("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " " + #{v}.inspect) || begin; $stderr.puts local_variables; local_variables.each{|#{blocal}| #{blocal2} = eval(#{blocal}); if #{v} == #{blocal2} && #{blocal} != %#{expr}.strip; $stderr.puts("#{MARKER}[#{idx}] ==> " + #{blocal}); elsif [#{blocal2}] == #{v}; $stderr.puts("#{MARKER}[#{idx}] ==> [" + #{blocal} + "]") end }; nil rescue Exception; nil end || #{v})) | ||||
| 81 | # EOF | ||||
| 82 | oneline_ize(<<-EOF).chomp | ||||
| 83 | #{v} = (#{expr}) | ||||
| 84 | $stderr.puts("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " " + #{v}.inspect) || begin | ||||
| 85 | $stderr.puts local_variables | ||||
| 86 | local_variables.each{|#{blocal}| | ||||
| 87 | #{blocal2} = eval(#{blocal}) | ||||
| 88 | if #{v} == #{blocal2} && #{blocal} != %#{expr}.strip | ||||
| 89 | $stderr.puts("#{MARKER}[#{idx}] ==> " + #{blocal}) | ||||
| 90 | elsif [#{blocal2}] == #{v} | ||||
| 91 | $stderr.puts("#{MARKER}[#{idx}] ==> [" + #{blocal} + "]") | ||||
| 92 | end | ||||
| 93 | } | ||||
| 94 | nil | ||||
| 95 | rescue Exception | ||||
| 96 | nil | ||||
| 97 | end || #{v} | ||||
| 98 | EOF | ||||
| 99 | |||||
| 100 | end | ||||
| 101 | alias_method :prepare_line, :prepare_line_annotation | ||||
| 102 | |||||
| 103 | def execute_tmpfile(code) | ||||
| 104 | stdin, stdout, stderr = (1..3).map do |i| | ||||
| 105 | fname = "xmpfilter.tmpfile_#{Process.pid}-#{i}.rb" | ||||
| 106 | at_exit { File.unlink fname } | ||||
| 107 | File.open(fname, "w+") | ||||
| 108 | end | ||||
| 109 | stdin.puts code | ||||
| 110 | stdin.close | ||||
| 111 | exe_line = <<-EOF.map{|l| l.strip}.join(";") | ||||
| 112 | $stdout.reopen('#{stdout.path}', 'w') | ||||
| 113 | $stderr.reopen('#{stderr.path}', 'w') | ||||
| 114 | $0.replace '#{stdin.path}' | ||||
| 115 | ARGV.replace(#{@options.inspect}) | ||||
| 116 | load #{stdin.path.inspect} | ||||
| 117 | #{@evals.join(";")} | ||||
| 118 | EOF | ||||
| 119 | system(*(interpreter_command << "-e" << exe_line)) | ||||
| 120 | [stdout, stderr] | ||||
| 121 | end | ||||
| 122 | |||||
| 123 | def execute_popen(code) | ||||
| 124 | require 'open3' | ||||
| 125 | stdin, stdout, stderr = Open3::popen3(*interpreter_command) | ||||
| 126 | stdin.puts code | ||||
| 127 | @evals.each{|x| stdin.puts x } unless @evals.empty? | ||||
| 128 | stdin.close | ||||
| 129 | [stdout, stderr] | ||||
| 130 | end | ||||
| 131 | |||||
| 132 | if /win|mingw/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM | ||||
| 133 | alias_method :execute, :execute_tmpfile | ||||
| 134 | else | ||||
| 135 | alias_method :execute, :execute_popen | ||||
| 136 | end | ||||
| 137 | |||||
| 138 | def interpreter_command | ||||
| 139 | r = [@interpreter, "-w"] | ||||
| 140 | r << "-d" if $DEBUG | ||||
| 141 | r << "-I#{@include_paths.join(":")}" unless @include_paths.empty? | ||||
| 142 | @libs.each{|x| r << "-r#{x}" } unless @libs.empty? | ||||
| 143 | r | ||||
| 144 | end | ||||
| 145 | |||||
| 146 | def extract_data(output) | ||||
| 147 | results = Hash.new{|h,k| h[k] = []} | ||||
| 148 | exceptions = Hash.new{|h,k| h[k] = []} | ||||
| 149 | bindings = Hash.new{|h,k| h[k] = []} | ||||
| 150 | output.grep(XMP_RE).each do |line| | ||||
| 151 | result_id, op, result = XMP_RE.match(line).captures | ||||
| 152 | case op | ||||
| 153 | when "=>" | ||||
| 154 | klass, value = /(\S+)\s+(.*)/.match(result).captures | ||||
| 155 | results[result_id.to_i] << [klass, value] | ||||
| 156 | when "~>" | ||||
| 157 | exceptions[result_id.to_i] << result | ||||
| 158 | when "==>" | ||||
| 159 | bindings[result_id.to_i] << result unless result.index(VAR) | ||||
| 160 | end | ||||
| 161 | end | ||||
| 162 | RuntimeData.new(results, exceptions, bindings) | ||||
| 163 | end | ||||
| 164 | |||||
| 165 | def final_decoration(code, output) | ||||
| 166 | warnings = {} | ||||
| 07ab3088 » | manveru | 2009-06-16 | 167 | output.join.lines.grep(WARNING_RE).map do |x| | |
| 27be08b6 » | manveru | 2009-04-13 | 168 | md = WARNING_RE.match(x) | |
| 169 | warnings[md[1].to_i] = md[2] | ||||
| 170 | end | ||||
| 171 | idx = 0 | ||||
| 07ab3088 » | manveru | 2009-06-16 | 172 | ret = code.lines.map do |line| | |
| 27be08b6 » | manveru | 2009-04-13 | 173 | w = warnings[idx+=1] | |
| 174 | if @warnings | ||||
| 175 | w ? (line.chomp + " # !> #{w}") : line | ||||
| 176 | else | ||||
| 177 | line | ||||
| 178 | end | ||||
| 179 | end | ||||
| 180 | output = output.reject{|x| /^-:[0-9]+: warning/.match(x)} | ||||
| 181 | if exception = /^-:[0-9]+:.*/m.match(output.join) | ||||
| 182 | ret << exception[0].map{|line| "# ~> " + line } | ||||
| 183 | end | ||||
| 184 | ret | ||||
| 185 | end | ||||
| 186 | |||||
| 187 | def oneline_ize(code) | ||||
| 188 | "((" + code.gsub(/\r?\n|\r/, ';') + "))#{@postfix}\n" | ||||
| 189 | end | ||||
| 190 | |||||
| 191 | def debugprint(*args) | ||||
| 192 | $stderr.puts(*args) if $DEBUG | ||||
| 193 | end | ||||
| 194 | end # clas XMPFilter | ||||
| 195 | |||||
| 196 | class XMPTestUnitFilter < XMPFilter | ||||
| 197 | def initialize(opts = {}) | ||||
| 198 | super | ||||
| 199 | @output_stdout = false | ||||
| 200 | end | ||||
| 201 | |||||
| 202 | private | ||||
| 203 | def annotated_line(line, expression, runtime_data, idx) | ||||
| 204 | indent = /^\s*/.match(line)[0] | ||||
| 205 | assertions(expression.strip, runtime_data, idx).map{|x| indent + x}.join("\n") | ||||
| 206 | end | ||||
| 207 | |||||
| 208 | def prepare_line(expr, idx) | ||||
| 209 | basic_eval = prepare_line_annotation(expr, idx) | ||||
| 210 | %|begin; #{basic_eval}; rescue Exception; $stderr.puts("#{MARKER}[#{idx}] ~> " + $!.class.to_s); end| | ||||
| 211 | end | ||||
| 212 | |||||
| 213 | def assertions(expression, runtime_data, index) | ||||
| 214 | exceptions = runtime_data.exceptions | ||||
| 215 | ret = [] | ||||
| 216 | |||||
| 217 | unless (vars = runtime_data.bindings[index]).empty? | ||||
| 218 | vars.each{|var| ret << equal_assertion(var, expression) } | ||||
| 219 | end | ||||
| 220 | if !(wanted = runtime_data.results[index]).empty? || !exceptions[index] | ||||
| 221 | case (wanted[0][1] rescue 1) | ||||
| 222 | when "nil" | ||||
| 223 | ret.concat nil_assertion(expression) | ||||
| 224 | else | ||||
| 225 | case wanted.size | ||||
| 226 | when 1 | ||||
| 227 | ret.concat value_assertions(wanted[0], expression) | ||||
| 228 | else | ||||
| 229 | # discard values from multiple runs | ||||
| 230 | ret.concat(["#xmpfilter: WARNING!! extra values ignored"] + | ||||
| 231 | value_assertions(wanted[0], expression)) | ||||
| 232 | end | ||||
| 233 | end | ||||
| 234 | else | ||||
| 235 | ret.concat raise_assertion(expression, exceptions, index) | ||||
| 236 | end | ||||
| 237 | |||||
| 238 | ret | ||||
| 239 | end | ||||
| 240 | |||||
| 241 | def nil_assertion(expression) | ||||
| 242 | if @parentheses | ||||
| 243 | ["assert_nil(#{expression})"] | ||||
| 244 | else | ||||
| 245 | ["assert_nil #{expression}"] | ||||
| 246 | end | ||||
| 247 | end | ||||
| 248 | |||||
| 249 | def raise_assertion(expression, exceptions, index) | ||||
| 250 | ["assert_raise(#{exceptions[index][0]}){#{expression}}"] | ||||
| 251 | end | ||||
| 252 | |||||
| 253 | OTHER = Class.new | ||||
| 254 | def value_assertions(klass_value_txt_pair, expression) | ||||
| 255 | klass_txt, value_txt = klass_value_txt_pair | ||||
| 256 | value = eval(value_txt) || OTHER.new | ||||
| 257 | # special cases | ||||
| 258 | value = nil if value_txt.strip == "nil" | ||||
| 259 | value = false if value_txt.strip == "false" | ||||
| 260 | case value | ||||
| 261 | when Float | ||||
| 262 | @parentheses ? ["assert_in_delta(#{value.inspect}, #{expression}, 0.0001)"] : | ||||
| 263 | ["assert_in_delta #{value.inspect}, #{expression}, 0.0001"] | ||||
| 264 | when Numeric, String, Hash, Array, Regexp, TrueClass, FalseClass, Symbol, NilClass | ||||
| 265 | @parentheses ? ["assert_equal(#{value_txt}, #{expression})"] : | ||||
| 266 | ["assert_equal #{value_txt}, #{expression}"] | ||||
| 267 | else | ||||
| 268 | @parentheses ? [ "assert_kind_of(#{klass_txt}, #{expression})", | ||||
| 269 | "assert_equal(#{value_txt.inspect}, #{expression}.inspect)" ] : | ||||
| 270 | [ "assert_kind_of #{klass_txt}, #{expression} ", | ||||
| 271 | "assert_equal #{value_txt.inspect}, #{expression}.inspect" ] | ||||
| 272 | end | ||||
| 273 | rescue Exception | ||||
| 274 | return @parentheses ? [ "assert_kind_of(#{klass_txt}, #{expression})", | ||||
| 275 | "assert_equal(#{value_txt.inspect}, #{expression}.inspect)" ] : | ||||
| 276 | [ "assert_kind_of #{klass_txt}, #{expression}", | ||||
| 277 | "assert_equal #{value_txt.inspect}, #{expression}.inspect" ] | ||||
| 278 | end | ||||
| 279 | |||||
| 280 | def equal_assertion(expected, actual) | ||||
| 281 | @parentheses ? "assert_equal(#{expected}, #{actual})" : "assert_equal #{expected}, #{actual}" | ||||
| 282 | end | ||||
| 283 | end | ||||
| 284 | |||||
| 285 | class XMPRSpecFilter < XMPTestUnitFilter | ||||
| 286 | private | ||||
| 287 | def execute(code) | ||||
| 288 | codefile = "xmpfilter.rspec_tmpfile_#{Process.pid}.rb" | ||||
| 289 | File.open(codefile, "w"){|f| f.puts code} | ||||
| 290 | path = File.expand_path(codefile) | ||||
| 291 | at_exit { File.unlink path } | ||||
| 292 | stdout, stderr = (1..2).map do |i| | ||||
| 293 | fname = "xmpfilter.rspec_tmpfile_#{Process.pid}-#{i}.rb" | ||||
| 294 | fullname = File.expand_path(fname) | ||||
| 295 | at_exit { File.unlink fullname } | ||||
| 296 | File.open(fname, "w+") | ||||
| 297 | end | ||||
| 298 | args = *(interpreter_command << %["#{codefile}"] << "2>" << | ||||
| 299 | %["#{stderr.path}"] << ">" << %["#{stdout.path}"]) | ||||
| 300 | system(args.join(" ")) | ||||
| 301 | [stdout, stderr] | ||||
| 302 | end | ||||
| 303 | |||||
| 304 | def interpreter_command | ||||
| 305 | [@interpreter] + @libs.map{|x| "-r#{x}"} | ||||
| 306 | end | ||||
| 307 | |||||
| 308 | def nil_assertion(expression) | ||||
| 309 | @parentheses ? ["(#{expression}).should_be_nil"] : ["#{expression}.should_be_nil"] | ||||
| 310 | end | ||||
| 311 | |||||
| 312 | def raise_assertion(expression, exceptions, index) | ||||
| 313 | ["lambda{#{expression}}.should_raise #{exceptions[index][0]}"] | ||||
| 314 | end | ||||
| 315 | |||||
| 316 | def value_assertions(klass_value_txt_pair, expression) | ||||
| 317 | klass_txt, value_txt = klass_value_txt_pair | ||||
| 318 | value = eval(value_txt) || OTHER.new | ||||
| 319 | # special cases | ||||
| 320 | value = nil if value_txt.strip == "nil" | ||||
| 321 | value = false if value_txt.strip == "false" | ||||
| 322 | case value | ||||
| 323 | when Float | ||||
| 324 | @parentheses ? | ||||
| 325 | ["(#{expression}).should_be_close #{value.inspect}, 0.0001"] : | ||||
| 326 | ["#{expression}.should_be_close #{value.inspect}, 0.0001"] | ||||
| 327 | when Numeric, String, Hash, Array, Regexp, TrueClass, FalseClass, Symbol, NilClass | ||||
| 328 | @parentheses ? | ||||
| 329 | ["(#{expression}).should_equal #{value_txt}"] : | ||||
| 330 | ["#{expression}.should_equal #{value_txt}"] | ||||
| 331 | else | ||||
| 332 | @parentheses ? | ||||
| 333 | [ "(#{expression}).should_be_a_kind_of #{klass_txt}", | ||||
| 334 | "(#{expression}.inspect).should_equal #{value_txt.inspect}" ] : | ||||
| 335 | [ "#{expression}.should_be_a_kind_of #{klass_txt}", | ||||
| 336 | "#{expression}.inspect.should_equal #{value_txt.inspect}" ] | ||||
| 337 | end | ||||
| 338 | rescue | ||||
| 339 | return @parentheses ? | ||||
| 340 | [ "(#{expression}).should_be_a_kind_of #{klass_txt}", | ||||
| 341 | "(#{expression}.inspect).should_equal #{value_txt.inspect}" ] : | ||||
| 342 | [ "#{expression}.should_be_a_kind_of #{klass_txt}", | ||||
| 343 | "#{expression}.inspect.should_equal #{value_txt.inspect}" ] | ||||
| 344 | end | ||||
| 345 | |||||
| 346 | def equal_assertion(expected, actual) | ||||
| 347 | @parentheses ? | ||||
| 348 | "(#{actual}).should_equal #{expected}" : "#{actual}.should_equal #{expected}" | ||||
| 349 | end | ||||
| 350 | end | ||||
| 351 | |||||
| 352 | require 'enumerator' | ||||
| 353 | # Common routines for XMPCompletionFilter/XMPDocFilter | ||||
| 354 | module ProcessParticularLine | ||||
| 355 | def fill_literal!(expr) | ||||
| 356 | [ "\"", "'", "`" ].each do |q| | ||||
| 357 | expr.gsub!(/#{q}(.+)#{q}/){ '"' + "x"*$1.length + '"' } | ||||
| 358 | end | ||||
| 359 | expr.gsub!(/(%([wWqQxrs])?(\W))(.+?)\3/){ | ||||
| 360 | percent = $2 == 'x' ? '%'+$3 : $1 # avoid executing shell command | ||||
| 361 | percent + "x"*$4.length + $3 | ||||
| 362 | } | ||||
| 363 | [ %w[( )], %w[{ }], %w![ ]!, %w[< >] ].each do |b,e| | ||||
| 364 | rb, re = [b,e].map{ |x| Regexp.quote(x)} | ||||
| 365 | expr.gsub!(/(%([wWqQxrs])?(#{rb}))(.+)#{re}/){ | ||||
| 366 | percent = $2 == 'x' ? '%'+$3 : $1 # avoid executing shell command | ||||
| 367 | percent + "x"*$4.length + e | ||||
| 368 | } | ||||
| 369 | end | ||||
| 370 | end | ||||
| 371 | |||||
| 372 | module ExpressionExtension | ||||
| 373 | attr_accessor :eval_string | ||||
| 374 | attr_accessor :meth | ||||
| 375 | end | ||||
| 376 | OPERATOR_CHARS = '\|^&<>=~\+\-\*\/%\[' | ||||
| 377 | def set_expr_and_postfix!(expr, column, ®exp) | ||||
| 378 | expr.extend ExpressionExtension | ||||
| 379 | |||||
| 380 | @postfix = "" | ||||
| 381 | expr_orig = expr.clone | ||||
| 382 | column ||= expr.length | ||||
| 383 | last_char = expr[column-1] | ||||
| 384 | expr.replace expr[ regexp[column] ] | ||||
| 385 | debugprint "expr_orig=#{expr_orig}", "expr(sliced)=#{expr}" | ||||
| 386 | right_stripped = Regexp.last_match.post_match | ||||
| 387 | _handle_do_end right_stripped | ||||
| 388 | aref_or_aset = aref_or_aset? right_stripped, last_char | ||||
| 389 | debugprint "aref_or_aset=#{aref_or_aset.inspect}" | ||||
| 390 | set_last_word! expr, aref_or_aset | ||||
| 391 | fill_literal! expr_orig | ||||
| 392 | _handle_brackets expr_orig, expr | ||||
| 393 | expr << aref_or_aset if aref_or_aset | ||||
| 394 | _handle_keywords expr_orig, column | ||||
| 395 | debugprint "expr(processed)=#{expr}" | ||||
| 396 | expr | ||||
| 397 | end | ||||
| 398 | |||||
| 399 | def _handle_do_end(right_stripped) | ||||
| 400 | right_stripped << "\n" | ||||
| 401 | n_do = right_stripped.scan(/[\s\)]do\s/).length | ||||
| 402 | n_end = right_stripped.scan(/\bend\b/).length | ||||
| 403 | @postfix = ";begin" * (n_do - n_end) | ||||
| 404 | end | ||||
| 405 | |||||
| 406 | def _handle_brackets(expr_orig, expr) | ||||
| 407 | [ %w[{ }], %w[( )], %w![ ]! ].each do |left, right| | ||||
| 408 | n_left = expr_orig.count(left) - expr.count(left) | ||||
| 409 | n_right = expr_orig.count(right) - expr.count(right) | ||||
| 410 | n = n_left - n_right | ||||
| 411 | @postfix << ";#{left}" * n if n >= 0 | ||||
| 412 | end | ||||
| 413 | end | ||||
| 414 | |||||
| 415 | def _handle_keywords(expr_orig, column) | ||||
| 416 | %w[if unless while until for].each do |keyw| | ||||
| 417 | pos = expr_orig.index(/\b#{keyw}\b/) | ||||
| 418 | @postfix << ";begin" if pos and pos < column # if * xxx | ||||
| 419 | |||||
| 420 | pos = expr_orig.index(/;\s*#{keyw}\b/) | ||||
| 421 | @postfix << ";begin" if pos and column < pos # * ; if xxx | ||||
| 422 | end | ||||
| 423 | end | ||||
| 424 | |||||
| 425 | def aref_or_aset?(right_stripped, last_char) | ||||
| 426 | if last_char == ?[ | ||||
| 427 | case right_stripped | ||||
| 07ab3088 » | manveru | 2009-06-16 | 428 | when /\]\s*=/; "[]=" | |
| 429 | when /\]/; "[]" | ||||
| 27be08b6 » | manveru | 2009-04-13 | 430 | end | |
| 431 | end | ||||
| 432 | end | ||||
| 433 | |||||
| 434 | def set_last_word!(expr, aref_or_aset=nil) | ||||
| 435 | debugprint "expr(before set_last_word)=#{expr}" | ||||
| 436 | if aref_or_aset | ||||
| 437 | opchars = "" | ||||
| 438 | else | ||||
| 439 | opchars = expr.slice!(/\s*[#{OPERATOR_CHARS}]+$/) | ||||
| 440 | debugprint "expr(strip opchars)=#{expr}" | ||||
| 441 | end | ||||
| 442 | |||||
| 443 | expr.replace(if expr =~ /[\"\'\`]$/ # String operations | ||||
| 444 | "''" | ||||
| 445 | else | ||||
| 446 | fill_literal! expr | ||||
| 447 | phrase = current_phrase(expr) | ||||
| 448 | if aref_or_aset | ||||
| 449 | expr.eval_string = expr[0..-2] | ||||
| 450 | expr.meth = aref_or_aset | ||||
| 451 | elsif phrase.match( /^(.+)\.(.*)$/ ) | ||||
| 452 | expr.eval_string, expr.meth = $1, $2 | ||||
| 453 | elsif opchars != '' | ||||
| 454 | expr | ||||
| 455 | end | ||||
| 456 | debugprint "expr.eval_string=#{expr.eval_string}", "expr.meth=#{expr.meth}" | ||||
| 457 | phrase | ||||
| 458 | end << (opchars || '')) # ` font-lock hack | ||||
| 459 | debugprint "expr(after set_last_word)=#{expr}" | ||||
| 460 | end | ||||
| 461 | |||||
| 462 | def current_phrase(expr) | ||||
| 463 | paren_level = 0 | ||||
| 464 | start = 0 | ||||
| 465 | (expr.length-1).downto(0) do |i| | ||||
| 466 | c = expr[i,1] | ||||
| 467 | if c =~ /[\)\}\]]/ | ||||
| 468 | paren_level += 1 | ||||
| 469 | next | ||||
| 470 | end | ||||
| 471 | if paren_level > 0 | ||||
| 472 | next if c =~ /[, ]/ | ||||
| 473 | else | ||||
| 474 | break (start = i+1) if c =~ /[ ,\(\{\[]/ | ||||
| 475 | end | ||||
| 476 | if c =~ /[\(\{\[]/ | ||||
| 477 | paren_level -= 1 | ||||
| 478 | break (start = i+1) if paren_level < 0 | ||||
| 479 | end | ||||
| 480 | end | ||||
| 481 | expr[start..-1] | ||||
| 482 | end | ||||
| 483 | |||||
| 484 | class RuntimeDataError < RuntimeError; end | ||||
| 485 | def runtime_data(code, lineno, column=nil) | ||||
| 486 | newcode = code.to_a.enum_with_index.map{|line, i| | ||||
| 487 | i+1==lineno ? prepare_line(line.chomp, column) : line | ||||
| 488 | }.join | ||||
| 489 | debugprint "newcode", newcode, "-"*80 | ||||
| 490 | stdout, stderr = execute(newcode) | ||||
| 491 | output = stderr.readlines | ||||
| 492 | debugprint "stdout", output, "-"*80 | ||||
| 493 | runtime_data = extract_data(output) | ||||
| 494 | begin | ||||
| 495 | runtime_data.results[1][0][1..-1].to_s | ||||
| 496 | rescue | ||||
| 497 | raise RuntimeDataError, runtime_data.inspect | ||||
| 498 | end | ||||
| 499 | |||||
| 500 | end | ||||
| 501 | |||||
| 502 | end | ||||
| 503 | |||||
| 504 | # Nearly 100% accurate completion for any editors!! | ||||
| 505 | # by rubikitch <rubikitch@ruby-lang.org> | ||||
| 506 | class XMPCompletionFilter < XMPFilter | ||||
| 507 | include ProcessParticularLine | ||||
| 508 | |||||
| 509 | # String completion begins with this. | ||||
| 510 | attr :prefix | ||||
| 511 | |||||
| 512 | def prepare_line(expr, column) | ||||
| 513 | set_expr_and_postfix!(expr, column){|c| /^.{#{c}}/ } | ||||
| 514 | @prefix = expr | ||||
| 515 | case expr | ||||
| 516 | when /^\$\w+$/ # global variable | ||||
| 517 | __prepare_line 'global_variables' | ||||
| 518 | when /^@@\w+$/ # class variable | ||||
| 519 | __prepare_line 'Module === self ? class_variables : self.class.class_variables' | ||||
| 520 | when /^@\w+$/ # instance variable | ||||
| 521 | __prepare_line 'instance_variables' | ||||
| 522 | when /^([A-Z].*)::(.*)$/ # nested constants / class methods | ||||
| 523 | @prefix = $2 | ||||
| 524 | __prepare_line "#$1.constants | #$1.methods(true)" | ||||
| 525 | when /^[A-Z]\w*$/ # normal constants | ||||
| 526 | __prepare_line 'Module.constants' | ||||
| 527 | when /^::(.+)::(.*)$/ # toplevel nested constants | ||||
| 528 | @prefix = $2 | ||||
| 529 | __prepare_line "::#$1.constants | ::#$1.methods" | ||||
| 530 | when /^::(.*)/ # toplevel constant | ||||
| 531 | @prefix = $1 | ||||
| 532 | __prepare_line 'Object.constants' | ||||
| 533 | when /^(:[^:.]*)$/ # symbol | ||||
| 534 | __prepare_line 'Symbol.all_symbols.map{|s| ":" + s.id2name}' | ||||
| 535 | when /\.([^.]*)$/ # method call | ||||
| 536 | @prefix = $1 | ||||
| 537 | __prepare_line "(#{Regexp.last_match.pre_match}).methods(true)" | ||||
| 538 | else # bare words | ||||
| 539 | __prepare_line "methods | private_methods | local_variables | self.class.constants" | ||||
| 540 | end | ||||
| 541 | end | ||||
| 542 | |||||
| 543 | def __prepare_line(all_completion_expr) | ||||
| 544 | v = "#{VAR}" | ||||
| 545 | idx = 1 | ||||
| 546 | oneline_ize(<<EOC) | ||||
| 547 | #{v} = (#{all_completion_expr}).grep(/^#{Regexp.quote(@prefix)}/) | ||||
| 548 | $stderr.puts("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " " + #{v}.join(" ")) || #{v} | ||||
| 549 | exit | ||||
| 550 | EOC | ||||
| 551 | end | ||||
| 552 | |||||
| 553 | # Array of completion candidates. | ||||
| 554 | def candidates(code, lineno, column=nil) | ||||
| 555 | methods = runtime_data(code, lineno, column) rescue "" | ||||
| 556 | methods.split | ||||
| 557 | end | ||||
| 558 | |||||
| 559 | # Completion code for editors. | ||||
| 560 | def completion_code(code, lineno, column=nil) | ||||
| 561 | candidates(code, lineno, column).join("\n") | ||||
| 562 | end | ||||
| 563 | end | ||||
| 564 | |||||
| 565 | class XMPCompletionEmacsFilter < XMPCompletionFilter | ||||
| 566 | def completion_code(code, lineno, column=nil) | ||||
| 567 | elisp = "(progn\n" | ||||
| 568 | elisp << "(setq xmpfilter-method-completion-table '(" | ||||
| 569 | begin | ||||
| 570 | candidates(code, lineno, column).each do |meth| | ||||
| 571 | elisp << format('("%s") ', meth) | ||||
| 572 | end | ||||
| 573 | rescue => err | ||||
| 574 | return %Q[(error "#{err.message}")] | ||||
| 575 | end | ||||
| 576 | elisp << "))\n" | ||||
| 577 | elisp << %Q[(setq pattern "#{prefix}")\n] | ||||
| 578 | elisp << %Q[(try-completion pattern xmpfilter-method-completion-table nil)\n] | ||||
| 579 | elisp << ")" # /progn | ||||
| 580 | end | ||||
| 581 | end | ||||
| 582 | |||||
| 583 | # FIXME rubikitch: I do not use vim, so I cannot implement XMPCompletionVimFilter class. | ||||
| 584 | class XMPCompletionVimFilter < XMPCompletionFilter | ||||
| 585 | def completion_code(code, lineno, column=nil) | ||||
| 586 | raise NotImplementedError | ||||
| 587 | end | ||||
| 588 | end | ||||
| 589 | |||||
| 590 | |||||
| 591 | # Call Ri for any editors!! | ||||
| 592 | # by rubikitch <rubikitch@ruby-lang.org> | ||||
| 593 | class XMPDocFilter < XMPFilter | ||||
| 594 | include ProcessParticularLine | ||||
| 595 | |||||
| 596 | def initialize(opts = {}) | ||||
| 597 | super | ||||
| 598 | @filename = opts[:filename] | ||||
| 599 | extend UseMethodAnalyzer if opts[:use_method_analyzer] | ||||
| 600 | end | ||||
| 601 | |||||
| 602 | def prepare_line(expr, column) | ||||
| 603 | set_expr_and_postfix!(expr, column){|c| | ||||
| 604 | withop_re = /^.{#{c-1}}[#{OPERATOR_CHARS}]+/ | ||||
| 605 | if expr =~ withop_re | ||||
| 606 | withop_re | ||||
| 607 | else | ||||
| 608 | /^.{#{c}}[\w#{OPERATOR_CHARS}]*/ | ||||
| 609 | end | ||||
| 610 | } | ||||
| 611 | recv = expr | ||||
| 612 | |||||
| 613 | # When expr already knows receiver and method, | ||||
| 614 | return(__prepare_line :recv => expr.eval_string, :meth => expr.meth) if expr.eval_string | ||||
| 615 | |||||
| 616 | case expr | ||||
| 617 | when /^(?:::)?([A-Z].*)(?:::|\.)(.*)$/ # nested constants / class methods | ||||
| 618 | __prepare_line :klass => $1, :meth_or_constant => $2 | ||||
| 619 | when /^(?:::)?[A-Z]/ # normal constants | ||||
| 620 | __prepare_line :klass => expr | ||||
| 621 | when /\.([^.]*)$/ # method call | ||||
| 622 | __prepare_line :recv => Regexp.last_match.pre_match, :meth => $1 | ||||
| 623 | when /^(.+)(\[\]=?)$/ # [], []= | ||||
| 624 | __prepare_line :recv => $1, :meth => $2 | ||||
| 625 | when /[#{OPERATOR_CHARS}]+$/ # operator | ||||
| 626 | __prepare_line :recv => Regexp.last_match.pre_match, :meth => $& | ||||
| 627 | else # bare words | ||||
| 628 | __prepare_line :recv => "self", :meth => expr | ||||
| 629 | end | ||||
| 630 | end | ||||
| 631 | |||||
| 632 | def __prepare_line(x) | ||||
| 633 | v = "#{VAR}" | ||||
| 634 | klass = "#{VAR}_klass" | ||||
| 635 | flag = "#{VAR}_flag" | ||||
| 636 | which_methods = "#{VAR}_methods" | ||||
| 637 | ancestor_class = "#{VAR}_ancestor_class" | ||||
| 638 | idx = 1 | ||||
| 639 | recv = x[:recv] || x[:klass] || raise(ArgumentError, "need :recv or :klass") | ||||
| 640 | meth = x[:meth_or_constant] || x[:meth] | ||||
| 641 | debugprint "recv=#{recv}", "meth=#{meth}" | ||||
| 642 | if meth | ||||
| 643 | code = <<-EOC | ||||
| 644 | #{v} = (#{recv}) | ||||
| 645 | if Class === #{v} | ||||
| 646 | #{flag} = #{v}.respond_to?('#{meth}') ? "." : "::" | ||||
| 647 | #{klass} = #{v} | ||||
| 648 | #{which_methods} = :methods | ||||
| 649 | else | ||||
| 650 | #{flag} = "#" | ||||
| 651 | #{klass} = #{v}.class | ||||
| 652 | #{which_methods} = :instance_methods | ||||
| 653 | end | ||||
| 654 | #{ancestor_class} = #{klass}.ancestors.delete_if{|c| c==Kernel }.find{|c| c.__send__(#{which_methods}, false).include? '#{meth}' } | ||||
| 655 | $stderr.print("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " ") | ||||
| 656 | |||||
| 657 | if #{ancestor_class} | ||||
| 658 | $stderr.puts(#{ancestor_class}.to_s + #{flag} + '#{meth}') | ||||
| 659 | else | ||||
| 660 | [Kernel, Module, Class].each do |k| | ||||
| 661 | if (k.instance_methods(false) + k.private_instance_methods(false)).include? '#{meth}' | ||||
| 662 | $stderr.printf("%s#%s\\n", k, '#{meth}'); exit | ||||
| 663 | end | ||||
| 664 | end | ||||
| 665 | $stderr.puts(#{v}.to_s + '::' + '#{meth}') | ||||
| 666 | end | ||||
| 667 | exit | ||||
| 668 | EOC | ||||
| 669 | else | ||||
| 670 | code = <<-EOC | ||||
| 671 | #{v} = (#{recv}) | ||||
| 672 | $stderr.print("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " ") | ||||
| 673 | $stderr.puts(#{v}.to_s) | ||||
| 674 | exit | ||||
| 675 | EOC | ||||
| 676 | end | ||||
| 677 | oneline_ize(code) | ||||
| 678 | end | ||||
| 679 | |||||
| 680 | |||||
| 681 | # Completion code for editors. | ||||
| 682 | def completion_code(code, lineno, column=nil) | ||||
| 683 | candidates(code, lineno, column).join("\n") | ||||
| 684 | end | ||||
| 685 | |||||
| 686 | # overridable by module | ||||
| 687 | def _doc(code, lineno, column) | ||||
| 688 | end | ||||
| 689 | |||||
| 690 | def doc(code, lineno, column=nil) | ||||
| 691 | _doc(code, lineno, column) or runtime_data(code, lineno, column).to_s | ||||
| 692 | end | ||||
| 693 | |||||
| 694 | module UseMethodAnalyzer | ||||
| 695 | METHOD_ANALYSIS = "method_analysis" | ||||
| 696 | def have_method_analysis | ||||
| 697 | File.file? METHOD_ANALYSIS | ||||
| 698 | end | ||||
| 699 | |||||
| 700 | def find_method_analysis | ||||
| 701 | here = Dir.pwd | ||||
| 702 | oldpwd = here | ||||
| 703 | begin | ||||
| 704 | while ! have_method_analysis | ||||
| 705 | Dir.chdir("..") | ||||
| 706 | if Dir.pwd == here | ||||
| 707 | return nil # not found | ||||
| 708 | end | ||||
| 709 | here = Dir.pwd | ||||
| 710 | end | ||||
| 711 | ensure | ||||
| 712 | Dir.chdir oldpwd | ||||
| 713 | end | ||||
| 714 | yield(File.join(here, METHOD_ANALYSIS)) | ||||
| 715 | end | ||||
| 716 | |||||
| 717 | def _doc(code, lineno, column=nil) | ||||
| 718 | find_method_analysis do |ma_file| | ||||
| 719 | methods = open(ma_file, "rb"){ |f| Marshal.load(f)} | ||||
| 720 | line = File.readlines(@filename)[lineno-1] | ||||
| 721 | current_method = line[ /^.{#{column}}\w*/][ /\w+[\?!]?$/ ].sub(/:+/,'') | ||||
| 722 | filename = @filename # FIXME | ||||
| 723 | begin | ||||
| 724 | methods[filename][lineno].grep(Regexp.new(Regexp.quote(current_method)))[0] | ||||
| 725 | rescue NoMethodError | ||||
| 726 | raise "doc/method_analyzer:cannot find #{current_method}" | ||||
| 727 | end | ||||
| 728 | |||||
| 729 | end | ||||
| 730 | end | ||||
| 731 | end | ||||
| 732 | |||||
| 733 | end | ||||
| 734 | |||||
| 735 | # ReFe is so-called `Japanese Ri'. | ||||
| 736 | class XMPReFeFilter < XMPDocFilter | ||||
| 737 | def doc(code, lineno, column=nil) | ||||
| 738 | "refe '#{super}'" | ||||
| 739 | end | ||||
| 740 | end | ||||
| 741 | |||||
| 742 | class XMPRiFilter < XMPDocFilter | ||||
| 743 | def doc(code, lineno, column=nil) | ||||
| 744 | "ri '#{super}'" | ||||
| 745 | end | ||||
| 746 | end | ||||
| 747 | |||||
| 748 | class XMPRiEmacsFilter < XMPDocFilter | ||||
| 749 | def doc(code, lineno, column=nil) | ||||
| 750 | begin | ||||
| 751 | %!(xmpfilter-find-tag-or-ri "#{super}")! | ||||
| 752 | rescue => err | ||||
| 753 | return %Q[(error "#{err.message}")] | ||||
| 754 | end | ||||
| 755 | end | ||||
| 756 | end | ||||
| 757 | |||||
| 758 | # FIXME rubikitch: I do not use vim, so I cannot implement XMPRiVimFilter class. | ||||
| 759 | class XMPRiVimFilter < XMPRiFilter | ||||
| 760 | def doc(code, lineno, column=nil) | ||||
| 761 | end | ||||
| 762 | end | ||||
| 763 | |||||
| 764 | #{{{ Main code | ||||
| 765 | if __FILE__ == $0 | ||||
| 766 | require 'optparse' | ||||
| 767 | require 'ostruct' | ||||
| 768 | |||||
| 769 | options = OpenStruct.new | ||||
| 770 | options.interpreter = "ruby" | ||||
| 771 | options.options = "" | ||||
| 772 | options.mode = :annotation | ||||
| 773 | options.min_codeline_size = 50 | ||||
| 774 | options.libs = [] | ||||
| 775 | options.evals = [] | ||||
| 776 | options.include_paths = [] | ||||
| 777 | options.debug = nil | ||||
| 778 | options.wd = nil | ||||
| 779 | options.warnings = true | ||||
| 780 | options.poetry = false | ||||
| 781 | options.column = nil | ||||
| 782 | options.output_stdout = true | ||||
| 783 | |||||
| 784 | rails_settings = false | ||||
| 785 | |||||
| 786 | opts = OptionParser.new do |opts| | ||||
| 787 | opts.banner = "Usage: xmpfilter.rb [options] [inputfile] [-- cmdline args]" | ||||
| 788 | |||||
| 789 | opts.separator "" | ||||
| 790 | opts.separator "Modes:" | ||||
| 791 | opts.on("-a", "--annotations", "Annotate code (default)") do | ||||
| 792 | options.mode = :annotation | ||||
| 793 | end | ||||
| 794 | opts.on("-u", "--unittest", "Complete Test::Unit assertions.") do | ||||
| 795 | options.mode = :unittest | ||||
| 796 | end | ||||
| 797 | opts.on("-s", "--spec", "Complete RSpec expectations.") do | ||||
| 798 | options.mode = :rspec | ||||
| 799 | options.interpreter = "spec" | ||||
| 800 | end | ||||
| 801 | opts.on("-m", "--markers", "Add # => markers.") do | ||||
| 802 | options.mode = :marker | ||||
| 803 | end | ||||
| 804 | opts.on("-C", "--completion", "List completion candidates.") do | ||||
| 805 | options.mode = :completion | ||||
| 806 | end | ||||
| 807 | |||||
| 808 | opts.on("--completion-emacs", "Generate completion code for Emacs.") do | ||||
| 809 | options.mode = :completion_emacs | ||||
| 810 | end | ||||
| 811 | opts.on("--completion-vim", "Generate completion code for Vim.") do | ||||
| 812 | options.mode = :completion_vim | ||||
| 813 | end | ||||
| 814 | |||||
| 815 | opts.on("-D", "--doc", "Print callee method with class.") do | ||||
| 816 | options.mode = :doc | ||||
| 817 | end | ||||
| 818 | opts.on("--refe", "Refe callee method.") do | ||||
| 819 | options.mode = :refe | ||||
| 820 | end | ||||
| 821 | opts.on("--ri", "Ri callee method.") do | ||||
| 822 | options.mode = :ri | ||||
| 823 | end | ||||
| 824 | opts.on("--ri-emacs", "Generate ri code for emacs.") do | ||||
| 825 | options.mode = :ri_emacs | ||||
| 826 | end | ||||
| 827 | opts.on("--ri-vim", "Generate ri code for vim.") do | ||||
| 828 | options.mode = :ri_vim | ||||
| 829 | end | ||||
| 830 | |||||
| 831 | |||||
| 832 | opts.separator "" | ||||
| 833 | opts.separator "Completion and documentation options:" | ||||
| 834 | opts.on("--line=LINE", "Current line number.") do |n| | ||||
| 835 | options.lineno = n.to_i | ||||
| 836 | end | ||||
| 837 | opts.on("--column=COLUMN", "Current column number.") do |n| | ||||
| 838 | options.column = n.to_i | ||||
| 839 | end | ||||
| 840 | opts.on("--use-method-analyzer", "") do |n| | ||||
| 841 | options.use_method_analyzer = true | ||||
| 842 | end | ||||
| 843 | opts.on("--filename=FILENAME") do |n| | ||||
| 844 | options.filename = File.expand_path n | ||||
| 845 | end | ||||
| 846 | |||||
| 847 | opts.separator "" | ||||
| 848 | opts.separator "Interpreter options:" | ||||
| 849 | opts.on("-I PATH", "Add PATH to $LOAD_PATH") do |path| | ||||
| 850 | options.include_paths << path | ||||
| 851 | end | ||||
| 852 | opts.on("-r LIB", "Require LIB before execution.") do |lib| | ||||
| 853 | options.libs << lib | ||||
| 854 | end | ||||
| 855 | opts.on("-e EXPR", "--eval=EXPR", "--stub=EXPR", "Evaluate EXPR after execution.") do |expr| | ||||
| 856 | options.evals << expr | ||||
| 857 | end | ||||
| 858 | |||||
| 859 | opts.separator "" | ||||
| 860 | opts.on("--cd DIR", "Change working directory to DIR.") do |dir| | ||||
| 861 | options.wd = dir | ||||
| 862 | end | ||||
| 863 | opts.on("--debug", "Write transformed source code to xmp-tmp.PID.rb.") do | ||||
| 864 | options.debug = "xmp-tmp.#{Process.pid}.rb" | ||||
| 865 | end | ||||
| 866 | opts.on("-S FILE", "--interpreter FILE", "Use interpreter FILE.") do |interpreter| | ||||
| 867 | options.interpreter = interpreter | ||||
| 868 | end | ||||
| 869 | opts.on("-l N", "--min-line-length N", Integer, "Align markers to N spaces.") do |min_codeline_size| | ||||
| 870 | options.min_codeline_size = min_codeline_size | ||||
| 871 | end | ||||
| 872 | opts.on("--rails", "Setting appropriate for Rails.", | ||||
| 873 | "(no warnings, find working directory,", | ||||
| 874 | " Test::Unit assertions)") do | ||||
| 875 | options.warnings = false | ||||
| 876 | options.mode = :unittest | ||||
| 877 | rails_settings = true | ||||
| 878 | end | ||||
| 879 | opts.on("--[no-]poetry", "Whether to use extra parentheses.", | ||||
| 880 | "(default: use them)") do |poetry_p| | ||||
| 881 | options.poetry = poetry_p | ||||
| 882 | end | ||||
| 883 | opts.on("--[no-]warnings", "Whether to add warnings (# !>).", | ||||
| 884 | "(default: enabled)") {|warnings_p| options.warnings = warnings_p } | ||||
| 885 | opts.on("-q", "--quiet", "Supress standard output.") do | ||||
| 886 | options.output_stdout = false | ||||
| 887 | end | ||||
| 888 | opts.separator "" | ||||
| 889 | opts.on("-h", "--help", "Show this message") do | ||||
| 890 | puts opts | ||||
| 891 | exit | ||||
| 892 | end | ||||
| 893 | opts.on("-v", "--version", "Show version information") do | ||||
| 894 | puts "xmpfilter.rb #{XMPFilter::VERSION}" | ||||
| 895 | exit | ||||
| 896 | end | ||||
| 897 | end | ||||
| 898 | |||||
| 899 | if idx = ARGV.index("--") | ||||
| 900 | extra_opts = ARGV[idx+1..-1] | ||||
| 901 | ARGV.replace ARGV[0...idx] | ||||
| 902 | else | ||||
| 903 | extra_opts = [] | ||||
| 904 | end | ||||
| 905 | opts.parse!(ARGV) | ||||
| 906 | |||||
| 907 | if rails_settings && !options.wd | ||||
| 908 | if File.exist? ARGF.path | ||||
| 909 | options.wd = File.dirname(ARGF.path) | ||||
| 910 | elsif File.exist? "test/unit" | ||||
| 911 | options.wd = "test/unit" | ||||
| 912 | elsif File.exist? "unit" | ||||
| 913 | options.wd = "unit" | ||||
| 914 | end | ||||
| 915 | end | ||||
| 916 | targetcode = ARGF.read | ||||
| 917 | Dir.chdir options.wd if options.wd | ||||
| 918 | |||||
| 919 | klass = case options.mode | ||||
| 920 | when :annotation; XMPFilter | ||||
| 921 | when :unittest; XMPTestUnitFilter | ||||
| 922 | when :rspec; XMPRSpecFilter | ||||
| 923 | when :completion; XMPCompletionFilter | ||||
| 924 | when :completion_emacs; XMPCompletionEmacsFilter | ||||
| 925 | when :completion_vim; XMPCompletionVimFilter | ||||
| 926 | when :doc; XMPDocFilter | ||||
| 927 | when :refe; XMPReFeFilter | ||||
| 928 | when :ri; XMPRiFilter | ||||
| 929 | when :ri_emacs; XMPRiEmacsFilter | ||||
| 930 | when :ri_vim; XMPRiVimFilter | ||||
| 931 | else XMPFilter | ||||
| 932 | end | ||||
| 933 | |||||
| 934 | xmp = klass.new(:interpreter => options.interpreter, :options => extra_opts, | ||||
| 935 | :output_stdout => options.output_stdout, | ||||
| 936 | :include_paths => options.include_paths, :libs => options.libs, | ||||
| 937 | :evals => options.evals, | ||||
| 938 | :use_method_analyzer => options.use_method_analyzer, | ||||
| 939 | :filename => options.filename, | ||||
| 940 | :dump => options.debug, :warnings => options.warnings, | ||||
| 941 | :use_parentheses => !options.poetry) | ||||
| 942 | |||||
| 943 | case options.mode | ||||
| 07ab3088 » | manveru | 2009-06-16 | 944 | when :marker ; puts xmp.add_markers(targetcode, options.min_codeline_size) | |
| 27be08b6 » | manveru | 2009-04-13 | 945 | when :annotation, :unittest, :rspec | |
| 946 | puts xmp.annotate(targetcode) | ||||
| 947 | when :completion, :completion_emacs, :completion_vim | ||||
| 948 | puts xmp.completion_code(targetcode, options.lineno, options.column) | ||||
| 949 | when :doc, :refe, :ri, :ri_emacs, :ri_vim | ||||
| 950 | puts xmp.doc(targetcode, options.lineno, options.column) | ||||
| 951 | else | ||||
| 952 | puts opts | ||||
| 953 | exit | ||||
| 954 | end | ||||
| 955 | end | ||||








