rcodetools 0.4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +18 -0
- data/README +34 -0
- data/README.emacs +54 -0
- data/README.method_analysis +13 -0
- data/README.vim +84 -0
- data/README.xmpfilter +202 -0
- data/Rakefile +123 -0
- data/Rakefile.method_analysis +30 -0
- data/THANKS +6 -0
- data/bin/rct-complete +37 -0
- data/bin/rct-doc +50 -0
- data/bin/rct-meth-args +392 -0
- data/bin/xmpfilter +75 -0
- data/icicles-rcodetools.el +31 -0
- data/lib/method_analyzer.rb +107 -0
- data/lib/rcodetools/completion.rb +282 -0
- data/lib/rcodetools/doc.rb +176 -0
- data/lib/rcodetools/options.rb +83 -0
- data/lib/rcodetools/xmpfilter.rb +208 -0
- data/lib/rcodetools/xmptestunitfilter.rb +197 -0
- data/rcodetools.el +162 -0
- data/rcodetools.vim +118 -0
- data/setup.rb +1585 -0
- data/test/data/add_markers-input.rb +2 -0
- data/test/data/add_markers-output.rb +2 -0
- data/test/data/bindings-input.rb +26 -0
- data/test/data/bindings-output.rb +31 -0
- data/test/data/completion-input.rb +1 -0
- data/test/data/completion-output.rb +2 -0
- data/test/data/completion_emacs-input.rb +1 -0
- data/test/data/completion_emacs-output.rb +5 -0
- data/test/data/completion_emacs_icicles-input.rb +1 -0
- data/test/data/completion_emacs_icicles-output.rb +5 -0
- data/test/data/doc-input.rb +1 -0
- data/test/data/doc-output.rb +1 -0
- data/test/data/method_analyzer-data.rb +33 -0
- data/test/data/method_args.data.rb +106 -0
- data/test/data/no_warnings-input.rb +3 -0
- data/test/data/no_warnings-output.rb +4 -0
- data/test/data/refe-input.rb +1 -0
- data/test/data/refe-output.rb +1 -0
- data/test/data/ri-input.rb +1 -0
- data/test/data/ri-output.rb +1 -0
- data/test/data/ri_emacs-input.rb +1 -0
- data/test/data/ri_emacs-output.rb +1 -0
- data/test/data/ri_vim-input.rb +1 -0
- data/test/data/ri_vim-output.rb +1 -0
- data/test/data/rspec-input.rb +48 -0
- data/test/data/rspec-output.rb +52 -0
- data/test/data/rspec_poetry-input.rb +48 -0
- data/test/data/rspec_poetry-output.rb +52 -0
- data/test/data/simple_annotation-input.rb +8 -0
- data/test/data/simple_annotation-output.rb +8 -0
- data/test/data/unit_test-input.rb +50 -0
- data/test/data/unit_test-output.rb +52 -0
- data/test/data/unit_test_poetry-input.rb +50 -0
- data/test/data/unit_test_poetry-output.rb +52 -0
- data/test/test_completion.rb +467 -0
- data/test/test_doc.rb +403 -0
- data/test/test_functional.rb +18 -0
- data/test/test_method_analyzer.rb +99 -0
- data/test/test_method_args.rb +134 -0
- data/test/test_run.rb +41 -0
- data/test/test_xmpfilter.rb +36 -0
- data/test/test_xmptestunitfilter.rb +84 -0
- metadata +139 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'rcodetools/completion'
|
2
|
+
# Call Ri for any editors!!
|
3
|
+
# by rubikitch <rubikitch@ruby-lang.org>
|
4
|
+
class XMPDocFilter < XMPFilter
|
5
|
+
include ProcessParticularLine
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
super
|
9
|
+
@filename = opts[:filename]
|
10
|
+
extend UseMethodAnalyzer if opts[:use_method_analyzer]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.run(code, opts)
|
14
|
+
new(opts).doc(code, opts[:lineno], opts[:column])
|
15
|
+
end
|
16
|
+
|
17
|
+
def prepare_line(expr, column)
|
18
|
+
set_expr_and_postfix!(expr, column){|c|
|
19
|
+
withop_re = /^.{#{c-1}}[#{OPERATOR_CHARS}]+/
|
20
|
+
if expr =~ withop_re
|
21
|
+
withop_re
|
22
|
+
else
|
23
|
+
/^.{#{c}}[\w#{OPERATOR_CHARS}]*/
|
24
|
+
end
|
25
|
+
}
|
26
|
+
recv = expr
|
27
|
+
|
28
|
+
# When expr already knows receiver and method,
|
29
|
+
return(__prepare_line :recv => expr.eval_string, :meth => expr.meth) if expr.eval_string
|
30
|
+
|
31
|
+
case expr
|
32
|
+
when /^(?:::)?([A-Z].*)(?:::|\.)(.*)$/ # nested constants / class methods
|
33
|
+
__prepare_line :klass => $1, :meth_or_constant => $2
|
34
|
+
when /^(?:::)?[A-Z]/ # normal constants
|
35
|
+
__prepare_line :klass => expr
|
36
|
+
when /\.([^.]*)$/ # method call
|
37
|
+
__prepare_line :recv => Regexp.last_match.pre_match, :meth => $1
|
38
|
+
when /^(.+)(\[\]=?)$/ # [], []=
|
39
|
+
__prepare_line :recv => $1, :meth => $2
|
40
|
+
when /[#{OPERATOR_CHARS}]+$/ # operator
|
41
|
+
__prepare_line :recv => Regexp.last_match.pre_match, :meth => $&
|
42
|
+
else # bare words
|
43
|
+
__prepare_line :recv => "self", :meth => expr
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def __prepare_line(x)
|
48
|
+
v = "#{VAR}"
|
49
|
+
klass = "#{VAR}_klass"
|
50
|
+
flag = "#{VAR}_flag"
|
51
|
+
which_methods = "#{VAR}_methods"
|
52
|
+
ancestor_class = "#{VAR}_ancestor_class"
|
53
|
+
idx = 1
|
54
|
+
recv = x[:recv] || x[:klass] || raise(ArgumentError, "need :recv or :klass")
|
55
|
+
meth = x[:meth_or_constant] || x[:meth]
|
56
|
+
debugprint "recv=#{recv}", "meth=#{meth}"
|
57
|
+
if meth
|
58
|
+
code = <<-EOC
|
59
|
+
#{v} = (#{recv})
|
60
|
+
if Class === #{v}
|
61
|
+
#{flag} = #{v}.respond_to?('#{meth}') ? "." : "::"
|
62
|
+
#{klass} = #{v}
|
63
|
+
#{which_methods} = :methods
|
64
|
+
else
|
65
|
+
#{flag} = "#"
|
66
|
+
#{klass} = #{v}.class
|
67
|
+
#{which_methods} = :instance_methods
|
68
|
+
end
|
69
|
+
#{ancestor_class} = #{klass}.ancestors.delete_if{|c| c==Kernel }.find{|c| c.__send__(#{which_methods}, false).include? '#{meth}' }
|
70
|
+
$stderr.print("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " ")
|
71
|
+
|
72
|
+
if #{ancestor_class}
|
73
|
+
$stderr.puts(#{ancestor_class}.to_s + #{flag} + '#{meth}')
|
74
|
+
else
|
75
|
+
[Kernel, Module, Class].each do |k|
|
76
|
+
if (k.instance_methods(false) + k.private_instance_methods(false)).include? '#{meth}'
|
77
|
+
$stderr.printf("%s#%s\\n", k, '#{meth}'); exit
|
78
|
+
end
|
79
|
+
end
|
80
|
+
$stderr.puts(#{v}.to_s + '::' + '#{meth}')
|
81
|
+
end
|
82
|
+
exit
|
83
|
+
EOC
|
84
|
+
else
|
85
|
+
code = <<-EOC
|
86
|
+
#{v} = (#{recv})
|
87
|
+
$stderr.print("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " ")
|
88
|
+
$stderr.puts(#{v}.to_s)
|
89
|
+
exit
|
90
|
+
EOC
|
91
|
+
end
|
92
|
+
oneline_ize(code)
|
93
|
+
end
|
94
|
+
|
95
|
+
# overridable by module
|
96
|
+
def _doc(code, lineno, column)
|
97
|
+
end
|
98
|
+
|
99
|
+
def doc(code, lineno, column=nil)
|
100
|
+
_doc(code, lineno, column) or runtime_data(code, lineno, column).to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
module UseMethodAnalyzer
|
104
|
+
METHOD_ANALYSIS = "method_analysis"
|
105
|
+
def have_method_analysis
|
106
|
+
File.file? METHOD_ANALYSIS
|
107
|
+
end
|
108
|
+
|
109
|
+
def find_method_analysis
|
110
|
+
here = Dir.pwd
|
111
|
+
oldpwd = here
|
112
|
+
begin
|
113
|
+
while ! have_method_analysis
|
114
|
+
Dir.chdir("..")
|
115
|
+
if Dir.pwd == here
|
116
|
+
return nil # not found
|
117
|
+
end
|
118
|
+
here = Dir.pwd
|
119
|
+
end
|
120
|
+
ensure
|
121
|
+
Dir.chdir oldpwd
|
122
|
+
end
|
123
|
+
yield(File.join(here, METHOD_ANALYSIS))
|
124
|
+
end
|
125
|
+
|
126
|
+
def _doc(code, lineno, column=nil)
|
127
|
+
find_method_analysis do |ma_file|
|
128
|
+
methods = open(ma_file, "rb"){ |f| Marshal.load(f)}
|
129
|
+
line = File.readlines(@filename)[lineno-1]
|
130
|
+
current_method = line[ /^.{#{column}}\w*/][ /\w+[\?!]?$/ ].sub(/:+/,'')
|
131
|
+
filename = @filename # FIXME
|
132
|
+
begin
|
133
|
+
methods[filename][lineno].grep(Regexp.new(Regexp.quote(current_method)))[0]
|
134
|
+
rescue NoMethodError
|
135
|
+
raise "doc/method_analyzer:cannot find #{current_method}"
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
# ReFe is so-called `Japanese Ri'.
|
145
|
+
class XMPReFeFilter < XMPDocFilter
|
146
|
+
def doc(code, lineno, column=nil)
|
147
|
+
"refe '#{super}'"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class XMPRiFilter < XMPDocFilter
|
152
|
+
def doc(code, lineno, column=nil)
|
153
|
+
"ri '#{super}'"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class XMPRiEmacsFilter < XMPDocFilter
|
158
|
+
def doc(code, lineno, column=nil)
|
159
|
+
begin
|
160
|
+
%!(rct-find-tag-or-ri "#{super}")!
|
161
|
+
rescue Exception => err
|
162
|
+
return %Q[(error "#{err.message}")]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class XMPRiVimFilter < XMPDocFilter
|
168
|
+
def doc(code, lineno, column=nil)
|
169
|
+
begin
|
170
|
+
%{call RCT_find_tag_or_ri("#{super}")}
|
171
|
+
rescue Exception => err
|
172
|
+
return %Q[echo #{err.message.inspect}]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
# Domain specific OptionParser extensions
|
4
|
+
module OptionHandler
|
5
|
+
def set_banner
|
6
|
+
self.banner = "Usage: #{$0} [options] [inputfile] [-- cmdline args]"
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_position(options)
|
10
|
+
separator ""
|
11
|
+
separator "Position options:"
|
12
|
+
on("--line=LINE", "Current line number.") do |n|
|
13
|
+
options[:lineno] = n.to_i
|
14
|
+
end
|
15
|
+
on("--column=COLUMN", "Current column number.") do |n|
|
16
|
+
options[:column] = n.to_i
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle_interpreter(options)
|
21
|
+
separator ""
|
22
|
+
separator "Interpreter options:"
|
23
|
+
on("-S FILE", "--interpreter FILE", "Use interpreter FILE.") do |interpreter|
|
24
|
+
options[:interpreter] = interpreter
|
25
|
+
end
|
26
|
+
on("-I PATH", "Add PATH to $LOAD_PATH") do |path|
|
27
|
+
options[:include_paths] << path
|
28
|
+
end
|
29
|
+
on("-r LIB", "Require LIB before execution.") do |lib|
|
30
|
+
options[:libs] << lib
|
31
|
+
end
|
32
|
+
on("-e EXPR", "--eval=EXPR", "--stub=EXPR", "Evaluate EXPR after execution.") do |expr|
|
33
|
+
options[:evals] << expr
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_misc(options)
|
39
|
+
separator ""
|
40
|
+
separator "Misc options:"
|
41
|
+
on("--cd DIR", "Change working directory to DIR.") do |dir|
|
42
|
+
options[:wd] = dir
|
43
|
+
end
|
44
|
+
on("--debug", "Write transformed source code to xmp-tmp.PID.rb.") do
|
45
|
+
options[:dump] = "xmp-tmp.#{Process.pid}.rb"
|
46
|
+
end
|
47
|
+
separator ""
|
48
|
+
on("-h", "--help", "Show this message") do
|
49
|
+
puts self
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
on("-v", "--version", "Show version information") do
|
53
|
+
puts "#{File.basename($0)} #{XMPFilter::VERSION}"
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_extra_opts(options)
|
62
|
+
if idx = ARGV.index("--")
|
63
|
+
options[:options] = ARGV[idx+1..-1]
|
64
|
+
ARGV.replace ARGV[0...idx]
|
65
|
+
else
|
66
|
+
options[:options] = []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
DEFAULT_OPTIONS = {
|
71
|
+
:interpreter => "ruby",
|
72
|
+
:options => ["hoge"],
|
73
|
+
:min_codeline_size => 50,
|
74
|
+
:libs => [],
|
75
|
+
:evals => [],
|
76
|
+
:include_paths => [],
|
77
|
+
:dump => nil,
|
78
|
+
:wd => nil,
|
79
|
+
:warnings => true,
|
80
|
+
:use_parentheses => true,
|
81
|
+
:column => nil,
|
82
|
+
:output_stdout => true,
|
83
|
+
}
|
@@ -0,0 +1,208 @@
|
|
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
|
+
|
20
|
+
# The processor (overridable)
|
21
|
+
def self.run(code, opts)
|
22
|
+
new(opts).annotate(code)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(opts = {})
|
26
|
+
options = INITIALIZE_OPTS.merge opts
|
27
|
+
@interpreter = options[:interpreter]
|
28
|
+
@options = options[:options]
|
29
|
+
@libs = options[:libs]
|
30
|
+
@evals = options[:evals] || []
|
31
|
+
@include_paths = options[:include_paths]
|
32
|
+
@output_stdout = options[:output_stdout]
|
33
|
+
@dump = options[:dump]
|
34
|
+
@warnings = options[:warnings]
|
35
|
+
@parentheses = options[:use_parentheses]
|
36
|
+
@ignore_NoMethodError = options[:ignore_NoMethodError]
|
37
|
+
|
38
|
+
@postfix = ""
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_markers(code, min_codeline_size = 50)
|
42
|
+
maxlen = code.map{|x| x.size}.max
|
43
|
+
maxlen = [min_codeline_size, maxlen + 2].max
|
44
|
+
ret = ""
|
45
|
+
code.each do |l|
|
46
|
+
l = l.chomp.gsub(/ # (=>|!>).*/, "").gsub(/\s*$/, "")
|
47
|
+
ret << (l + " " * (maxlen - l.size) + " # =>\n")
|
48
|
+
end
|
49
|
+
ret
|
50
|
+
end
|
51
|
+
|
52
|
+
def annotate(code)
|
53
|
+
idx = 0
|
54
|
+
newcode = code.gsub(/^(.*) # =>.*/){|l| prepare_line($1, idx += 1) }
|
55
|
+
if @dump
|
56
|
+
File.open(@dump, "w"){|f| f.puts newcode}
|
57
|
+
end
|
58
|
+
stdout, stderr = execute(newcode)
|
59
|
+
output = stderr.readlines
|
60
|
+
runtime_data = extract_data(output)
|
61
|
+
idx = 0
|
62
|
+
annotated = code.gsub(/^(.*) # =>.*/) do |l|
|
63
|
+
expr = $1
|
64
|
+
if /^\s*#/ =~ l
|
65
|
+
l
|
66
|
+
else
|
67
|
+
annotated_line(l, expr, runtime_data, idx += 1)
|
68
|
+
end
|
69
|
+
end.gsub(/ # !>.*/, '').gsub(/# (>>|~>)[^\n]*\n/m, "");
|
70
|
+
ret = final_decoration(annotated, output)
|
71
|
+
if @output_stdout and (s = stdout.read) != ""
|
72
|
+
ret << s.inject(""){|s,line| s + "# >> #{line}".chomp + "\n" }
|
73
|
+
end
|
74
|
+
ret
|
75
|
+
end
|
76
|
+
|
77
|
+
def annotated_line(line, expression, runtime_data, idx)
|
78
|
+
"#{expression} # => " + (runtime_data.results[idx].map{|x| x[1]} || []).join(", ")
|
79
|
+
end
|
80
|
+
|
81
|
+
def prepare_line_annotation(expr, idx)
|
82
|
+
v = "#{VAR}"
|
83
|
+
blocal = "__#{VAR}"
|
84
|
+
blocal2 = "___#{VAR}"
|
85
|
+
# rubikitch: oneline-ized
|
86
|
+
# <<EOF.chomp
|
87
|
+
# ((#{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}))
|
88
|
+
# EOF
|
89
|
+
oneline_ize(<<-EOF).chomp
|
90
|
+
#{v} = (#{expr})
|
91
|
+
$stderr.puts("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " " + #{v}.inspect) || begin
|
92
|
+
$stderr.puts local_variables
|
93
|
+
local_variables.each{|#{blocal}|
|
94
|
+
#{blocal2} = eval(#{blocal})
|
95
|
+
if #{v} == #{blocal2} && #{blocal} != %#{expr}.strip
|
96
|
+
$stderr.puts("#{MARKER}[#{idx}] ==> " + #{blocal})
|
97
|
+
elsif [#{blocal2}] == #{v}
|
98
|
+
$stderr.puts("#{MARKER}[#{idx}] ==> [" + #{blocal} + "]")
|
99
|
+
end
|
100
|
+
}
|
101
|
+
nil
|
102
|
+
rescue Exception
|
103
|
+
nil
|
104
|
+
end || #{v}
|
105
|
+
EOF
|
106
|
+
|
107
|
+
end
|
108
|
+
alias_method :prepare_line, :prepare_line_annotation
|
109
|
+
|
110
|
+
def execute_tmpfile(code)
|
111
|
+
stdin, stdout, stderr = (1..3).map do |i|
|
112
|
+
fname = "xmpfilter.tmpfile_#{Process.pid}-#{i}.rb"
|
113
|
+
at_exit { File.unlink fname }
|
114
|
+
File.open(fname, "w+")
|
115
|
+
end
|
116
|
+
stdin.puts code
|
117
|
+
stdin.close
|
118
|
+
exe_line = <<-EOF.map{|l| l.strip}.join(";")
|
119
|
+
$stdout.reopen('#{stdout.path}', 'w')
|
120
|
+
$stderr.reopen('#{stderr.path}', 'w')
|
121
|
+
$0.replace '#{stdin.path}'
|
122
|
+
ARGV.replace(#{@options.inspect})
|
123
|
+
load #{stdin.path.inspect}
|
124
|
+
#{@evals.join(";")}
|
125
|
+
EOF
|
126
|
+
system(*(interpreter_command << "-e" << exe_line))
|
127
|
+
[stdout, stderr]
|
128
|
+
end
|
129
|
+
|
130
|
+
def execute_popen(code)
|
131
|
+
require 'open3'
|
132
|
+
stdin, stdout, stderr = Open3::popen3(*interpreter_command)
|
133
|
+
stdin.puts code
|
134
|
+
@evals.each{|x| stdin.puts x } unless @evals.empty?
|
135
|
+
stdin.close
|
136
|
+
[stdout, stderr]
|
137
|
+
end
|
138
|
+
|
139
|
+
if /win|mingw/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM
|
140
|
+
alias_method :execute, :execute_tmpfile
|
141
|
+
else
|
142
|
+
alias_method :execute, :execute_popen
|
143
|
+
end
|
144
|
+
|
145
|
+
def interpreter_command
|
146
|
+
r = [@interpreter, "-w"]
|
147
|
+
r << "-d" if $DEBUG
|
148
|
+
r << "-I#{@include_paths.join(":")}" unless @include_paths.empty?
|
149
|
+
@libs.each{|x| r << "-r#{x}" } unless @libs.empty?
|
150
|
+
(r << "-").concat @options unless @options.empty?
|
151
|
+
r
|
152
|
+
end
|
153
|
+
|
154
|
+
def extract_data(output)
|
155
|
+
results = Hash.new{|h,k| h[k] = []}
|
156
|
+
exceptions = Hash.new{|h,k| h[k] = []}
|
157
|
+
bindings = Hash.new{|h,k| h[k] = []}
|
158
|
+
output.grep(XMP_RE).each do |line|
|
159
|
+
result_id, op, result = XMP_RE.match(line).captures
|
160
|
+
case op
|
161
|
+
when "=>"
|
162
|
+
klass, value = /(\S+)\s+(.*)/.match(result).captures
|
163
|
+
results[result_id.to_i] << [klass, value]
|
164
|
+
when "~>"
|
165
|
+
exceptions[result_id.to_i] << result
|
166
|
+
when "==>"
|
167
|
+
bindings[result_id.to_i] << result unless result.index(VAR)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
RuntimeData.new(results, exceptions, bindings)
|
171
|
+
end
|
172
|
+
|
173
|
+
def final_decoration(code, output)
|
174
|
+
warnings = {}
|
175
|
+
output.join.grep(WARNING_RE).map do |x|
|
176
|
+
md = WARNING_RE.match(x)
|
177
|
+
warnings[md[1].to_i] = md[2]
|
178
|
+
end
|
179
|
+
idx = 0
|
180
|
+
ret = code.map do |line|
|
181
|
+
w = warnings[idx+=1]
|
182
|
+
if @warnings
|
183
|
+
w ? (line.chomp + " # !> #{w}") : line
|
184
|
+
else
|
185
|
+
line
|
186
|
+
end
|
187
|
+
end
|
188
|
+
output = output.reject{|x| /^-:[0-9]+: warning/.match(x)}
|
189
|
+
if exception = /^-:[0-9]+:.*/m.match(output.join)
|
190
|
+
ret << exception[0].map{|line| "# ~> " + line }
|
191
|
+
end
|
192
|
+
ret
|
193
|
+
end
|
194
|
+
|
195
|
+
def oneline_ize(code)
|
196
|
+
"((" + code.gsub(/\r?\n|\r/, ';') + "))#{@postfix}\n"
|
197
|
+
end
|
198
|
+
|
199
|
+
def debugprint(*args)
|
200
|
+
$stderr.puts(*args) if $DEBUG
|
201
|
+
end
|
202
|
+
end # clas XMPFilter
|
203
|
+
|
204
|
+
class XMPAddMarkers < XMPFilter
|
205
|
+
def self.run(code, opts)
|
206
|
+
new(opts).add_markers(code, opts[:min_codeline_size])
|
207
|
+
end
|
208
|
+
end
|