dakrone-fastri 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +61 -0
- data/COPYING +340 -0
- data/LEGAL +4 -0
- data/LICENSE +56 -0
- data/README.en +102 -0
- data/Rakefile +26 -0
- data/THANKS +36 -0
- data/bin/fastri-server +251 -0
- data/bin/fri +353 -0
- data/bin/ri-emacs +202 -0
- data/fastri.gemspec +64 -0
- data/indexer.rb +135 -0
- data/lib/fastri/full_text_index.rb +245 -0
- data/lib/fastri/full_text_indexer.rb +100 -0
- data/lib/fastri/name_descriptor.rb +71 -0
- data/lib/fastri/ri_index.rb +601 -0
- data/lib/fastri/ri_service.rb +430 -0
- data/lib/fastri/util.rb +183 -0
- data/lib/fastri/version.rb +13 -0
- data/lookup.rb +197 -0
- data/pre-install.rb +11 -0
- data/setup.rb +1585 -0
- data/test/test_full_text_index.rb +182 -0
- data/test/test_full_text_indexer.rb +84 -0
- data/test/test_functional_ri_service.rb +60 -0
- data/test/test_integration_full_text_index.rb +43 -0
- data/test/test_name_descriptor.rb +35 -0
- data/test/test_ri_index.rb +389 -0
- data/test/test_util.rb +91 -0
- metadata +84 -0
@@ -0,0 +1,430 @@
|
|
1
|
+
# Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org>
|
2
|
+
#
|
3
|
+
# Inspired by ri-emacs.rb by Kristof Bastiaensen <kristof@vleeuwen.org>
|
4
|
+
|
5
|
+
require 'rdoc/ri/paths'
|
6
|
+
require 'rdoc/ri/util'
|
7
|
+
require 'rdoc/ri/formatter'
|
8
|
+
require 'rdoc/ri/display'
|
9
|
+
|
10
|
+
require 'fastri/ri_index.rb'
|
11
|
+
require 'fastri/name_descriptor'
|
12
|
+
|
13
|
+
|
14
|
+
module FastRI
|
15
|
+
|
16
|
+
class RiError < Exception; end
|
17
|
+
|
18
|
+
class ::DefaultDisplay
|
19
|
+
def full_params(method)
|
20
|
+
method.params.split(/\n/).each do |p|
|
21
|
+
p.sub!(/^#{method.name}\(/o,'(')
|
22
|
+
unless p =~ /\b\.\b/
|
23
|
+
p = method.full_name + p
|
24
|
+
end
|
25
|
+
@formatter.wrap(p)
|
26
|
+
@formatter.break_to_newline
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class StringRedirectedDisplay < ::RDoc::RI::DefaultDisplay
|
32
|
+
attr_reader :stringio, :formatter
|
33
|
+
def initialize(*args)
|
34
|
+
super(*args)
|
35
|
+
reset_stringio
|
36
|
+
end
|
37
|
+
|
38
|
+
def puts(*a)
|
39
|
+
@stringio.puts(*a)
|
40
|
+
end
|
41
|
+
|
42
|
+
def print(*a)
|
43
|
+
@stringio.print(*a)
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset_stringio
|
47
|
+
@stringio = StringIO.new("")
|
48
|
+
@formatter.stringio = @stringio
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class ::RDoc::RI::TextFormatter
|
53
|
+
def puts(*a); @stringio.puts(*a) end
|
54
|
+
def print(*a); @stringio.print(*a) end
|
55
|
+
end
|
56
|
+
|
57
|
+
module FormatterRedirection
|
58
|
+
attr_accessor :stringio
|
59
|
+
def initialize(*options)
|
60
|
+
@stringio = StringIO.new("")
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class RedirectedAnsiFormatter < ::RDoc::RI::AnsiFormatter
|
66
|
+
include FormatterRedirection
|
67
|
+
end
|
68
|
+
|
69
|
+
class RedirectedTextFormatter < ::RDoc::RI::TextFormatter
|
70
|
+
include FormatterRedirection
|
71
|
+
end
|
72
|
+
|
73
|
+
class RiService
|
74
|
+
|
75
|
+
class MatchFinder
|
76
|
+
def self.new
|
77
|
+
ret = super
|
78
|
+
yield ret if block_given?
|
79
|
+
ret
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize
|
83
|
+
@matchers = {}
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_matcher(name, &block)
|
87
|
+
@matchers[name] = block
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_matches(methods)
|
91
|
+
catch(:MatchFinder_return) do
|
92
|
+
methods.each do |name|
|
93
|
+
matcher = @matchers[name]
|
94
|
+
matcher.call(self) if matcher
|
95
|
+
end
|
96
|
+
[]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def yield(matches)
|
101
|
+
case matches
|
102
|
+
when nil, []; nil
|
103
|
+
when Array
|
104
|
+
throw :MatchFinder_return, matches
|
105
|
+
else
|
106
|
+
throw :MatchFinder_return, [matches]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end # MatchFinder
|
110
|
+
|
111
|
+
|
112
|
+
Options = Struct.new(:formatter, :use_stdout, :width)
|
113
|
+
|
114
|
+
def initialize(ri_reader)
|
115
|
+
@ri_reader = ri_reader
|
116
|
+
end
|
117
|
+
|
118
|
+
DEFAULT_OBTAIN_ENTRIES_OPTIONS = {
|
119
|
+
:lookup_order => [
|
120
|
+
:exact, :exact_ci, :nested, :nested_ci, :partial, :partial_ci,
|
121
|
+
:nested_partial, :nested_partial_ci,
|
122
|
+
],
|
123
|
+
}
|
124
|
+
def obtain_entries(descriptor, options = {})
|
125
|
+
options = DEFAULT_OBTAIN_ENTRIES_OPTIONS.merge(options)
|
126
|
+
if descriptor.class_names.empty?
|
127
|
+
seps = separators(descriptor.is_class_method)
|
128
|
+
return obtain_unqualified_method_entries(descriptor.method_name, seps,
|
129
|
+
options[:lookup_order])
|
130
|
+
end
|
131
|
+
|
132
|
+
# if we're here, some namespace was given
|
133
|
+
full_ns_name = descriptor.class_names.join("::")
|
134
|
+
if descriptor.method_name == nil
|
135
|
+
return obtain_namespace_entries(full_ns_name, options[:lookup_order])
|
136
|
+
else # both namespace and method
|
137
|
+
seps = separators(descriptor.is_class_method)
|
138
|
+
return obtain_qualified_method_entries(full_ns_name, descriptor.method_name,
|
139
|
+
seps, options[:lookup_order])
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def completion_list(keyw)
|
144
|
+
return @ri_reader.full_class_names if keyw == ""
|
145
|
+
|
146
|
+
descriptor = NameDescriptor.new(keyw)
|
147
|
+
|
148
|
+
if descriptor.class_names.empty?
|
149
|
+
# try partial matches
|
150
|
+
meths = @ri_reader.methods_under_matching("", /(#|\.)#{descriptor.method_name}/, true)
|
151
|
+
ret = meths.map{|x| x.name}.uniq.sort
|
152
|
+
return ret.empty? ? nil : ret
|
153
|
+
end
|
154
|
+
|
155
|
+
# if we're here, some namespace was given
|
156
|
+
full_ns_name = descriptor.class_names.join("::")
|
157
|
+
if descriptor.method_name == nil && ! [?#, ?:, ?.].include?(keyw[-1])
|
158
|
+
# partial match
|
159
|
+
namespaces = @ri_reader.namespaces_under_matching("", /^#{full_ns_name}/, false)
|
160
|
+
ret = namespaces.map{|x| x.full_name}.uniq.sort
|
161
|
+
return ret.empty? ? nil : ret
|
162
|
+
else
|
163
|
+
if [?#, ?:, ?.].include?(keyw[-1])
|
164
|
+
seps = case keyw[-1]
|
165
|
+
when ?#; %w[#]
|
166
|
+
when ?:; %w[.]
|
167
|
+
when ?.; %w[. #]
|
168
|
+
end
|
169
|
+
else # both namespace and method
|
170
|
+
seps = separators(descriptor.is_class_method)
|
171
|
+
end
|
172
|
+
sep_re = "(" + seps.map{|x| Regexp.escape(x)}.join("|") + ")"
|
173
|
+
# partial
|
174
|
+
methods = @ri_reader.methods_under_matching(full_ns_name, /#{sep_re}#{descriptor.method_name}/, false)
|
175
|
+
ret = methods.map{|x| x.full_name}.uniq.sort
|
176
|
+
return ret.empty? ? nil : ret
|
177
|
+
end
|
178
|
+
rescue RiError
|
179
|
+
return nil
|
180
|
+
end
|
181
|
+
|
182
|
+
DEFAULT_INFO_OPTIONS = {
|
183
|
+
:formatter => :ansi,
|
184
|
+
:width => 72,
|
185
|
+
:extended => false,
|
186
|
+
:expand_choices => false,
|
187
|
+
}
|
188
|
+
|
189
|
+
def matches(keyword, options = {})
|
190
|
+
options = DEFAULT_INFO_OPTIONS.merge(options)
|
191
|
+
return nil if keyword.strip.empty?
|
192
|
+
descriptor = NameDescriptor.new(keyword)
|
193
|
+
ret = obtain_entries(descriptor, options).map{|x| x.full_name}
|
194
|
+
ret ? ret : nil
|
195
|
+
rescue RiError
|
196
|
+
return nil
|
197
|
+
end
|
198
|
+
|
199
|
+
def info(keyw, options = {})
|
200
|
+
options = DEFAULT_INFO_OPTIONS.merge(options)
|
201
|
+
return nil if keyw.strip.empty?
|
202
|
+
descriptor = NameDescriptor.new(keyw)
|
203
|
+
entries = obtain_entries(descriptor, options)
|
204
|
+
|
205
|
+
case entries.size
|
206
|
+
when 0; nil
|
207
|
+
when 1
|
208
|
+
case entries[0].type
|
209
|
+
when :namespace
|
210
|
+
capture_stdout(display(options)) do |display|
|
211
|
+
#display.display_class_info(@ri_reader.get_class(entries[0]), @ri_reader)
|
212
|
+
display.display_class_info(@ri_reader.get_class(entries[0]))
|
213
|
+
if options[:extended]
|
214
|
+
methods = @ri_reader.methods_under(entries[0], true)
|
215
|
+
methods.each do |meth_entry|
|
216
|
+
display.display_method_info(@ri_reader.get_method(meth_entry))
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
when :method
|
221
|
+
capture_stdout(display(options)) do |display|
|
222
|
+
display.display_method_info(@ri_reader.get_method(entries[0]))
|
223
|
+
end
|
224
|
+
end
|
225
|
+
else
|
226
|
+
capture_stdout(display(options)) do |display|
|
227
|
+
formatter = display.formatter
|
228
|
+
formatter.draw_line("Multiple choices:")
|
229
|
+
formatter.blankline
|
230
|
+
formatter.wrap(entries.map{|x| x.full_name}.join(", "))
|
231
|
+
entries.each do |entry|
|
232
|
+
display.display_method_info(@ri_reader.get_method(entry)) if entry.type == :method
|
233
|
+
end if options[:expand_choices]
|
234
|
+
end
|
235
|
+
end
|
236
|
+
rescue RiError
|
237
|
+
return nil
|
238
|
+
end
|
239
|
+
|
240
|
+
def args(keyword, options = {})
|
241
|
+
options = DEFAULT_INFO_OPTIONS.merge(options)
|
242
|
+
return nil if keyword.strip.empty?
|
243
|
+
descriptor = NameDescriptor.new(keyword)
|
244
|
+
entries = obtain_entries(descriptor, options)
|
245
|
+
return nil if entries.empty? || RiIndex::ClassEntry === entries[0]
|
246
|
+
|
247
|
+
params_text = ""
|
248
|
+
entries.each do |entry|
|
249
|
+
desc = @ri_reader.get_method(entry)
|
250
|
+
params_text << capture_stdout(display(options)) do |display|
|
251
|
+
display.full_params(desc)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
params_text
|
255
|
+
rescue RiError
|
256
|
+
return nil
|
257
|
+
end
|
258
|
+
|
259
|
+
# Returns a list with the names of the modules/classes that define the given
|
260
|
+
# method, or +nil+.
|
261
|
+
def class_list(keyword)
|
262
|
+
_class_list(keyword, '\1')
|
263
|
+
end
|
264
|
+
|
265
|
+
# Returns a list with the names of the modules/classes that define the given
|
266
|
+
# method, followed by a flag (#|::), or +nil+.
|
267
|
+
# e.g. ["Array#", "IO#", "IO::", ... ]
|
268
|
+
def class_list_with_flag(keyword)
|
269
|
+
r = _class_list(keyword, '\1\2')
|
270
|
+
r ? r.map{|x| x.gsub(/\./, "::")} : nil
|
271
|
+
end
|
272
|
+
|
273
|
+
# Return array of strings with the names of all known methods.
|
274
|
+
def all_methods
|
275
|
+
@ri_reader.full_method_names
|
276
|
+
end
|
277
|
+
|
278
|
+
# Return array of strings with the names of all known classes.
|
279
|
+
def all_classes
|
280
|
+
@ri_reader.full_class_names
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
def obtain_unqualified_method_entries(name, separators, order)
|
286
|
+
name = Regexp.escape(name)
|
287
|
+
sep_re = "(" + separators.map{|x| Regexp.escape(x)}.join("|") + ")"
|
288
|
+
matcher = MatchFinder.new do |m|
|
289
|
+
m.add_matcher(:exact) do
|
290
|
+
m.yield @ri_reader.methods_under_matching("", /#{sep_re}#{name}$/, true)
|
291
|
+
end
|
292
|
+
m.add_matcher(:exact_ci) do
|
293
|
+
m.yield @ri_reader.methods_under_matching("", /#{sep_re}#{name}$/i, true)
|
294
|
+
end
|
295
|
+
m.add_matcher(:partial) do
|
296
|
+
m.yield @ri_reader.methods_under_matching("", /#{sep_re}#{name}/, true)
|
297
|
+
end
|
298
|
+
m.add_matcher(:partial_ci) do
|
299
|
+
m.yield @ri_reader.methods_under_matching("", /#{sep_re}#{name}/i, true)
|
300
|
+
end
|
301
|
+
m.add_matcher(:anywhere) do
|
302
|
+
m.yield @ri_reader.methods_under_matching("", /#{sep_re}.*#{name}/, true)
|
303
|
+
end
|
304
|
+
m.add_matcher(:anywhere_ci) do
|
305
|
+
m.yield @ri_reader.methods_under_matching("", /#{sep_re}.*#{name}/i, true)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
matcher.get_matches(order)
|
309
|
+
end
|
310
|
+
|
311
|
+
def obtain_qualified_method_entries(namespace, method, separators, order)
|
312
|
+
namespace, unescaped_namespace = Regexp.escape(namespace), namespace
|
313
|
+
method = Regexp.escape(method)
|
314
|
+
matcher = MatchFinder.new do |m|
|
315
|
+
m.add_matcher(:exact) do
|
316
|
+
separators.each do |sep|
|
317
|
+
m.yield @ri_reader.get_method_entry("#{namespace}#{sep}#{method}")
|
318
|
+
end
|
319
|
+
end
|
320
|
+
sep_re = "(" + separators.map{|x| Regexp.escape(x)}.join("|") + ")"
|
321
|
+
m.add_matcher(:exact_ci) do
|
322
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}#{sep_re}#{method}$/i, true)
|
323
|
+
end
|
324
|
+
m.add_matcher(:nested) do
|
325
|
+
m.yield @ri_reader.methods_under_matching("", /::#{namespace}#{sep_re}#{method}$/, true)
|
326
|
+
end
|
327
|
+
m.add_matcher(:nested_ci) do
|
328
|
+
m.yield @ri_reader.methods_under_matching("", /::#{namespace}#{sep_re}#{method}$/i, true)
|
329
|
+
end
|
330
|
+
m.add_matcher(:partial) do
|
331
|
+
m.yield @ri_reader.methods_under_matching(unescaped_namespace, /#{sep_re}#{method}/, false)
|
332
|
+
end
|
333
|
+
m.add_matcher(:partial_ci) do
|
334
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}#{sep_re}#{method}/i, true)
|
335
|
+
end
|
336
|
+
m.add_matcher(:nested_partial) do
|
337
|
+
m.yield @ri_reader.methods_under_matching("", /::#{namespace}#{sep_re}#{method}/, true)
|
338
|
+
end
|
339
|
+
m.add_matcher(:nested_partial_ci) do
|
340
|
+
m.yield @ri_reader.methods_under_matching("", /::#{namespace}#{sep_re}#{method}/i, true)
|
341
|
+
end
|
342
|
+
m.add_matcher(:namespace_partial) do
|
343
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}$/, true)
|
344
|
+
end
|
345
|
+
m.add_matcher(:namespace_partial_ci) do
|
346
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}$/i, true)
|
347
|
+
end
|
348
|
+
m.add_matcher(:full_partial) do
|
349
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}/, true)
|
350
|
+
end
|
351
|
+
m.add_matcher(:full_partial_ci) do
|
352
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}/i, true)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
matcher.get_matches(order)
|
356
|
+
end
|
357
|
+
|
358
|
+
def obtain_namespace_entries(name, order)
|
359
|
+
name = Regexp.escape(name)
|
360
|
+
matcher = MatchFinder.new do |m|
|
361
|
+
m.add_matcher(:exact){ m.yield @ri_reader.get_class_entry(name) }
|
362
|
+
m.add_matcher(:exact_ci) do
|
363
|
+
m.yield @ri_reader.namespaces_under_matching("", /^#{name}$/i, true)
|
364
|
+
end
|
365
|
+
m.add_matcher(:nested) do
|
366
|
+
m.yield @ri_reader.namespaces_under_matching("", /::#{name}$/, true)
|
367
|
+
end
|
368
|
+
m.add_matcher(:nested_ci) do
|
369
|
+
m.yield @ri_reader.namespaces_under_matching("", /::#{name}$/i, true)
|
370
|
+
end
|
371
|
+
m.add_matcher(:partial) do
|
372
|
+
m.yield @ri_reader.namespaces_under_matching("", /^#{name}/, true)
|
373
|
+
end
|
374
|
+
m.add_matcher(:partial_ci) do
|
375
|
+
m.yield @ri_reader.namespaces_under_matching("", /^#{name}/i, true)
|
376
|
+
end
|
377
|
+
m.add_matcher(:nested_partial) do
|
378
|
+
m.yield @ri_reader.namespaces_under_matching("", /::#{name}[^:]*$/, true)
|
379
|
+
end
|
380
|
+
m.add_matcher(:nested_partial_ci) do
|
381
|
+
m.yield @ri_reader.namespaces_under_matching("", /::#{name}[^:]*$/i, true)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
matcher.get_matches(order)
|
385
|
+
end
|
386
|
+
|
387
|
+
def _class_list(keyword, rep)
|
388
|
+
return nil if keyword.strip.empty?
|
389
|
+
entries = @ri_reader.methods_under_matching("", /#{keyword}$/, true)
|
390
|
+
return nil if entries.empty?
|
391
|
+
|
392
|
+
entries.map{|entry| entry.full_name.sub(/(.*)(#|\.).*/, rep) }.uniq
|
393
|
+
rescue RiError
|
394
|
+
return nil
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
def separators(is_class_method)
|
399
|
+
case is_class_method
|
400
|
+
when true; ["."]
|
401
|
+
when false; ["#"]
|
402
|
+
when nil; [".","#"]
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
DEFAULT_DISPLAY_OPTIONS = {
|
407
|
+
:formatter => :ansi,
|
408
|
+
:width => 72,
|
409
|
+
}
|
410
|
+
def display(opt = {})
|
411
|
+
opt = DEFAULT_DISPLAY_OPTIONS.merge(opt)
|
412
|
+
options = Options.new
|
413
|
+
options.use_stdout = true
|
414
|
+
case opt[:formatter].to_sym
|
415
|
+
when :ansi
|
416
|
+
options.formatter = RedirectedAnsiFormatter
|
417
|
+
else
|
418
|
+
options.formatter = RedirectedTextFormatter
|
419
|
+
end
|
420
|
+
options.width = opt[:width]
|
421
|
+
StringRedirectedDisplay.new(options.formatter, options.width, options.use_stdout)
|
422
|
+
end
|
423
|
+
|
424
|
+
def capture_stdout(display)
|
425
|
+
yield display
|
426
|
+
display.stringio.string
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
end # module FastRI
|
data/lib/fastri/util.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
# Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org>
|
2
|
+
|
3
|
+
# emulate rubygems.rb and define Gem.path if not loaded
|
4
|
+
# This is much faster than requiring rubygems.rb, which loads way too much
|
5
|
+
# stuff.
|
6
|
+
unless defined? ::Gem
|
7
|
+
require 'rbconfig'
|
8
|
+
module Gem
|
9
|
+
def self.path
|
10
|
+
[ENV['GEM_HOME'], ENV['GEM_PATH'], default_dir].compact.flatten
|
11
|
+
end
|
12
|
+
def self.default_dir
|
13
|
+
if defined? RUBY_FRAMEWORK_VERSION
|
14
|
+
paths = []
|
15
|
+
paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
|
16
|
+
path = File.join(File.dirname(Config::CONFIG["sitedir"]), "Gems")
|
17
|
+
newpath = File.join(path, Config::CONFIG['ruby_version'])
|
18
|
+
# RubyGems post r1498 appends the ruby version to the path. This
|
19
|
+
# modification was included in the RubyGems shipped with 10.5.0.
|
20
|
+
if File.directory?(newpath)
|
21
|
+
# try new path first, user might have upgraded RubyGems and left old
|
22
|
+
# installation behind
|
23
|
+
paths + [ newpath ]
|
24
|
+
else
|
25
|
+
# pre-10.5.0 or older RubyGems
|
26
|
+
paths + [ path ]
|
27
|
+
end
|
28
|
+
else
|
29
|
+
[ File.join(Config::CONFIG['libdir'], 'ruby', 'gems',
|
30
|
+
Config::CONFIG['ruby_version']) ]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# don't let rdoc/ri/ri_paths load rubygems.rb, that takes ~100ms !
|
36
|
+
emulation = $".all?{|x| /rubygems\.rb$/ !~ x} # 1.9 compatibility
|
37
|
+
$".unshift "rubygems.rb" if emulation
|
38
|
+
require 'rdoc/ri/paths'
|
39
|
+
$".delete "rubygems.rb" if emulation
|
40
|
+
require 'rdoc/ri/writer'
|
41
|
+
|
42
|
+
module FastRI
|
43
|
+
module Util
|
44
|
+
# Return an array of <tt>[name, version, path]</tt> arrays corresponding to
|
45
|
+
# the last version of each installed gem. +path+ is the base path of the RI
|
46
|
+
# documentation from the gem. If the version cannot be determined, it will
|
47
|
+
# be +nil+, and the corresponding gem might be repeated in the output array
|
48
|
+
# (once per version).
|
49
|
+
def gem_directories_unique
|
50
|
+
return [] unless defined? Gem
|
51
|
+
gemdirs = Gem.path.map{|p| Dir["#{p}/doc/*/ri"]}.flatten
|
52
|
+
gems = Hash.new{|h,k| h[k] = []}
|
53
|
+
gemdirs.each do |path|
|
54
|
+
gemname, version = %r{/([^/]+)-([^-]*)/ri$}.match(path).captures
|
55
|
+
if gemname.nil? # doesn't follow any conventions :(
|
56
|
+
gems[path[%r{/([^/]+)/ri$}, 1]] << [nil, path]
|
57
|
+
else
|
58
|
+
gems[gemname] << [version, path]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
gems.sort_by{|name, _| name}.map do |name, versions|
|
62
|
+
version, path = versions.sort.last
|
63
|
+
[name, version, File.expand_path(path)]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
module_function :gem_directories_unique
|
67
|
+
|
68
|
+
# Return the <tt>[name, version, path]</tt> array for the gem owning the RI
|
69
|
+
# information stored in +path+, or +nil+.
|
70
|
+
def gem_info_for_path(path, gem_dir_info = FastRI::Util.gem_directories_unique)
|
71
|
+
path = File.expand_path(path)
|
72
|
+
matches = gem_dir_info.select{|name, version, gem_path| path.index(gem_path) == 0}
|
73
|
+
matches.sort_by{|name, version, gem_path| [gem_path.size, version, name]}.last
|
74
|
+
end
|
75
|
+
module_function :gem_info_for_path
|
76
|
+
|
77
|
+
# Return the +full_name+ (in ClassEntry or MethodEntry's sense) given a path
|
78
|
+
# to a .yaml file relative to a "base RI DB path".
|
79
|
+
def gem_relpath_to_full_name(relpath)
|
80
|
+
case relpath
|
81
|
+
when %r{^(.*)/cdesc-([^/]*)\.yaml$}
|
82
|
+
path, name = $~.captures
|
83
|
+
(path.split(%r{/})[0..-2] << name).join("::")
|
84
|
+
when %r{^(.*)/([^/]*)-(i|c)\.yaml$}
|
85
|
+
path, escaped_name, type = $~.captures
|
86
|
+
name = RI::RiWriter.external_to_internal(escaped_name)
|
87
|
+
sep = ( type == 'c' ) ? "." : "#"
|
88
|
+
path.gsub("/", "::") + sep + name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
module_function :gem_relpath_to_full_name
|
92
|
+
|
93
|
+
# Returns the home directory (win32-aware).
|
94
|
+
def find_home
|
95
|
+
# stolen from RubyGems
|
96
|
+
['HOME', 'USERPROFILE'].each do |homekey|
|
97
|
+
return ENV[homekey] if ENV[homekey]
|
98
|
+
end
|
99
|
+
if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
|
100
|
+
return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
|
101
|
+
end
|
102
|
+
begin
|
103
|
+
File.expand_path("~")
|
104
|
+
rescue StandardError => ex
|
105
|
+
if File::ALT_SEPARATOR
|
106
|
+
"C:/"
|
107
|
+
else
|
108
|
+
"/"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
module_function :find_home
|
113
|
+
|
114
|
+
def change_query_method_type(query)
|
115
|
+
if md = /\A(.*)(#|\.|::)([^#.:]+)\z/.match(query)
|
116
|
+
namespace, sep, meth = md.captures
|
117
|
+
case sep
|
118
|
+
when /::/ then "#{namespace}##{meth}"
|
119
|
+
when /#/ then "#{namespace}::#{meth}"
|
120
|
+
else
|
121
|
+
query
|
122
|
+
end
|
123
|
+
else
|
124
|
+
query
|
125
|
+
end
|
126
|
+
end
|
127
|
+
module_function :change_query_method_type
|
128
|
+
|
129
|
+
|
130
|
+
module MagicHelp
|
131
|
+
def help_method_extract(m) # :nodoc:
|
132
|
+
unless m.inspect =~ %r[\A#<(?:Unbound)?Method: (.*?)>\Z]
|
133
|
+
raise "Cannot parse result of #{m.class}#inspect: #{m.inspect}"
|
134
|
+
end
|
135
|
+
$1.sub(/\A.*?\((.*?)\)(.*)\Z/){ "#{$1}#{$2}" }.sub(/\./, "::").sub(/#<Class:(.*?)>#/) { "#{$1}::" }
|
136
|
+
end
|
137
|
+
|
138
|
+
def magic_help(query)
|
139
|
+
if query =~ /\A(.*?)(#|::|\.)([^:#.]+)\Z/
|
140
|
+
c, k, m = $1, $2, $3
|
141
|
+
mid = m
|
142
|
+
begin
|
143
|
+
c = c.split(/::/).inject(Object){|s,x| s.const_get(x)}
|
144
|
+
m = case k
|
145
|
+
when "#"
|
146
|
+
c.instance_method(m)
|
147
|
+
when "::"
|
148
|
+
c.method(m)
|
149
|
+
when "."
|
150
|
+
begin
|
151
|
+
# if it's a private_instance_method, assume it was created
|
152
|
+
# with module_function
|
153
|
+
if c.private_instance_methods.include?(m)
|
154
|
+
c.instance_method(m)
|
155
|
+
else
|
156
|
+
c.method(m)
|
157
|
+
end
|
158
|
+
rescue NameError
|
159
|
+
c.instance_method(m)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
ret = help_method_extract(m)
|
164
|
+
if ret == 'Class#new' and
|
165
|
+
c.private_method_defined?(:initialize)
|
166
|
+
return c.name + "::new"
|
167
|
+
elsif ret =~ /^Kernel#/ and
|
168
|
+
Kernel.instance_methods(false).include? mid
|
169
|
+
return "Object##{mid}"
|
170
|
+
end
|
171
|
+
ret
|
172
|
+
rescue Exception
|
173
|
+
query
|
174
|
+
end
|
175
|
+
else
|
176
|
+
query
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
end # module Util
|
183
|
+
end # module FastRI
|