fastri 0.1.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/COPYING +340 -0
- data/LEGAL +4 -0
- data/LICENSE +56 -0
- data/README.en +93 -0
- data/Rakefile +95 -0
- data/bin/fastri-server +188 -0
- data/bin/fri +67 -0
- data/bin/ri-emacs +188 -0
- data/lib/fastri/ri_index.rb +566 -0
- data/lib/fastri/ri_service.rb +264 -0
- data/lib/fastri/version.rb +8 -0
- data/setup.rb +1585 -0
- data/test/test_functional_ri_service.rb +60 -0
- data/test/test_ri_index.rb +267 -0
- metadata +88 -0
@@ -0,0 +1,566 @@
|
|
1
|
+
# Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org>
|
2
|
+
#
|
3
|
+
|
4
|
+
require 'rdoc/ri/ri_cache'
|
5
|
+
require 'rdoc/ri/ri_reader'
|
6
|
+
require 'fastri/version'
|
7
|
+
|
8
|
+
|
9
|
+
module FastRI
|
10
|
+
|
11
|
+
# This class provides the same functionality as RiReader, with some
|
12
|
+
# improvements:
|
13
|
+
# * lower memory consumption
|
14
|
+
# * ability to handle information from different sources separately.
|
15
|
+
#
|
16
|
+
# Some operations can be restricted to a given "scope", that is, a
|
17
|
+
# "RI DB directory". This allows you to e.g. look for all the instance methods
|
18
|
+
# in String defined by a package.
|
19
|
+
#
|
20
|
+
# Such operations take a +scope+ argument, which is either an integer which
|
21
|
+
# indexes the source in #paths, or a name identifying the source (either
|
22
|
+
# "system" or a package name). If <tt>scope == nil</tt>, the information from
|
23
|
+
# all sources is merged.
|
24
|
+
class RiIndex
|
25
|
+
# Redefine RI::MethodEntry#full_name to use the following notation:
|
26
|
+
# Namespace::Foo.singleton_method (instead of ::). RiIndex depends on this to
|
27
|
+
# tell singleton methods apart.
|
28
|
+
class ::RI::MethodEntry # :nodoc:
|
29
|
+
remove_method :full_name
|
30
|
+
def full_name
|
31
|
+
res = @in_class.full_name
|
32
|
+
unless res.empty?
|
33
|
+
if @is_class_method
|
34
|
+
res << "."
|
35
|
+
else
|
36
|
+
res << "#"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
res << @name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class MethodEntry
|
44
|
+
attr_reader :full_name, :name, :index, :source_index
|
45
|
+
|
46
|
+
def initialize(ri_index, fullname, index, source_index)
|
47
|
+
# index is the index in ri_index' array
|
48
|
+
# source_index either nil (all scopes) or the integer referencing the
|
49
|
+
# path (-> we'll do @ri_index.paths[@source_index])
|
50
|
+
@ri_index = ri_index
|
51
|
+
@full_name = fullname
|
52
|
+
@name = fullname[/[.#](.*)$/, 1]
|
53
|
+
@index = index
|
54
|
+
@source_index = source_index
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the "fully resolved" file name of the yaml containing our
|
58
|
+
# description.
|
59
|
+
def path_name
|
60
|
+
prefix = @full_name.split(/::|[#.]/)[0..-2]
|
61
|
+
case @source_index
|
62
|
+
when nil
|
63
|
+
## we'd like to do
|
64
|
+
#@ri_index.source_paths_for(self).map do |path|
|
65
|
+
# File.join(File.join(path, *prefix), RI::RiWriter.internal_to_external(@name))
|
66
|
+
#end
|
67
|
+
# but RI doesn't support merging at the method-level, so
|
68
|
+
path = @ri_index.source_paths_for(self).first
|
69
|
+
File.join(File.join(path, *prefix),
|
70
|
+
RI::RiWriter.internal_to_external(@name) +
|
71
|
+
(singleton_method? ? "-c" : "-i" ) + ".yaml")
|
72
|
+
else
|
73
|
+
path = @ri_index.paths[@source_index]
|
74
|
+
File.join(File.join(path, *prefix),
|
75
|
+
RI::RiWriter.internal_to_external(@name) +
|
76
|
+
(singleton_method? ? "-c" : "-i" ) + ".yaml")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def singleton_method?
|
81
|
+
/\.[^:]+$/ =~ @full_name
|
82
|
+
end
|
83
|
+
|
84
|
+
def instance_method?
|
85
|
+
!singleton_method?
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the type of this entry (<tt>:method</tt>).
|
89
|
+
def type
|
90
|
+
:method
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class ClassEntry
|
95
|
+
attr_reader :full_name, :name, :index, :source_index
|
96
|
+
|
97
|
+
def initialize(ri_index, fullname, index, source_index)
|
98
|
+
@ri_index = ri_index
|
99
|
+
@full_name = fullname
|
100
|
+
@name = fullname.split(/::/).last
|
101
|
+
@index = index
|
102
|
+
@source_index = source_index
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns an array of directory names holding the cdesc-Classname.yaml
|
106
|
+
# files.
|
107
|
+
def path_names
|
108
|
+
prefix = @full_name.split(/::/)
|
109
|
+
case @source_index
|
110
|
+
when nil
|
111
|
+
@ri_index.source_paths_for(self).map{|path| File.join(path, *prefix) }
|
112
|
+
else
|
113
|
+
[File.join(@ri_index.paths[@source_index], *prefix)]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns nested classes and modules matching name (non-recursive).
|
118
|
+
def contained_modules_matching(name)
|
119
|
+
@ri_index.namespaces_under(self, false, @source_index).select do |x|
|
120
|
+
x.name[name]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns all nested classes and modules (non-recursive).
|
125
|
+
def classes_and_modules
|
126
|
+
@ri_index.namespaces_under(self, false, @source_index)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns nested class or module named exactly +name+ (non-recursive).
|
130
|
+
def contained_class_named(name)
|
131
|
+
contained_modules_matching(name).find{|x| x.name == name}
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns instance or singleton methods matching name (non-recursive).
|
135
|
+
def methods_matching(name, is_class_method)
|
136
|
+
@ri_index.methods_under(self, false, @source_index).select do |meth|
|
137
|
+
meth.name[name] &&
|
138
|
+
(is_class_method ? meth.singleton_method? : meth.instance_method?)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns instance or singleton methods matching name (recursive).
|
143
|
+
def recursively_find_methods_matching(name, is_class_method)
|
144
|
+
@ri_index.methods_under(self, true, @source_index).select do |meth|
|
145
|
+
meth.name[name] &&
|
146
|
+
(is_class_method ? meth.singleton_method? : meth.instance_method?)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns all methods, both instance and singleton (non-recursive).
|
151
|
+
def all_method_names
|
152
|
+
@ri_index.methods_under(self, false, @source_index).map{|meth| meth.full_name}
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns the type of this entry (<tt>:namespace</tt>).
|
156
|
+
def type
|
157
|
+
:namespace
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class TopLevelEntry < ClassEntry
|
162
|
+
def methods_matching(name, is_class_method)
|
163
|
+
recursively_find_methods_matching(name, is_class_method)
|
164
|
+
end
|
165
|
+
|
166
|
+
def module_named(name)
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
attr_reader :paths
|
172
|
+
|
173
|
+
class << self; private :new end
|
174
|
+
|
175
|
+
def self.new_from_paths(paths = nil)
|
176
|
+
obj = new
|
177
|
+
obj.rebuild_index(paths)
|
178
|
+
obj
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.new_from_IO(anIO)
|
182
|
+
obj = new
|
183
|
+
obj.load(anIO)
|
184
|
+
obj
|
185
|
+
end
|
186
|
+
|
187
|
+
def rebuild_index(paths = nil)
|
188
|
+
@paths = paths || RI::Paths::PATH
|
189
|
+
@gem_names = paths.map do |p|
|
190
|
+
fullp = File.expand_path(p)
|
191
|
+
gemname = nil
|
192
|
+
Gem.path.each do |gempath|
|
193
|
+
re = %r!^#{Regexp.escape(File.expand_path(gempath))}/doc/!
|
194
|
+
if re =~ fullp
|
195
|
+
gemname = fullp.gsub(re,"")[%r{^[^/]+}]
|
196
|
+
break
|
197
|
+
end
|
198
|
+
end
|
199
|
+
gemname ? gemname : "system"
|
200
|
+
end
|
201
|
+
methods = Hash.new{|h,k| h[k] = []}
|
202
|
+
namespaces = methods.clone
|
203
|
+
@paths.each_with_index do |path, source_index|
|
204
|
+
ri_reader = RI::RiReader.new(RI::RiCache.new(path))
|
205
|
+
obtain_classes(ri_reader.top_level_namespace.first).each{|name| namespaces[name] << source_index }
|
206
|
+
obtain_methods(ri_reader.top_level_namespace.first).each{|name| methods[name] << source_index }
|
207
|
+
end
|
208
|
+
@method_array = methods.sort_by{|h,k| h}.map do |name, sources|
|
209
|
+
"#{name} #{sources.map{|x| x.to_s}.join(' ')}"
|
210
|
+
end
|
211
|
+
@namespace_array = namespaces.sort_by{|h,k| h}.map do |name, sources|
|
212
|
+
"#{name} #{sources.map{|x| x.to_s}.join(' ')}"
|
213
|
+
end
|
214
|
+
|
215
|
+
=begin
|
216
|
+
puts "@method_array: #{@method_array.size}"
|
217
|
+
puts "@namespace_array: #{@namespace_array.size}"
|
218
|
+
puts @method_array.inject(0){|s,x| s + x.size}
|
219
|
+
puts @namespace_array.inject(0){|s,x| s + x.size}
|
220
|
+
=end
|
221
|
+
end
|
222
|
+
|
223
|
+
MAGIC = "FastRI index #{FASTRI_INDEX_FORMAT}"
|
224
|
+
# Load the index from the given IO.
|
225
|
+
# It must contain a textual representation generated by #dump.
|
226
|
+
def load(anIO)
|
227
|
+
header = anIO.gets
|
228
|
+
raise "Invalid format." unless header.chomp == MAGIC
|
229
|
+
anIO.gets # discard "Sources:"
|
230
|
+
paths = []
|
231
|
+
gem_names = []
|
232
|
+
until (line = anIO.gets).index("=" * 80) == 0
|
233
|
+
gemname, path = line.strip.split(/\s+/)
|
234
|
+
paths << path
|
235
|
+
gem_names << gemname
|
236
|
+
end
|
237
|
+
anIO.gets # discard "Namespaces:"
|
238
|
+
namespace_array = []
|
239
|
+
until (line = anIO.gets).index("=" * 80) == 0
|
240
|
+
namespace_array << line
|
241
|
+
end
|
242
|
+
anIO.gets # discard "Methods:"
|
243
|
+
method_array = []
|
244
|
+
until (line = anIO.gets).index("=" * 80) == 0
|
245
|
+
method_array << line
|
246
|
+
end
|
247
|
+
@paths = paths
|
248
|
+
@gem_names = gem_names
|
249
|
+
@namespace_array = namespace_array
|
250
|
+
@method_array = method_array
|
251
|
+
end
|
252
|
+
|
253
|
+
# Serializes index to the given IO.
|
254
|
+
def dump(anIO)
|
255
|
+
anIO.puts MAGIC
|
256
|
+
anIO.puts "Sources:"
|
257
|
+
@paths.zip(@gem_names).each{|p,g| anIO.puts "%-30s %s" % [g, p]}
|
258
|
+
anIO.puts "=" * 80
|
259
|
+
anIO.puts "Namespaces:"
|
260
|
+
anIO.puts @namespace_array
|
261
|
+
anIO.puts "=" * 80
|
262
|
+
anIO.puts "Methods:"
|
263
|
+
anIO.puts @method_array
|
264
|
+
anIO.puts "=" * 80
|
265
|
+
end
|
266
|
+
#{{{ RiReader compatibility interface
|
267
|
+
|
268
|
+
# Returns an array with the top level namespace.
|
269
|
+
def top_level_namespace(scope = nil)
|
270
|
+
[TopLevelEntry.new(self, "", -1, scope ? scope_to_sindex(scope) : nil)]
|
271
|
+
end
|
272
|
+
|
273
|
+
# Returns an array of ClassEntry objects whose names match +target+, and
|
274
|
+
# which correspond to the namespaces contained in +namespaces+.
|
275
|
+
# +namespaces+ is an array of ClassEntry objects.
|
276
|
+
def lookup_namespace_in(target, namespaces)
|
277
|
+
result = []
|
278
|
+
namespaces.each do |ns|
|
279
|
+
result.concat(ns.contained_modules_matching(target))
|
280
|
+
end
|
281
|
+
result
|
282
|
+
end
|
283
|
+
|
284
|
+
# Returns the ClassDescription associated to the given +full_name+.
|
285
|
+
def find_class_by_name(full_name, scope = nil)
|
286
|
+
entry = get_entry(@namespace_array, full_name, ClassEntry, scope)
|
287
|
+
return nil unless entry && entry.full_name == full_name
|
288
|
+
get_class(entry)
|
289
|
+
end
|
290
|
+
|
291
|
+
# Returns the MethodDescription associated to the given +full_name+.
|
292
|
+
# Only the first definition is returned when <tt>scope = nil</tt>.
|
293
|
+
def find_method_by_name(full_name, scope = nil)
|
294
|
+
entry = get_entry(@method_array, full_name, MethodEntry, scope)
|
295
|
+
return nil unless entry && entry.full_name == full_name
|
296
|
+
get_method(entry)
|
297
|
+
end
|
298
|
+
|
299
|
+
# Returns an array of MethodEntry objects, corresponding to the methods in
|
300
|
+
# the ClassEntry objects in the +namespaces+ array.
|
301
|
+
def find_methods(name, is_class_method, namespaces)
|
302
|
+
result = []
|
303
|
+
namespaces.each do |ns|
|
304
|
+
result.concat ns.methods_matching(name, is_class_method)
|
305
|
+
end
|
306
|
+
result
|
307
|
+
end
|
308
|
+
|
309
|
+
# Return the MethodDescription for a given MethodEntry
|
310
|
+
# by deserializing the YAML.
|
311
|
+
def get_method(method_entry)
|
312
|
+
path = method_entry.path_name
|
313
|
+
File.open(path) { |f| RI::Description.deserialize(f) }
|
314
|
+
end
|
315
|
+
|
316
|
+
# Return a ClassDescription for a given ClassEntry.
|
317
|
+
def get_class(class_entry)
|
318
|
+
result = nil
|
319
|
+
for path in class_entry.path_names
|
320
|
+
path = RI::RiWriter.class_desc_path(path, class_entry)
|
321
|
+
desc = File.open(path) {|f| RI::Description.deserialize(f) }
|
322
|
+
if result
|
323
|
+
result.merge_in(desc)
|
324
|
+
else
|
325
|
+
result = desc
|
326
|
+
end
|
327
|
+
end
|
328
|
+
result
|
329
|
+
end
|
330
|
+
|
331
|
+
# Return the names of all classes and modules.
|
332
|
+
def full_class_names(scope = nil)
|
333
|
+
all_entries(@namespace_array, scope)
|
334
|
+
end
|
335
|
+
|
336
|
+
# Return the names of all methods.
|
337
|
+
def full_method_names(scope = nil)
|
338
|
+
all_entries(@method_array, scope)
|
339
|
+
end
|
340
|
+
|
341
|
+
# Return a list of all classes, modules, and methods.
|
342
|
+
def all_names(scope = nil)
|
343
|
+
full_class_names(scope).concat(full_method_names(scope))
|
344
|
+
end
|
345
|
+
|
346
|
+
#{{{ New (faster) interface
|
347
|
+
|
348
|
+
# Returns the number of methods in the index.
|
349
|
+
def num_methods
|
350
|
+
@method_array.size
|
351
|
+
end
|
352
|
+
|
353
|
+
# Returns the number of namespaces in the index.
|
354
|
+
def num_namespaces
|
355
|
+
@namespace_array.size
|
356
|
+
end
|
357
|
+
|
358
|
+
# Returns the ClassEntry associated to the given +full_name+.
|
359
|
+
def get_class_entry(full_name, scope = nil)
|
360
|
+
entry = get_entry(@namespace_array, full_name, ClassEntry, scope)
|
361
|
+
return nil unless entry && entry.full_name == full_name
|
362
|
+
entry
|
363
|
+
end
|
364
|
+
|
365
|
+
# Returns the MethodEntry associated to the given +full_name+.
|
366
|
+
def get_method_entry(full_name, scope = nil)
|
367
|
+
entry = get_entry(@method_array, full_name, MethodEntry, scope)
|
368
|
+
return nil unless entry && entry.full_name == full_name
|
369
|
+
entry
|
370
|
+
end
|
371
|
+
|
372
|
+
# Returns array of ClassEntry objects under class_entry_or_name
|
373
|
+
# (either String or ClassEntry) in the hierarchy.
|
374
|
+
def namespaces_under(class_entry_or_name, recursive, scope = nil)
|
375
|
+
namespaces_under_matching(class_entry_or_name, //, recursive, scope)
|
376
|
+
end
|
377
|
+
|
378
|
+
# Returns array of ClassEntry objects under class_entry_or_name (either
|
379
|
+
# String or ClassEntry) in the hierarchy whose +full_name+ matches the given
|
380
|
+
# regexp.
|
381
|
+
def namespaces_under_matching(class_entry_or_name, regexp, recursive, scope = nil)
|
382
|
+
case class_entry_or_name
|
383
|
+
when ClassEntry
|
384
|
+
class_entry = class_entry_or_name
|
385
|
+
when ""
|
386
|
+
class_entry = top_level_namespace(scope)[0]
|
387
|
+
else
|
388
|
+
class_entry = get_entry(@namespace_array, class_entry_or_name, ClassEntry, scope)
|
389
|
+
end
|
390
|
+
return [] unless class_entry
|
391
|
+
ret = []
|
392
|
+
re1, re2 = matching_regexps_namespace(class_entry.full_name)
|
393
|
+
(class_entry.index+1...@namespace_array.size).each do |i|
|
394
|
+
entry = @namespace_array[i]
|
395
|
+
break unless re1 =~ entry
|
396
|
+
next if !recursive && re2 !~ entry
|
397
|
+
full_name = entry[/\S+/]
|
398
|
+
next unless regexp =~ full_name
|
399
|
+
if scope
|
400
|
+
sources = namespace_sources(i)
|
401
|
+
if sources.include?(sindex = scope_to_sindex(scope))
|
402
|
+
ret << ClassEntry.new(self, full_name, i, sindex)
|
403
|
+
end
|
404
|
+
else
|
405
|
+
ret << ClassEntry.new(self, full_name, i, nil)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
ret
|
409
|
+
end
|
410
|
+
|
411
|
+
# Returns array of MethodEntry objects under class_entry_or_name
|
412
|
+
# (either String or ClassEntry) in the hierarchy.
|
413
|
+
def methods_under(class_entry_or_name, recursive, scope = nil)
|
414
|
+
methods_under_matching(class_entry_or_name, //, recursive, scope)
|
415
|
+
end
|
416
|
+
|
417
|
+
# Returns array of MethodEntry objects under class_entry_or_name (either
|
418
|
+
# String or ClassEntry) in the hierarchy whose +full_name+ matches the given
|
419
|
+
# regexp.
|
420
|
+
def methods_under_matching(class_entry_or_name, regexp, recursive, scope = nil)
|
421
|
+
case class_entry_or_name
|
422
|
+
when ClassEntry
|
423
|
+
full_name = class_entry_or_name.full_name
|
424
|
+
else
|
425
|
+
full_name = class_entry_or_name
|
426
|
+
end
|
427
|
+
method_entry = get_entry(@method_array, full_name, MethodEntry)
|
428
|
+
return [] unless method_entry
|
429
|
+
ret = []
|
430
|
+
re1, re2 = matching_regexps_method(full_name)
|
431
|
+
(method_entry.index...@method_array.size).each do |i|
|
432
|
+
entry = @method_array[i]
|
433
|
+
break unless re1 =~ entry
|
434
|
+
next if !recursive && re2 !~ entry
|
435
|
+
full_name = entry[/\S+/]
|
436
|
+
next unless regexp =~ full_name
|
437
|
+
if scope
|
438
|
+
sources = method_sources(i)
|
439
|
+
if sources.include?(sindex = scope_to_sindex(scope))
|
440
|
+
ret << MethodEntry.new(self, full_name, i, sindex)
|
441
|
+
end
|
442
|
+
else
|
443
|
+
ret << MethodEntry.new(self, full_name, i, nil)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
ret
|
447
|
+
end
|
448
|
+
|
449
|
+
# Returns array of Strings corresponding to the base directories of all the
|
450
|
+
# sources fo the given entry_or_name.
|
451
|
+
def source_paths_for(entry_or_name)
|
452
|
+
case entry_or_name
|
453
|
+
when ClassEntry
|
454
|
+
namespace_sources(entry_or_name.index).map{|i| @paths[i] }
|
455
|
+
when MethodEntry
|
456
|
+
method_sources(entry_or_name.index).map{|i| @paths[i]}
|
457
|
+
when nil
|
458
|
+
[]
|
459
|
+
else
|
460
|
+
case entry_or_name
|
461
|
+
when /[#.]\S+/
|
462
|
+
method_entry = get_entry(@method_array, entry_or_name, MethodEntry, nil)
|
463
|
+
source_paths_for(method_entry)
|
464
|
+
else
|
465
|
+
class_entry = get_entry(@namespace_array, entry_or_name, ClassEntry, nil)
|
466
|
+
source_paths_for(class_entry)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
private
|
472
|
+
def namespace_sources(index)
|
473
|
+
@namespace_array[index][/\S+ (.*)/,1].split(/\s+/).map{|x| x.to_i}
|
474
|
+
end
|
475
|
+
|
476
|
+
def method_sources(index)
|
477
|
+
@method_array[index][/\S+ (.*)/,1].split(/\s+/).map{|x| x.to_i}
|
478
|
+
end
|
479
|
+
|
480
|
+
def all_entries(array, scope)
|
481
|
+
if scope
|
482
|
+
wanted_sidx = scope_to_sindex(scope)
|
483
|
+
chosen = array.select{|x| x[/ (.*$)/, 1].split(/\s+/).map{|x| x.to_i}.include? wanted_sidx }
|
484
|
+
else
|
485
|
+
chosen = array
|
486
|
+
end
|
487
|
+
chosen.map{|x| x[/(\S+)/]}
|
488
|
+
end
|
489
|
+
|
490
|
+
def matching_regexps_namespace(prefix)
|
491
|
+
if prefix.empty?
|
492
|
+
[//, /^[^:]+ /]
|
493
|
+
else
|
494
|
+
[/^#{Regexp.escape(prefix)}/, /^#{Regexp.escape(prefix)}(::|[#.])[^:]+ / ]
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
def matching_regexps_method(prefix)
|
499
|
+
if prefix.empty?
|
500
|
+
[//, /^[#.] /] # the second should never match
|
501
|
+
else
|
502
|
+
[/^#{Regexp.escape(prefix)}/, /^#{Regexp.escape(prefix)}([#.])\S+ / ]
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def scope_to_sindex(scope)
|
507
|
+
case scope
|
508
|
+
when Integer
|
509
|
+
scope
|
510
|
+
else
|
511
|
+
@gem_names.index(scope)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def get_entry(array, fullname, klass, scope = nil)
|
516
|
+
index = binary_search(array, fullname)
|
517
|
+
return nil unless index
|
518
|
+
entry = array[index]
|
519
|
+
sources = entry[/\S+ (.*)/,1].split(/\s+/).map{|x| x.to_i}
|
520
|
+
if scope
|
521
|
+
wanted_sidx = scope_to_sindex(scope)
|
522
|
+
return nil unless wanted_sidx
|
523
|
+
return nil unless sources.include?(wanted_sidx)
|
524
|
+
return klass.new(self, entry[/\S+/], index, wanted_sidx)
|
525
|
+
end
|
526
|
+
klass.new(self, entry[/\S+/], index, nil)
|
527
|
+
end
|
528
|
+
|
529
|
+
def binary_search(array, name, from = 0, to = array.size - 1)
|
530
|
+
middle = (from + to) / 2
|
531
|
+
pivot = array[middle][/\S+/]
|
532
|
+
if from == to
|
533
|
+
if pivot.index(name) == 0
|
534
|
+
from
|
535
|
+
else
|
536
|
+
nil
|
537
|
+
end
|
538
|
+
elsif name <= pivot
|
539
|
+
binary_search(array, name, from, middle)
|
540
|
+
elsif name > pivot
|
541
|
+
binary_search(array, name, middle+1, to)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
def obtain_classes(namespace, res = [])
|
546
|
+
subnamespaces = namespace.classes_and_modules
|
547
|
+
subnamespaces.each do |ns|
|
548
|
+
res << ns.full_name
|
549
|
+
obtain_classes(ns, res)
|
550
|
+
end
|
551
|
+
res
|
552
|
+
end
|
553
|
+
|
554
|
+
def obtain_methods(namespace, res = [])
|
555
|
+
subnamespaces = namespace.classes_and_modules
|
556
|
+
subnamespaces.each do |ns|
|
557
|
+
res.concat ns.all_method_names
|
558
|
+
obtain_methods(ns, res)
|
559
|
+
end
|
560
|
+
res
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
end #module FastRI
|
565
|
+
|
566
|
+
# vi: set sw=2 expandtab:
|