1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2005 - 2015 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 =begin edoc |
|
7 File implementing a command line completer class. |
|
8 =end |
|
9 |
|
10 # |
|
11 # This code is mostly based on the completer found in irb of the Ruby package |
|
12 # Original copyright |
|
13 # by Keiju ISHITSUKA(keiju@ishitsuka.com) |
|
14 # From Original Idea of shugo@ruby-lang.org |
|
15 # |
|
16 |
|
17 class Completer |
|
18 =begin edoc |
|
19 Class implementing a command completer. |
|
20 =end |
|
21 ReservedWords = [ |
|
22 "BEGIN", "END", |
|
23 "alias", "and", |
|
24 "begin", "break", |
|
25 "case", "class", |
|
26 "def", "defined", "do", |
|
27 "else", "elsif", "end", "ensure", |
|
28 "false", "for", |
|
29 "if", "in", |
|
30 "module", |
|
31 "next", "nil", "not", |
|
32 "or", |
|
33 "redo", "rescue", "retry", "return", |
|
34 "self", "super", |
|
35 "then", "true", |
|
36 "undef", "unless", "until", |
|
37 "when", "while", |
|
38 "yield", |
|
39 ] |
|
40 |
|
41 def initialize(binding) |
|
42 =begin edoc |
|
43 constructor |
|
44 |
|
45 @param binding binding object used to determine the possible completions |
|
46 =end |
|
47 @binding = binding |
|
48 end |
|
49 |
|
50 def complete(input) |
|
51 =begin edoc |
|
52 Public method to select the possible completions |
|
53 |
|
54 @param input text to be completed (String) |
|
55 @return list of possible completions (Array) |
|
56 =end |
|
57 case input |
|
58 when /^((["'`]).*\2)\.([^.]*)$/ |
|
59 # String |
|
60 receiver = $1 |
|
61 message = $3 |
|
62 |
|
63 candidates = String.instance_methods.collect{|m| m.to_s} |
|
64 select_message(receiver, message, candidates) |
|
65 |
|
66 when /^(\/[^\/]*\/)\.([^.]*)$/ |
|
67 # Regexp |
|
68 receiver = $1 |
|
69 message = Regexp.quote($2) |
|
70 |
|
71 candidates = Regexp.instance_methods.collect{|m| m.to_s} |
|
72 select_message(receiver, message, candidates) |
|
73 |
|
74 when /^([^\]]*\])\.([^.]*)$/ |
|
75 # Array |
|
76 receiver = $1 |
|
77 message = Regexp.quote($2) |
|
78 |
|
79 candidates = Array.instance_methods.collect{|m| m.to_s} |
|
80 select_message(receiver, message, candidates) |
|
81 |
|
82 when /^([^\}]*\})\.([^.]*)$/ |
|
83 # Proc or Hash |
|
84 receiver = $1 |
|
85 message = Regexp.quote($2) |
|
86 |
|
87 candidates = Proc.instance_methods.collect{|m| m.to_s} |
|
88 candidates |= Hash.instance_methods.collect{|m| m.to_s} |
|
89 select_message(receiver, message, candidates) |
|
90 |
|
91 when /^(:[^:.]*)$/ |
|
92 # Symbol |
|
93 if Symbol.respond_to?(:all_symbols) |
|
94 sym = $1 |
|
95 candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name} |
|
96 candidates.grep(/^#{sym}/) |
|
97 else |
|
98 [] |
|
99 end |
|
100 |
|
101 when /^::([A-Z][^:\.\(]*)$/ |
|
102 # Absolute Constant or class methods |
|
103 receiver = $1 |
|
104 candidates = Object.constants.collect{|m| m.to_s} |
|
105 candidates.grep(/^#{receiver}/).collect{|e| "::" + e} |
|
106 |
|
107 when /^([A-Z].*)::([^:.]*)$/ |
|
108 # Constant or class methods |
|
109 receiver = $1 |
|
110 message = Regexp.quote($4) |
|
111 begin |
|
112 candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind) |
|
113 candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind) |
|
114 rescue Exception |
|
115 candidates = [] |
|
116 end |
|
117 select_message(receiver, message, candidates, "::") |
|
118 |
|
119 when /^(:[^:.]+)(\.|::)([^.]*)$/ |
|
120 # Symbol |
|
121 receiver = $1 |
|
122 sep = $2 |
|
123 message = Regexp.quote($3) |
|
124 |
|
125 candidates = Symbol.instance_methods.collect{|m| m.to_s} |
|
126 select_message(receiver, message, candidates, sep) |
|
127 |
|
128 when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)(\.|::)([^.]*)$/ |
|
129 # Numeric |
|
130 receiver = $1 |
|
131 sep = $5 |
|
132 message = Regexp.quote($6) |
|
133 |
|
134 begin |
|
135 candidates = eval(receiver, @binding).methods.collect{|m| m.to_s} |
|
136 rescue Exception |
|
137 candidates |
|
138 end |
|
139 select_message(receiver, message, candidates, sep) |
|
140 |
|
141 when /^(-?0x[0-9a-fA-F_]+)(\.|::)([^.]*)$/ |
|
142 # Numeric(0xFFFF) |
|
143 receiver = $1 |
|
144 sep = $2 |
|
145 message = Regexp.quote($3) |
|
146 |
|
147 begin |
|
148 candidates = eval(receiver, bind).methods.collect{|m| m.to_s} |
|
149 rescue Exception |
|
150 candidates = [] |
|
151 end |
|
152 select_message(receiver, message, candidates, sep) |
|
153 |
|
154 when /^(\$[^.]*)$/ |
|
155 # Global variable |
|
156 candidates = global_variables.grep(Regexp.new(Regexp.quote($1))) |
|
157 |
|
158 when /^([^."].*)(\.|::)([^.]*)$/ |
|
159 # variable.func or func.func |
|
160 receiver = $1 |
|
161 sep = $2 |
|
162 message = Regexp.quote($3) |
|
163 |
|
164 gv = eval("global_variables", @binding).collect{|m| m.to_s} |
|
165 lv = eval("local_variables", @binding).collect{|m| m.to_s} |
|
166 cv = eval("self.class.constants", @binding).collect{|m| m.to_s} |
|
167 |
|
168 if (gv | lv | cv).include?(receiver) or \ |
|
169 /^[A-Z]/ =~ receiver && /\./ !~ receiver |
|
170 # foo.func and foo is var. OR |
|
171 # foo::func and foo is var. OR |
|
172 # foo::Const and foo is var. OR |
|
173 # Foo::Bar.func |
|
174 begin |
|
175 candidates = [] |
|
176 rec = eval(receiver, bind) |
|
177 if sep == "::" and rec.kind_of?(Module) |
|
178 candidates = rec.constants.collect{|m| m.to_s} |
|
179 end |
|
180 candidates |= rec.methods.collect{|m| m.to_s} |
|
181 rescue Exception |
|
182 candidates = [] |
|
183 end |
|
184 else |
|
185 # func1.func2 |
|
186 candidates = [] |
|
187 ObjectSpace.each_object(Module){|m| |
|
188 begin |
|
189 name = m.name |
|
190 rescue Exception |
|
191 name = "" |
|
192 end |
|
193 begin |
|
194 next if m.name != "IRB::Context" and |
|
195 /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name |
|
196 rescue Exception |
|
197 next |
|
198 end |
|
199 candidates.concat m.instance_methods(false).collect{|x| x.to_s} |
|
200 } |
|
201 candidates.sort! |
|
202 candidates.uniq! |
|
203 end |
|
204 select_message(receiver, message, candidates, sep) |
|
205 |
|
206 when /^\.([^.]*)$/ |
|
207 # unknown(maybe String) |
|
208 |
|
209 receiver = "" |
|
210 message = Regexp.quote($1) |
|
211 |
|
212 candidates = String.instance_methods(true).collect{|m| m.to_s} |
|
213 select_message(receiver, message, candidates) |
|
214 |
|
215 else |
|
216 candidates = eval( |
|
217 "methods | private_methods | local_variables | self.class.constants", |
|
218 @binding).collect{|m| m.to_s} |
|
219 |
|
220 (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/) |
|
221 end |
|
222 end |
|
223 |
|
224 Operators = ["%", "&", "*", "**", "+", "-", "/", |
|
225 "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>", |
|
226 "[]", "[]=", "^", "!", "!=", "!~"] |
|
227 |
|
228 def select_message(receiver, message, candidates, sep = ".") |
|
229 =begin edoc |
|
230 Method used to pick completion candidates. |
|
231 |
|
232 @param receiver object receiving the message |
|
233 @param message message to be sent to object |
|
234 @param candidates possible completion candidates |
|
235 @param sep separater string |
|
236 @return filtered list of candidates |
|
237 =end |
|
238 candidates.grep(/^#{message}/).collect do |e| |
|
239 case e |
|
240 when /^[a-zA-Z_]/ |
|
241 receiver + sep + e |
|
242 when /^[0-9]/ |
|
243 when *Operators |
|
244 #receiver + " " + e |
|
245 end |
|
246 end |
|
247 end |
|
248 end |
|