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
data/bin/xmpfilter
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#! /home/batsman/usr//bin/ruby
|
2
|
+
require 'rcodetools/xmpfilter'
|
3
|
+
require 'rcodetools/options'
|
4
|
+
|
5
|
+
options = DEFAULT_OPTIONS
|
6
|
+
rails_settings = false
|
7
|
+
klass = XMPFilter
|
8
|
+
|
9
|
+
opts = OptionParser.new do |opts|
|
10
|
+
opts.extend OptionHandler
|
11
|
+
opts.set_banner
|
12
|
+
|
13
|
+
opts.separator ""
|
14
|
+
opts.separator "Modes:"
|
15
|
+
opts.on("-a", "--annotations", "Annotate code (default)") do
|
16
|
+
klass = XMPFilter
|
17
|
+
end
|
18
|
+
opts.on("-u", "--unittest", "Complete Test::Unit assertions.") do
|
19
|
+
require 'rcodetools/xmptestunitfilter'
|
20
|
+
klass = XMPTestUnitFilter
|
21
|
+
end
|
22
|
+
opts.on("-s", "--spec", "Complete RSpec expectations.") do
|
23
|
+
require 'rcodetools/xmptestunitfilter'
|
24
|
+
klass = XMPRSpecFilter
|
25
|
+
options[:interpreter] = "spec"
|
26
|
+
end
|
27
|
+
opts.on("-m", "--markers", "Add # => markers.") do
|
28
|
+
klass = XMPAddMarkers
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.handle_interpreter options
|
32
|
+
|
33
|
+
opts.separator ""
|
34
|
+
opts.separator "Specific options:"
|
35
|
+
opts.on("-l N", "--min-line-length N", Integer, "Align markers to N spaces.") do |min_codeline_size|
|
36
|
+
options[:min_codeline_size] = min_codeline_size
|
37
|
+
end
|
38
|
+
opts.on("--rails", "Setting appropriate for Rails.",
|
39
|
+
"(no warnings, find working directory,",
|
40
|
+
" Test::Unit assertions)") do
|
41
|
+
require 'rcodetools/xmptestunitfilter'
|
42
|
+
options[:warnings] = false
|
43
|
+
klass = XMPTestUnitFilter
|
44
|
+
rails_settings = true
|
45
|
+
end
|
46
|
+
opts.on("--[no-]poetry", "Whether to use extra parentheses.",
|
47
|
+
"(default: use them)") do |poetry_p|
|
48
|
+
options[:use_parentheses] = !poetry_p
|
49
|
+
end
|
50
|
+
opts.on("--[no-]warnings", "Whether to add warnings (# !>).",
|
51
|
+
"(default: enabled)") {|warnings_p| options[:warnings] = warnings_p }
|
52
|
+
opts.on("-q", "--quiet", "Supress standard output.") do
|
53
|
+
options[:output_stdout] = false
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.handle_misc options
|
57
|
+
end
|
58
|
+
|
59
|
+
set_extra_opts options
|
60
|
+
opts.parse!(ARGV)
|
61
|
+
|
62
|
+
if rails_settings && !options[:wd]
|
63
|
+
if File.exist? ARGF.path
|
64
|
+
options[:wd] = File.dirname(ARGF.path)
|
65
|
+
elsif File.exist? "test/unit"
|
66
|
+
options[:wd] = "test/unit"
|
67
|
+
elsif File.exist? "unit"
|
68
|
+
options[:wd] = "unit"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
targetcode = ARGF.read
|
72
|
+
Dir.chdir options[:wd] if options[:wd]
|
73
|
+
|
74
|
+
# Do the job. dispatched by klass.
|
75
|
+
puts klass.run(targetcode, options)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
;;; icicles-rcodetools.el -- accurate completion with icicles
|
2
|
+
|
3
|
+
;;; Copyright (c) 2006 rubikitch <rubikitch@ruby-lang.org>
|
4
|
+
;;;
|
5
|
+
;;; Use and distribution subject to the terms of the Ruby license.
|
6
|
+
|
7
|
+
(require 'icicles)
|
8
|
+
(require 'rcodetools)
|
9
|
+
|
10
|
+
(setq rct-complete-symbol-function 'rct-complete-symbol--icicles)
|
11
|
+
(icicle-define-command rct-complete-symbol--icicles
|
12
|
+
"Perform ruby method and class completion on the text around point with icicles.
|
13
|
+
C-M-RET shows RI documentation on each candidate.
|
14
|
+
See also `rct-interactive'."
|
15
|
+
|
16
|
+
(lambda (result)
|
17
|
+
(save-excursion
|
18
|
+
(search-backward pattern)
|
19
|
+
(setq beg (point)))
|
20
|
+
(delete-region beg end)
|
21
|
+
(insert result)) ;/function
|
22
|
+
"rct-complete: " ;prompt
|
23
|
+
rct-method-completion-table
|
24
|
+
nil t pattern nil nil nil
|
25
|
+
((end (point)) beg
|
26
|
+
pattern klass
|
27
|
+
(icicle-candidate-help-fn
|
28
|
+
(lambda (result) (ri result klass)))) ;bindings
|
29
|
+
(rct-exec-and-eval rct-complete-command-name "--completion-emacs-icicles"))
|
30
|
+
|
31
|
+
(provide 'icicles-rcodetools)
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class Module
|
2
|
+
remove_method :attr_reader
|
3
|
+
def attr_reader(*names)
|
4
|
+
names.each do |name|
|
5
|
+
module_eval "def #{name}() @#{name} end"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
remove_method :attr_writer
|
9
|
+
def attr_writer(*names)
|
10
|
+
names.each do |name|
|
11
|
+
module_eval "def #{name}=(x) @#{name}=x end"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
remove_method :attr_accessor
|
15
|
+
def attr_accessor(*names)
|
16
|
+
attr_reader(*names)
|
17
|
+
attr_writer(*names)
|
18
|
+
end
|
19
|
+
remove_method :attr
|
20
|
+
def attr(name, writer=false)
|
21
|
+
attr_reader name
|
22
|
+
attr_writer name if writer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
module MethodAnalyzer
|
28
|
+
@@methods = Hash.new{ |h,k| h[k] = Hash.new{ |h,k| h[k] = []} }
|
29
|
+
@@whereis = []
|
30
|
+
@@expand_path = Hash.new{ |h,k| h[k] = File.expand_path(k)}
|
31
|
+
|
32
|
+
def self.trace_func(event, file, line, id, binding, klass, *rest)
|
33
|
+
return if file == __FILE__
|
34
|
+
return if (event != 'call' and event != 'c-call')
|
35
|
+
return if klass == Class and id == :inherited
|
36
|
+
return if klass == Module and id == :method_added
|
37
|
+
return if klass == Kernel and id == :singleton_method_added
|
38
|
+
saved_crit = Thread.critical
|
39
|
+
Thread.critical = true
|
40
|
+
|
41
|
+
the_self = eval("self",binding)
|
42
|
+
flag = Class === the_self ? "." : "#"
|
43
|
+
#klass = klass == Kernel ? Object : klass
|
44
|
+
fullname = "#{klass}#{flag}#{id}"
|
45
|
+
file.replace @@expand_path[file]
|
46
|
+
if event == 'call'
|
47
|
+
@@whereis << [file, line, fullname] if file !~ /\(eval\)$/
|
48
|
+
file, line, rest = caller(4)[0].split(/:/)
|
49
|
+
file.replace @@expand_path[file] # DRY
|
50
|
+
p caller(0) if $DEBUG
|
51
|
+
line = line.to_i
|
52
|
+
end
|
53
|
+
@@methods[file][line] << fullname if event =~ /call/
|
54
|
+
|
55
|
+
Thread.critical = saved_crit
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.at_exit__output_marshal
|
59
|
+
at_exit do
|
60
|
+
set_trace_func nil
|
61
|
+
dbfile = "method_analysis"
|
62
|
+
old = Marshal.load(File.read(dbfile)) rescue {}
|
63
|
+
open(dbfile, "wb") do |io|
|
64
|
+
# Because Marshal.dump cannot handle hashes with default_proc
|
65
|
+
@@methods.default = nil
|
66
|
+
@@methods.each_value{ |v| v.default=nil; v.each_value{ |vv| vv.uniq! } }
|
67
|
+
Marshal.dump(@@methods.merge(old), io)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def self.at_exit__output_text
|
74
|
+
at_exit do
|
75
|
+
set_trace_func nil
|
76
|
+
puts "method fullnames"
|
77
|
+
@@methods.sort.each do |file, lines|
|
78
|
+
lines.sort.each do |line, methods|
|
79
|
+
printf "%s:%s:%s\n", file, line, methods.uniq.join(" ")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
puts
|
84
|
+
puts "method definitions"
|
85
|
+
@@whereis.sort.uniq.each do |file, line, fullname |
|
86
|
+
printf "%s:%s:%s\n", file, line, fullname
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.set_at_exit
|
93
|
+
case ENV['METHOD_ANALYZER_FORMAT']
|
94
|
+
when 'marshal'
|
95
|
+
at_exit__output_marshal
|
96
|
+
else
|
97
|
+
at_exit__output_text
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
set_at_exit
|
102
|
+
set_trace_func method(:trace_func).to_proc
|
103
|
+
end
|
104
|
+
|
105
|
+
if __FILE__ == $0
|
106
|
+
load "./test/data/method_analyzer-data.rb"
|
107
|
+
end
|
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'rcodetools/xmpfilter'
|
2
|
+
require 'enumerator'
|
3
|
+
# Common routines for XMPCompletionFilter/XMPDocFilter
|
4
|
+
module ProcessParticularLine
|
5
|
+
def fill_literal!(expr)
|
6
|
+
[ "\"", "'", "`" ].each do |q|
|
7
|
+
expr.gsub!(/#{q}(.+)#{q}/){ '"' + "x"*$1.length + '"' }
|
8
|
+
end
|
9
|
+
expr.gsub!(/(%([wWqQxrs])?(\W))(.+?)\3/){
|
10
|
+
percent = $2 == 'x' ? '%'+$3 : $1 # avoid executing shell command
|
11
|
+
percent + "x"*$4.length + $3
|
12
|
+
}
|
13
|
+
[ %w[( )], %w[{ }], %w![ ]!, %w[< >] ].each do |b,e|
|
14
|
+
rb, re = [b,e].map{ |x| Regexp.quote(x)}
|
15
|
+
expr.gsub!(/(%([wWqQxrs])?(#{rb}))(.+)#{re}/){
|
16
|
+
percent = $2 == 'x' ? '%'+$3 : $1 # avoid executing shell command
|
17
|
+
percent + "x"*$4.length + e
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ExpressionExtension
|
23
|
+
attr_accessor :eval_string
|
24
|
+
attr_accessor :meth
|
25
|
+
end
|
26
|
+
OPERATOR_CHARS = '\|^&<>=~\+\-\*\/%\['
|
27
|
+
def set_expr_and_postfix!(expr, column, ®exp)
|
28
|
+
expr.extend ExpressionExtension
|
29
|
+
|
30
|
+
@postfix = ""
|
31
|
+
expr_orig = expr.clone
|
32
|
+
column ||= expr.length
|
33
|
+
last_char = expr[column-1]
|
34
|
+
expr.replace expr[ regexp[column] ]
|
35
|
+
debugprint "expr_orig=#{expr_orig}", "expr(sliced)=#{expr}"
|
36
|
+
right_stripped = Regexp.last_match.post_match
|
37
|
+
_handle_do_end right_stripped
|
38
|
+
aref_or_aset = aref_or_aset? right_stripped, last_char
|
39
|
+
debugprint "aref_or_aset=#{aref_or_aset.inspect}"
|
40
|
+
set_last_word! expr, aref_or_aset
|
41
|
+
fill_literal! expr_orig
|
42
|
+
_handle_brackets expr_orig, expr
|
43
|
+
expr << aref_or_aset if aref_or_aset
|
44
|
+
_handle_keywords expr_orig, column
|
45
|
+
debugprint "expr(processed)=#{expr}"
|
46
|
+
expr
|
47
|
+
end
|
48
|
+
|
49
|
+
def _handle_do_end(right_stripped)
|
50
|
+
right_stripped << "\n"
|
51
|
+
n_do = right_stripped.scan(/[\s\)]do\s/).length
|
52
|
+
n_end = right_stripped.scan(/\bend\b/).length
|
53
|
+
@postfix = ";begin" * (n_do - n_end)
|
54
|
+
end
|
55
|
+
|
56
|
+
def _handle_brackets(expr_orig, expr)
|
57
|
+
[ %w[{ }], %w[( )], %w![ ]! ].each do |left, right|
|
58
|
+
n_left = expr_orig.count(left) - expr.count(left)
|
59
|
+
n_right = expr_orig.count(right) - expr.count(right)
|
60
|
+
n = n_left - n_right
|
61
|
+
@postfix << ";#{left}" * n if n >= 0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def _handle_keywords(expr_orig, column)
|
66
|
+
%w[if unless while until for].each do |keyw|
|
67
|
+
pos = expr_orig.index(/\b#{keyw}\b/)
|
68
|
+
@postfix << ";begin" if pos and pos < column # if * xxx
|
69
|
+
|
70
|
+
pos = expr_orig.index(/;\s*#{keyw}\b/)
|
71
|
+
@postfix << ";begin" if pos and column < pos # * ; if xxx
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def aref_or_aset?(right_stripped, last_char)
|
76
|
+
if last_char == ?[
|
77
|
+
case right_stripped
|
78
|
+
when /\]\s*=/: "[]="
|
79
|
+
when /\]/: "[]"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_last_word!(expr, aref_or_aset=nil)
|
85
|
+
debugprint "expr(before set_last_word)=#{expr}"
|
86
|
+
if aref_or_aset
|
87
|
+
opchars = ""
|
88
|
+
else
|
89
|
+
opchars = expr.slice!(/\s*[#{OPERATOR_CHARS}]+$/)
|
90
|
+
debugprint "expr(strip opchars)=#{expr}"
|
91
|
+
end
|
92
|
+
|
93
|
+
expr.replace(if expr =~ /[\"\'\`]$/ # String operations
|
94
|
+
"''"
|
95
|
+
else
|
96
|
+
fill_literal! expr
|
97
|
+
phrase = current_phrase(expr)
|
98
|
+
if aref_or_aset
|
99
|
+
expr.eval_string = expr[0..-2]
|
100
|
+
expr.meth = aref_or_aset
|
101
|
+
elsif phrase.match( /^(.+)\.(.*)$/ )
|
102
|
+
expr.eval_string, expr.meth = $1, $2
|
103
|
+
elsif opchars != ''
|
104
|
+
expr
|
105
|
+
end
|
106
|
+
debugprint "expr.eval_string=#{expr.eval_string}", "expr.meth=#{expr.meth}"
|
107
|
+
phrase
|
108
|
+
end << (opchars || '')) # ` font-lock hack
|
109
|
+
debugprint "expr(after set_last_word)=#{expr}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def current_phrase(expr)
|
113
|
+
paren_level = 0
|
114
|
+
start = 0
|
115
|
+
(expr.length-1).downto(0) do |i|
|
116
|
+
c = expr[i,1]
|
117
|
+
if c =~ /[\)\}\]]/
|
118
|
+
paren_level += 1
|
119
|
+
next
|
120
|
+
end
|
121
|
+
if paren_level > 0
|
122
|
+
next if c =~ /[, ]/
|
123
|
+
else
|
124
|
+
break (start = i+1) if c =~ /[ ,\(\{\[]/
|
125
|
+
end
|
126
|
+
if c =~ /[\(\{\[]/
|
127
|
+
paren_level -= 1
|
128
|
+
break (start = i+1) if paren_level < 0
|
129
|
+
end
|
130
|
+
end
|
131
|
+
expr[start..-1]
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_BEGIN
|
135
|
+
<<XXX
|
136
|
+
BEGIN {
|
137
|
+
class Object
|
138
|
+
def method_missing(meth, *args, &block)
|
139
|
+
# ignore NoMethodError
|
140
|
+
end
|
141
|
+
end
|
142
|
+
}
|
143
|
+
XXX
|
144
|
+
end
|
145
|
+
|
146
|
+
class RuntimeDataError < RuntimeError; end
|
147
|
+
class NewCodeError < Exception; end
|
148
|
+
def runtime_data_with_class(code, lineno, column=nil)
|
149
|
+
newcode = code.to_a.enum_with_index.map{|line, i|
|
150
|
+
i+1==lineno ? prepare_line(line.chomp, column) : line
|
151
|
+
}.join
|
152
|
+
newcode << add_BEGIN if @ignore_NoMethodError
|
153
|
+
debugprint "newcode", newcode, "-"*80
|
154
|
+
stdout, stderr = execute(newcode)
|
155
|
+
output = stderr.readlines
|
156
|
+
debugprint "stdout", output, "-"*80
|
157
|
+
output = output.reject{|x| /^-:[0-9]+: warning/.match(x)}
|
158
|
+
runtime_data = extract_data(output)
|
159
|
+
if exception = /^-:[0-9]+:.*/m.match(output.join)
|
160
|
+
raise NewCodeError, exception[0].chomp
|
161
|
+
end
|
162
|
+
begin
|
163
|
+
dat = runtime_data.results[1][0]
|
164
|
+
[dat[0], dat[1..-1].to_s]
|
165
|
+
rescue
|
166
|
+
raise RuntimeDataError, runtime_data.inspect
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def runtime_data(code, lineno, column=nil)
|
171
|
+
runtime_data_with_class(code, lineno, column)[1]
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
# Nearly 100% accurate completion for any editors!!
|
177
|
+
# by rubikitch <rubikitch@ruby-lang.org>
|
178
|
+
class XMPCompletionFilter < XMPFilter
|
179
|
+
include ProcessParticularLine
|
180
|
+
|
181
|
+
# String completion begins with this.
|
182
|
+
attr :prefix
|
183
|
+
|
184
|
+
def self.run(code, opts)
|
185
|
+
new(opts).completion_code(code, opts[:lineno], opts[:column])
|
186
|
+
end
|
187
|
+
|
188
|
+
def prepare_line(expr, column)
|
189
|
+
set_expr_and_postfix!(expr, column){|c| /^.{#{c}}/ }
|
190
|
+
@prefix = expr
|
191
|
+
case expr
|
192
|
+
when /^\$\w+$/ # global variable
|
193
|
+
__prepare_line 'global_variables'
|
194
|
+
when /^@@\w+$/ # class variable
|
195
|
+
__prepare_line 'Module === self ? class_variables : self.class.class_variables'
|
196
|
+
when /^@\w+$/ # instance variable
|
197
|
+
__prepare_line 'instance_variables'
|
198
|
+
when /^([A-Z].*)::(.*)$/ # nested constants / class methods
|
199
|
+
@prefix = $2
|
200
|
+
__prepare_line "#$1.constants | #$1.methods(true)"
|
201
|
+
when /^[A-Z]\w*$/ # normal constants
|
202
|
+
__prepare_line 'Module.constants'
|
203
|
+
when /^::(.+)::(.*)$/ # toplevel nested constants
|
204
|
+
@prefix = $2
|
205
|
+
__prepare_line "::#$1.constants | ::#$1.methods"
|
206
|
+
when /^::(.*)/ # toplevel constant
|
207
|
+
@prefix = $1
|
208
|
+
__prepare_line 'Object.constants'
|
209
|
+
when /^(:[^:.]*)$/ # symbol
|
210
|
+
__prepare_line 'Symbol.all_symbols.map{|s| ":" + s.id2name}'
|
211
|
+
when /\.([^.]*)$/ # method call
|
212
|
+
@prefix = $1
|
213
|
+
__prepare_line "(#{Regexp.last_match.pre_match}).methods(true)"
|
214
|
+
else # bare words
|
215
|
+
__prepare_line "methods | private_methods | local_variables | self.class.constants"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def __prepare_line(all_completion_expr)
|
220
|
+
v = "#{VAR}"
|
221
|
+
idx = 1
|
222
|
+
oneline_ize(<<EOC)
|
223
|
+
#{v} = (#{all_completion_expr}).grep(/^#{Regexp.quote(@prefix)}/)
|
224
|
+
$stderr.puts("#{MARKER}[#{idx}] => " + #{v}.class.to_s + " " + #{v}.join(" ")) || #{v}
|
225
|
+
exit
|
226
|
+
EOC
|
227
|
+
end
|
228
|
+
|
229
|
+
# Array of completion candidates.
|
230
|
+
def candidates(code, lineno, column=nil)
|
231
|
+
methods = runtime_data(code, lineno, column) rescue ""
|
232
|
+
methods.split.sort
|
233
|
+
end
|
234
|
+
|
235
|
+
# Completion code for editors.
|
236
|
+
def completion_code(code, lineno, column=nil)
|
237
|
+
candidates(code, lineno, column).join("\n")
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class XMPCompletionEmacsFilter < XMPCompletionFilter
|
242
|
+
def completion_code(code, lineno, column=nil)
|
243
|
+
elisp = "(progn\n"
|
244
|
+
elisp << "(setq rct-method-completion-table '("
|
245
|
+
begin
|
246
|
+
candidates(code, lineno, column).each do |meth|
|
247
|
+
elisp << format('("%s") ', meth)
|
248
|
+
end
|
249
|
+
rescue Exception => err
|
250
|
+
return %Q[(error "#{err.message}")]
|
251
|
+
end
|
252
|
+
elisp << "))\n"
|
253
|
+
elisp << %Q[(setq pattern "#{prefix}")\n]
|
254
|
+
elisp << %Q[(try-completion pattern rct-method-completion-table nil)\n]
|
255
|
+
elisp << ")" # /progn
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class XMPCompletionEmacsIciclesFilter < XMPCompletionFilter
|
260
|
+
def candidates(code, lineno, column=nil)
|
261
|
+
klass, methods = runtime_data_with_class(code, lineno, column) rescue ["", ""]
|
262
|
+
@klass = klass
|
263
|
+
methods.split.sort
|
264
|
+
end
|
265
|
+
|
266
|
+
def completion_code(code, lineno, column=nil)
|
267
|
+
elisp = "(progn\n"
|
268
|
+
elisp << "(setq rct-method-completion-table '("
|
269
|
+
begin
|
270
|
+
candidates(code, lineno, column).each do |meth|
|
271
|
+
elisp << format('("%s") ', meth)
|
272
|
+
end
|
273
|
+
rescue Exception => err
|
274
|
+
return %Q[(error "#{err.message}")]
|
275
|
+
end
|
276
|
+
elisp << "))\n"
|
277
|
+
elisp << %Q[(setq pattern "#{prefix}")\n]
|
278
|
+
elisp << %Q[(setq klass "#{@klass}")\n]
|
279
|
+
elisp << ")" # /progn
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|