fastri 0.2.1.1 → 0.3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +13 -0
- data/Rakefile +8 -2
- data/bin/fastri-server +2 -0
- data/bin/fri +78 -54
- data/bin/qri +336 -0
- data/bin/ri-emacs +16 -7
- data/lib/fastri/ri_index.rb +1 -1
- data/lib/fastri/ri_service.rb +24 -1
- data/lib/fastri/util.rb +69 -0
- data/lib/fastri/version.rb +2 -2
- data/pre-install.rb +5 -2
- data/test/test_ri_index.rb +24 -0
- data/test/test_util.rb +53 -0
- metadata +4 -2
data/CHANGES
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
|
2
2
|
User-visible changes in FastRI
|
3
3
|
|
4
|
+
Since version 0.2.1 (2006-11-23)
|
5
|
+
================================
|
6
|
+
Features
|
7
|
+
--------
|
8
|
+
* get all the information about a class/module and its methods with --extended
|
9
|
+
* new search methods: complete namespace (-Om), partial completion on both
|
10
|
+
namespace and method name (-Of), and case-indep. variants.
|
11
|
+
* --local (-L) and --remote (-L) (default) options. --local makes fri operate
|
12
|
+
in standalone mode, without requiring fastri-server.
|
13
|
+
* new executable: qri, equivalent to fri -L (local, standalone mode)
|
14
|
+
* try to second-guess the correct method type if all search methods fail
|
15
|
+
and the type was explicitly given
|
16
|
+
|
4
17
|
Since version 0.2.0 (2006-11-15)
|
5
18
|
================================
|
6
19
|
Features
|
data/Rakefile
CHANGED
@@ -29,7 +29,7 @@ require 'fastri/version'
|
|
29
29
|
|
30
30
|
PKG_REVISION = ".1"
|
31
31
|
PKG_FILES = FileList[
|
32
|
-
"bin/fri", "bin/fastri-server", "bin/ri-emacs",
|
32
|
+
"bin/fri", "bin/qri", "bin/fastri-server", "bin/ri-emacs",
|
33
33
|
"lib/**/*.rb", "CHANGES",
|
34
34
|
"COPYING", "LEGAL", "LICENSE", "Rakefile", "README.*", "test/*.rb",
|
35
35
|
"setup.rb", "pre-install.rb",
|
@@ -54,7 +54,7 @@ EOF
|
|
54
54
|
s.email = "mfp@acm.org"
|
55
55
|
s.homepage = "http://eigenclass.org/hiki.rb?fastri"
|
56
56
|
s.bindir = "bin"
|
57
|
-
s.executables = %w[fri fastri-server ri-emacs]
|
57
|
+
s.executables = %w[fri qri fastri-server ri-emacs]
|
58
58
|
s.has_rdoc = true
|
59
59
|
#s.extra_rdoc_files = %w[]
|
60
60
|
s.rdoc_options << "--title" << 'FastRI: better, faster ri'
|
@@ -93,4 +93,10 @@ Rake::GemPackageTask.new(Spec) do |p|
|
|
93
93
|
p.need_tar = true
|
94
94
|
end
|
95
95
|
|
96
|
+
file "bin/qri" => %w[bin/fri] do
|
97
|
+
cp "bin/fri", "bin/qri"
|
98
|
+
end
|
99
|
+
|
100
|
+
task :gem => ["bin/qri"]
|
101
|
+
|
96
102
|
# vim: set sw=2 ft=ruby:
|
data/bin/fastri-server
CHANGED
@@ -107,6 +107,8 @@ options = {:allowed_hosts => ["127.0.0.1"], :addr => "127.0.0.1",
|
|
107
107
|
:full_text_dir => File.join(home, ".fastri-fulltext"),
|
108
108
|
}
|
109
109
|
OptionParser.new do |opts|
|
110
|
+
opts.version = FastRI::FASTRI_VERSION
|
111
|
+
opts.release = FastRI::FASTRI_RELEASE_DATE
|
110
112
|
opts.banner = "Usage: fastri-server.rb [options]"
|
111
113
|
|
112
114
|
opts.on("-a", "--allow HOST", "Allow connections from HOST.",
|
data/bin/fri
CHANGED
@@ -7,6 +7,8 @@ require 'optparse'
|
|
7
7
|
require 'fastri/util'
|
8
8
|
require 'fastri/full_text_index'
|
9
9
|
|
10
|
+
default_local_mode = File.basename($0)[/^qri/] ? true : false
|
11
|
+
|
10
12
|
# we bind to 127.0.0.1 by default, because otherwise Ruby will try with
|
11
13
|
# 0.0.0.0, which results in a DNS request, adding way too much latency
|
12
14
|
options = {
|
@@ -34,11 +36,30 @@ options = {
|
|
34
36
|
:pager => nil,
|
35
37
|
:list_classes => nil,
|
36
38
|
:list_methods => nil,
|
39
|
+
:extended => false,
|
40
|
+
:index_file => File.join(FastRI::Util.find_home, ".fastri-index"),
|
41
|
+
:local_mode => default_local_mode,
|
37
42
|
}
|
43
|
+
|
38
44
|
override_addr_env = false
|
45
|
+
|
39
46
|
optparser = OptionParser.new do |opts|
|
40
|
-
opts.
|
47
|
+
opts.version = FastRI::FASTRI_VERSION
|
48
|
+
opts.release = FastRI::FASTRI_RELEASE_DATE
|
49
|
+
opts.banner = "Usage: #{File.basename($0)} [options] <query>"
|
41
50
|
|
51
|
+
opts.on("-L", "--local", "Try to use local index instead of DRb service.",
|
52
|
+
*[("(default)" if default_local_mode)].compact) do
|
53
|
+
options[:local_mode] = true
|
54
|
+
end
|
55
|
+
opts.on("--index-file=FILE", "Use index file (forces --local mode).",
|
56
|
+
"(default: #{options[:index_file]})") do |file|
|
57
|
+
options[:index_file] = file
|
58
|
+
options[:local_mode] = true
|
59
|
+
end
|
60
|
+
opts.on("-R", "--remote", "Use DRb service. #{'(default)' unless default_local_mode}") do
|
61
|
+
options[:local_mode] = false
|
62
|
+
end
|
42
63
|
opts.on("-s", "--bind ADDR", "Bind to ADDR for incoming DRb connections.",
|
43
64
|
"(default: 127.0.0.1)") do |addr|
|
44
65
|
options[:addr] = addr
|
@@ -49,15 +70,24 @@ optparser = OptionParser.new do |opts|
|
|
49
70
|
'e' => :exact, 'E' => :exact_ci, 'n' => :nested, 'N' => :nested_ci,
|
50
71
|
'p' => :partial, 'P' => :partial_ci, 'x' => :nested_partial,
|
51
72
|
'X' => :nested_partial_ci, 'a' => :anywhere, 'A' => :anywhere_ci,
|
73
|
+
'm' => :namespace_partial, 'M' => :namespace_partial_ci,
|
74
|
+
'f' => :full_partial, 'F' => :full_partial_ci,
|
52
75
|
}
|
53
76
|
opts.on("-O", "--order ORDER", "Specify lookup order.",
|
54
77
|
"(default: eEnNpPxX)", "Uppercase: case-indep.",
|
55
78
|
"e:exact n:nested p:partial (completion)",
|
56
|
-
"x:nested and partial",
|
79
|
+
"x:nested and partial m:complete namespace",
|
80
|
+
"f:complete both class and method",
|
57
81
|
"a:match method name anywhere") do |order|
|
58
82
|
options[:lookup_order] = order.split(//).map{|x| order_mapping[x]}.compact
|
59
83
|
end
|
60
84
|
|
85
|
+
opts.on("-e", "--extended", "Show all methods for given namespace.") do
|
86
|
+
options[:extended] = true
|
87
|
+
options[:use_pager] = true
|
88
|
+
options[:format] = "plain"
|
89
|
+
end
|
90
|
+
|
61
91
|
opts.on("--show-matches", "Only show matching entries."){ options[:show_matches] = true }
|
62
92
|
|
63
93
|
opts.on("--classes", "List all known classes/modules.") do
|
@@ -124,39 +154,7 @@ if !options[:list_classes] && !options[:list_methods] && ARGV.empty?
|
|
124
154
|
end
|
125
155
|
|
126
156
|
# {{{ try to find where the method comes from exactly
|
127
|
-
|
128
|
-
unless m.inspect =~ %r[\A#<(?:Unbound)?Method: (.*?)>\Z]
|
129
|
-
raise "Cannot parse result of #{m.class}#inspect: #{m.inspect}"
|
130
|
-
end
|
131
|
-
$1.sub(/\A.*?\((.*?)\)(.*)\Z/){ "#{$1}#{$2}" }.sub(/\./, "::").sub(/#<Class:(.*?)>#/) { "#{$1}::" }
|
132
|
-
end
|
133
|
-
|
134
|
-
def magic_help(query)
|
135
|
-
if query =~ /\A(.*?)(#|::|\.)(.*)\Z/
|
136
|
-
c, k, m = $1, $2, $3
|
137
|
-
begin
|
138
|
-
c = Object.const_get(c)
|
139
|
-
m = case k
|
140
|
-
when "#"
|
141
|
-
c.instance_method(m)
|
142
|
-
when "::"
|
143
|
-
c.method(m)
|
144
|
-
when "."
|
145
|
-
begin
|
146
|
-
c.method(m)
|
147
|
-
rescue NameError
|
148
|
-
c.instance_method(m)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
help_method_extract(m)
|
152
|
-
rescue Exception
|
153
|
-
query
|
154
|
-
end
|
155
|
-
else
|
156
|
-
query
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
157
|
+
include FastRI::Util::MagicHelp
|
160
158
|
|
161
159
|
MAX_CONTEXT_LINES = 20
|
162
160
|
def context_wrap(text, width)
|
@@ -255,26 +253,40 @@ end
|
|
255
253
|
perform_fulltext_search(options) if options[:do_full_text]
|
256
254
|
|
257
255
|
#{{{ normal query
|
258
|
-
|
256
|
+
if options[:local_mode]
|
257
|
+
require 'fastri/ri_service'
|
258
|
+
ri_reader = open(options[:index_file], "rb"){|io| Marshal.load io } rescue nil
|
259
|
+
unless ri_reader
|
260
|
+
puts <<EOF
|
261
|
+
Couldn't open the index:
|
262
|
+
#{options[:index_file]}
|
259
263
|
|
264
|
+
The index needs to be rebuilt with
|
265
|
+
fastri-server -b
|
266
|
+
EOF
|
267
|
+
exit(-1)
|
268
|
+
end
|
269
|
+
service = FastRI::RiService.new(ri_reader)
|
270
|
+
else # remote
|
271
|
+
require 'rinda/ring'
|
260
272
|
|
261
|
-
#{{{ determine the address to bind to
|
262
|
-
if override_addr_env
|
263
|
-
|
264
|
-
else
|
265
|
-
|
266
|
-
end
|
273
|
+
#{{{ determine the address to bind to
|
274
|
+
if override_addr_env
|
275
|
+
addr_spec = options[:addr]
|
276
|
+
else
|
277
|
+
addr_spec = ENV["FASTRI_ADDR"] || options[:addr]
|
278
|
+
end
|
267
279
|
|
268
|
-
ip = addr_spec[/^[^:]+/] || "127.0.0.1"
|
269
|
-
port = addr_spec[/:(\d+)/, 1] || 0
|
270
|
-
addr = "druby://#{ip}:#{port}"
|
280
|
+
ip = addr_spec[/^[^:]+/] || "127.0.0.1"
|
281
|
+
port = addr_spec[/:(\d+)/, 1] || 0
|
282
|
+
addr = "druby://#{ip}:#{port}"
|
271
283
|
|
272
|
-
#{{{ start DRb and perform request
|
273
|
-
begin
|
274
|
-
|
275
|
-
|
276
|
-
rescue Exception
|
277
|
-
|
284
|
+
#{{{ start DRb and perform request
|
285
|
+
begin
|
286
|
+
DRb.start_service(addr)
|
287
|
+
ring_server = Rinda::RingFinger.primary
|
288
|
+
rescue Exception
|
289
|
+
$stderr.puts <<EOF
|
278
290
|
Couldn't initialize DRb and locate the Ring server.
|
279
291
|
|
280
292
|
Please make sure that:
|
@@ -285,13 +297,17 @@ Please make sure that:
|
|
285
297
|
export FASTRI_ADDR="192.168.1.12"
|
286
298
|
fri Array
|
287
299
|
EOF
|
288
|
-
|
300
|
+
exit(-1)
|
301
|
+
end
|
302
|
+
service = ring_server.read([:name, :FastRI, nil, nil])[2]
|
289
303
|
end
|
290
|
-
|
304
|
+
|
305
|
+
|
291
306
|
info_options = {
|
292
307
|
:formatter => options[:format],
|
293
308
|
:width => options[:width],
|
294
309
|
:lookup_order => options[:lookup_order],
|
310
|
+
:extended => options[:extended],
|
295
311
|
}
|
296
312
|
|
297
313
|
# {{{ list classes or methods
|
@@ -306,7 +322,15 @@ ARGV.each do |term|
|
|
306
322
|
if options[:show_matches]
|
307
323
|
puts service.matches(help_query, info_options).sort
|
308
324
|
else
|
309
|
-
|
325
|
+
result = service.info(help_query, info_options)
|
326
|
+
# second-guess the correct method type only as the last resort.
|
327
|
+
if result
|
328
|
+
puts result
|
329
|
+
elsif (new_query = FastRI::Util.change_query_method_type(help_query)) != help_query
|
330
|
+
puts service.info(new_query)
|
331
|
+
else
|
332
|
+
puts nil
|
333
|
+
end
|
310
334
|
end
|
311
335
|
end
|
312
336
|
# vi: set sw=2 expandtab:
|
data/bin/qri
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# fri: access RI documentation through DRb
|
3
|
+
# Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'fastri/util'
|
8
|
+
require 'fastri/full_text_index'
|
9
|
+
|
10
|
+
default_local_mode = File.basename($0)[/^qri/] ? true : false
|
11
|
+
|
12
|
+
# we bind to 127.0.0.1 by default, because otherwise Ruby will try with
|
13
|
+
# 0.0.0.0, which results in a DNS request, adding way too much latency
|
14
|
+
options = {
|
15
|
+
:addr => "127.0.0.1",
|
16
|
+
:format =>
|
17
|
+
case RUBY_PLATFORM
|
18
|
+
when /win/
|
19
|
+
if /darwin|cygwin/ =~ RUBY_PLATFORM
|
20
|
+
"ansi"
|
21
|
+
else
|
22
|
+
"plain"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
"ansi"
|
26
|
+
end,
|
27
|
+
:width => 72,
|
28
|
+
:lookup_order => [
|
29
|
+
:exact, :exact_ci, :nested, :nested_ci, :partial, :partial_ci,
|
30
|
+
:nested_partial, :nested_partial_ci,
|
31
|
+
],
|
32
|
+
:show_matches => false,
|
33
|
+
:do_full_text => false,
|
34
|
+
:full_text_dir => File.join(FastRI::Util.find_home, ".fastri-fulltext"),
|
35
|
+
:use_pager => nil,
|
36
|
+
:pager => nil,
|
37
|
+
:list_classes => nil,
|
38
|
+
:list_methods => nil,
|
39
|
+
:extended => false,
|
40
|
+
:index_file => File.join(FastRI::Util.find_home, ".fastri-index"),
|
41
|
+
:local_mode => default_local_mode,
|
42
|
+
}
|
43
|
+
|
44
|
+
override_addr_env = false
|
45
|
+
|
46
|
+
optparser = OptionParser.new do |opts|
|
47
|
+
opts.version = FastRI::FASTRI_VERSION
|
48
|
+
opts.release = FastRI::FASTRI_RELEASE_DATE
|
49
|
+
opts.banner = "Usage: #{File.basename($0)} [options] <query>"
|
50
|
+
|
51
|
+
opts.on("-L", "--local", "Try to use local index instead of DRb service.",
|
52
|
+
*[("(default)" if default_local_mode)].compact) do
|
53
|
+
options[:local_mode] = true
|
54
|
+
end
|
55
|
+
opts.on("--index-file=FILE", "Use index file (forces --local mode).",
|
56
|
+
"(default: #{options[:index_file]})") do |file|
|
57
|
+
options[:index_file] = file
|
58
|
+
options[:local_mode] = true
|
59
|
+
end
|
60
|
+
opts.on("-R", "--remote", "Use DRb service. #{'(default)' unless default_local_mode}") do
|
61
|
+
options[:local_mode] = false
|
62
|
+
end
|
63
|
+
opts.on("-s", "--bind ADDR", "Bind to ADDR for incoming DRb connections.",
|
64
|
+
"(default: 127.0.0.1)") do |addr|
|
65
|
+
options[:addr] = addr
|
66
|
+
override_addr_env = true
|
67
|
+
end
|
68
|
+
|
69
|
+
order_mapping = {
|
70
|
+
'e' => :exact, 'E' => :exact_ci, 'n' => :nested, 'N' => :nested_ci,
|
71
|
+
'p' => :partial, 'P' => :partial_ci, 'x' => :nested_partial,
|
72
|
+
'X' => :nested_partial_ci, 'a' => :anywhere, 'A' => :anywhere_ci,
|
73
|
+
'm' => :namespace_partial, 'M' => :namespace_partial_ci,
|
74
|
+
'f' => :full_partial, 'F' => :full_partial_ci,
|
75
|
+
}
|
76
|
+
opts.on("-O", "--order ORDER", "Specify lookup order.",
|
77
|
+
"(default: eEnNpPxX)", "Uppercase: case-indep.",
|
78
|
+
"e:exact n:nested p:partial (completion)",
|
79
|
+
"x:nested and partial m:complete namespace",
|
80
|
+
"f:complete both class and method",
|
81
|
+
"a:match method name anywhere") do |order|
|
82
|
+
options[:lookup_order] = order.split(//).map{|x| order_mapping[x]}.compact
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on("-e", "--extended", "Show all methods for given namespace.") do
|
86
|
+
options[:extended] = true
|
87
|
+
options[:use_pager] = true
|
88
|
+
options[:format] = "plain"
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on("--show-matches", "Only show matching entries."){ options[:show_matches] = true }
|
92
|
+
|
93
|
+
opts.on("--classes", "List all known classes/modules.") do
|
94
|
+
options[:use_pager] = true
|
95
|
+
options[:list_classes] = true
|
96
|
+
end
|
97
|
+
opts.on("--methods", "List all known methods.") do
|
98
|
+
options[:use_pager] = true
|
99
|
+
options[:list_methods] = true
|
100
|
+
end
|
101
|
+
opts.on("-l", "--list-names", "List all known namespaces/methods.") do
|
102
|
+
options[:use_pager] = true
|
103
|
+
options[:list_classes] = true
|
104
|
+
options[:list_methods] = true
|
105
|
+
end
|
106
|
+
|
107
|
+
opts.on("-S", "--full-text", "Perform full-text search.") do
|
108
|
+
options[:do_full_text] = true
|
109
|
+
options[:use_pager] = true if options[:use_pager].nil?
|
110
|
+
options[:format] = "plain"
|
111
|
+
end
|
112
|
+
|
113
|
+
opts.on("-F", "--full-text-dir DIR", "Use full-text index in DIR",
|
114
|
+
"(default: #{options[:full_text_dir]})") do |dir|
|
115
|
+
options[:full_text_dir] = dir if dir
|
116
|
+
options[:do_full_text] = true
|
117
|
+
options[:use_pager] = true
|
118
|
+
options[:format] = "plain"
|
119
|
+
end
|
120
|
+
|
121
|
+
opts.on("-f", "--format FMT", "Format to use when displaying output:",
|
122
|
+
" ansi, plain (default: #{options[:format]})") do |format|
|
123
|
+
options[:format] = format
|
124
|
+
end
|
125
|
+
|
126
|
+
opts.on("-P", "--[no-]pager", "Use pager.", "(default: don't)") do |usepager|
|
127
|
+
options[:use_pager] = usepager
|
128
|
+
options[:format] = "plain" if usepager
|
129
|
+
end
|
130
|
+
|
131
|
+
opts.on("-T", "Don't use a pager."){ options[:use_pager] = false }
|
132
|
+
|
133
|
+
opts.on("--pager-cmd PAGER", "Use pager PAGER.", "(default: don't)") do |pager|
|
134
|
+
options[:pager] = pager
|
135
|
+
options[:use_pager] = true
|
136
|
+
options[:format] = "plain"
|
137
|
+
end
|
138
|
+
|
139
|
+
opts.on("-w", "--width WIDTH", "Set the width of the output.") do |width|
|
140
|
+
w = width.to_i
|
141
|
+
options[:width] = w > 0 ? w : options[:width]
|
142
|
+
end
|
143
|
+
|
144
|
+
opts.on("-h", "--help", "Show this help message") do
|
145
|
+
puts opts
|
146
|
+
exit
|
147
|
+
end
|
148
|
+
end
|
149
|
+
optparser.parse!
|
150
|
+
|
151
|
+
if !options[:list_classes] && !options[:list_methods] && ARGV.empty?
|
152
|
+
puts optparser
|
153
|
+
exit
|
154
|
+
end
|
155
|
+
|
156
|
+
# {{{ try to find where the method comes from exactly
|
157
|
+
include FastRI::Util::MagicHelp
|
158
|
+
|
159
|
+
MAX_CONTEXT_LINES = 20
|
160
|
+
def context_wrap(text, width)
|
161
|
+
"... " +
|
162
|
+
text.gsub(/(.{1,#{width-4}})( +|$\n?)|(.{1,#{width-4}})/, "\\1\\3\n").chomp
|
163
|
+
end
|
164
|
+
|
165
|
+
def display_fulltext_search_results(results, gem_dir_info = FastRI::Util.gem_directories_unique,
|
166
|
+
width = 78)
|
167
|
+
return if results.empty?
|
168
|
+
path = File.expand_path(results[0].path)
|
169
|
+
gem_name, version, gem_path = FastRI::Util.gem_info_for_path(path, gem_dir_info)
|
170
|
+
if gem_name
|
171
|
+
rel_path = path[/#{Regexp.escape(gem_path)}\/(.*)/, 1]
|
172
|
+
if rel_path
|
173
|
+
entry_name = FastRI::Util.gem_relpath_to_full_name(rel_path)
|
174
|
+
end
|
175
|
+
puts "Found in #{gem_name} #{version} #{entry_name}"
|
176
|
+
else
|
177
|
+
rdoc_system_path = File.expand_path(RI::Paths::SYSDIR)
|
178
|
+
if path.index(rdoc_system_path)
|
179
|
+
rel_path = path[/#{Regexp.escape(rdoc_system_path)}\/(.*)/, 1]
|
180
|
+
puts "Found in system #{FastRI::Util.gem_relpath_to_full_name(rel_path)}"
|
181
|
+
else
|
182
|
+
puts "Found in #{path}:"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
text = results.map do |result|
|
186
|
+
context = result.context(120)
|
187
|
+
from = (context.rindex("\n", context.index(result.query)) || -1) + 1
|
188
|
+
to = (context.index("\n", context.index(result.query)) || 0) - 1
|
189
|
+
context_wrap(context[from..to], width)
|
190
|
+
end
|
191
|
+
puts
|
192
|
+
puts text.uniq[0...MAX_CONTEXT_LINES]
|
193
|
+
puts
|
194
|
+
end
|
195
|
+
|
196
|
+
def perform_fulltext_search(options)
|
197
|
+
fulltext = File.join(options[:full_text_dir], "full_text.dat")
|
198
|
+
suffixes = File.join(options[:full_text_dir], "suffixes.dat")
|
199
|
+
begin
|
200
|
+
index = FastRI::FullTextIndex.new_from_filenames(fulltext, suffixes)
|
201
|
+
rescue Exception
|
202
|
+
puts <<EOF
|
203
|
+
Couldn't open the full-text index:
|
204
|
+
#{fulltext}
|
205
|
+
#{suffixes}
|
206
|
+
|
207
|
+
The index needs to be rebuilt with
|
208
|
+
fastri-server -B
|
209
|
+
EOF
|
210
|
+
exit(-1)
|
211
|
+
end
|
212
|
+
gem_dir_info = FastRI::Util.gem_directories_unique
|
213
|
+
match_sets = ARGV.map do |query|
|
214
|
+
result = index.lookup(query)
|
215
|
+
if result
|
216
|
+
index.next_matches(result) + [result]
|
217
|
+
else
|
218
|
+
[]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
path_map = Hash.new{|h,k| h[k] = []}
|
222
|
+
match_sets.each{|matches| matches.each{|m| path_map[m.path] << m} }
|
223
|
+
paths = match_sets[1..-1].inject(match_sets[0].map{|x| x.path}.uniq) do |s,x|
|
224
|
+
s & x.map{|y| y.path}.uniq
|
225
|
+
end
|
226
|
+
if paths.empty?
|
227
|
+
puts "nil"
|
228
|
+
else
|
229
|
+
puts "#{paths.size} hits"
|
230
|
+
paths.sort_by{|path| 1.0 * -path_map[path].size / path_map[path].first.metadata[:size] ** 0.5}.map do |path|
|
231
|
+
puts "=" * options[:width]
|
232
|
+
puts 1.0 * path_map[path].size / path_map[path].first.metadata[:size] ** 0.5
|
233
|
+
display_fulltext_search_results(path_map[path], gem_dir_info, options[:width])
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
exit 0
|
238
|
+
end
|
239
|
+
|
240
|
+
#{{{ set up pager
|
241
|
+
if options[:use_pager]
|
242
|
+
[options[:pager], ENV["PAGER"], "less", "more", "pager"].compact.uniq.each do |pager|
|
243
|
+
begin
|
244
|
+
$stdout = IO.popen(pager, "w")
|
245
|
+
at_exit{ $stdout.close }
|
246
|
+
break
|
247
|
+
rescue Exception
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
#{{{ perform full text search if asked to
|
253
|
+
perform_fulltext_search(options) if options[:do_full_text]
|
254
|
+
|
255
|
+
#{{{ normal query
|
256
|
+
if options[:local_mode]
|
257
|
+
require 'fastri/ri_service'
|
258
|
+
ri_reader = open(options[:index_file], "rb"){|io| Marshal.load io } rescue nil
|
259
|
+
unless ri_reader
|
260
|
+
puts <<EOF
|
261
|
+
Couldn't open the index:
|
262
|
+
#{options[:index_file]}
|
263
|
+
|
264
|
+
The index needs to be rebuilt with
|
265
|
+
fastri-server -b
|
266
|
+
EOF
|
267
|
+
exit(-1)
|
268
|
+
end
|
269
|
+
service = FastRI::RiService.new(ri_reader)
|
270
|
+
else # remote
|
271
|
+
require 'rinda/ring'
|
272
|
+
|
273
|
+
#{{{ determine the address to bind to
|
274
|
+
if override_addr_env
|
275
|
+
addr_spec = options[:addr]
|
276
|
+
else
|
277
|
+
addr_spec = ENV["FASTRI_ADDR"] || options[:addr]
|
278
|
+
end
|
279
|
+
|
280
|
+
ip = addr_spec[/^[^:]+/] || "127.0.0.1"
|
281
|
+
port = addr_spec[/:(\d+)/, 1] || 0
|
282
|
+
addr = "druby://#{ip}:#{port}"
|
283
|
+
|
284
|
+
#{{{ start DRb and perform request
|
285
|
+
begin
|
286
|
+
DRb.start_service(addr)
|
287
|
+
ring_server = Rinda::RingFinger.primary
|
288
|
+
rescue Exception
|
289
|
+
$stderr.puts <<EOF
|
290
|
+
Couldn't initialize DRb and locate the Ring server.
|
291
|
+
|
292
|
+
Please make sure that:
|
293
|
+
* the fastri-server is running, the server is bound to the correct interface,
|
294
|
+
and the ACL setup allows connections from this host
|
295
|
+
* fri is using the correct interface for incoming DRb requests:
|
296
|
+
either set the FASTRI_ADDR environment variable, or use --bind ADDR, e.g
|
297
|
+
export FASTRI_ADDR="192.168.1.12"
|
298
|
+
fri Array
|
299
|
+
EOF
|
300
|
+
exit(-1)
|
301
|
+
end
|
302
|
+
service = ring_server.read([:name, :FastRI, nil, nil])[2]
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
info_options = {
|
307
|
+
:formatter => options[:format],
|
308
|
+
:width => options[:width],
|
309
|
+
:lookup_order => options[:lookup_order],
|
310
|
+
:extended => options[:extended],
|
311
|
+
}
|
312
|
+
|
313
|
+
# {{{ list classes or methods
|
314
|
+
puts service.all_classes if options[:list_classes]
|
315
|
+
puts service.all_methods if options[:list_methods]
|
316
|
+
exit if options[:list_classes] || options[:list_methods]
|
317
|
+
|
318
|
+
# {{{ normal query
|
319
|
+
|
320
|
+
ARGV.each do |term|
|
321
|
+
help_query = magic_help(term)
|
322
|
+
if options[:show_matches]
|
323
|
+
puts service.matches(help_query, info_options).sort
|
324
|
+
else
|
325
|
+
result = service.info(help_query, info_options)
|
326
|
+
# second-guess the correct method type only as the last resort.
|
327
|
+
if result
|
328
|
+
puts result
|
329
|
+
elsif (new_query = FastRI::Util.change_query_method_type(help_query)) != help_query
|
330
|
+
puts service.info(new_query)
|
331
|
+
else
|
332
|
+
puts nil
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
# vi: set sw=2 expandtab:
|
data/bin/ri-emacs
CHANGED
@@ -24,6 +24,7 @@
|
|
24
24
|
|
25
25
|
require 'rinda/ring'
|
26
26
|
require 'optparse'
|
27
|
+
require 'fastri/util'
|
27
28
|
|
28
29
|
# {{{ cmdline parsing and service discovery
|
29
30
|
# we bind to 127.0.0.1 by default, because otherwise Ruby will try with
|
@@ -67,11 +68,13 @@ Please make sure that:
|
|
67
68
|
export FASTRI_ADDR="192.168.1.12"
|
68
69
|
fri Array
|
69
70
|
EOF
|
70
|
-
exit(-1)
|
71
|
+
exit(-1) # '
|
71
72
|
end
|
72
73
|
service = ring_server.read([:name, :FastRI, nil, nil])[2]
|
73
74
|
|
74
75
|
class EventLoop
|
76
|
+
include FastRI::Util::MagicHelp
|
77
|
+
|
75
78
|
def initialize(ri)
|
76
79
|
@ri = ri
|
77
80
|
end
|
@@ -167,16 +170,22 @@ class EventLoop
|
|
167
170
|
end
|
168
171
|
end
|
169
172
|
|
170
|
-
def
|
171
|
-
data = @ri.
|
172
|
-
|
173
|
+
def display_(what, keyw)
|
174
|
+
data = @ri.__send__(what, magic_help(keyw))
|
175
|
+
if data
|
176
|
+
puts data
|
177
|
+
elsif (new_keyw = FastRI::Util.change_query_method_type(keyw)) != keyw
|
178
|
+
puts @ri.__send__(what, new_keyw)
|
179
|
+
end
|
173
180
|
puts "RI_EMACS_END_OF_INFO"
|
174
181
|
end
|
175
182
|
|
183
|
+
def display_args(keyw)
|
184
|
+
display_ :args, keyw
|
185
|
+
end
|
186
|
+
|
176
187
|
def display_info(keyw)
|
177
|
-
|
178
|
-
puts data if data
|
179
|
-
puts "RI_EMACS_END_OF_INFO"
|
188
|
+
display_ :info, keyw
|
180
189
|
end
|
181
190
|
end
|
182
191
|
|
data/lib/fastri/ri_index.rb
CHANGED
@@ -534,7 +534,7 @@ class RiIndex
|
|
534
534
|
if prefix.empty?
|
535
535
|
[//, /^[#.] /] # the second should never match
|
536
536
|
else
|
537
|
-
[/^#{Regexp.escape(prefix)}/, /^#{Regexp.escape(prefix)}([#.])\S+ / ]
|
537
|
+
[/^#{Regexp.escape(prefix)}([#.]|::)/, /^#{Regexp.escape(prefix)}([#.])\S+ / ]
|
538
538
|
end
|
539
539
|
end
|
540
540
|
|
data/lib/fastri/ri_service.rb
CHANGED
@@ -180,6 +180,7 @@ class RiService
|
|
180
180
|
DEFAULT_INFO_OPTIONS = {
|
181
181
|
:formatter => :ansi,
|
182
182
|
:width => 72,
|
183
|
+
:extended => false,
|
183
184
|
}
|
184
185
|
|
185
186
|
def matches(keyword, options = {})
|
@@ -205,6 +206,12 @@ class RiService
|
|
205
206
|
when :namespace
|
206
207
|
capture_stdout(display(options)) do |display|
|
207
208
|
display.display_class_info(@ri_reader.get_class(entries[0]), @ri_reader)
|
209
|
+
if options[:extended]
|
210
|
+
methods = @ri_reader.methods_under(entries[0], true)
|
211
|
+
methods.each do |meth_entry|
|
212
|
+
display.display_method_info(@ri_reader.get_method(meth_entry))
|
213
|
+
end
|
214
|
+
end
|
208
215
|
end
|
209
216
|
when :method
|
210
217
|
capture_stdout(display(options)) do |display|
|
@@ -269,6 +276,7 @@ class RiService
|
|
269
276
|
private
|
270
277
|
|
271
278
|
def obtain_unqualified_method_entries(name, separators, order)
|
279
|
+
name = Regexp.escape(name)
|
272
280
|
sep_re = "(" + separators.map{|x| Regexp.escape(x)}.join("|") + ")"
|
273
281
|
matcher = MatchFinder.new do |m|
|
274
282
|
m.add_matcher(:exact) do
|
@@ -294,6 +302,8 @@ class RiService
|
|
294
302
|
end
|
295
303
|
|
296
304
|
def obtain_qualified_method_entries(namespace, method, separators, order)
|
305
|
+
namespace, unescaped_namespace = Regexp.escape(namespace), namespace
|
306
|
+
method = Regexp.escape(method)
|
297
307
|
matcher = MatchFinder.new do |m|
|
298
308
|
m.add_matcher(:exact) do
|
299
309
|
separators.each do |sep|
|
@@ -311,7 +321,7 @@ class RiService
|
|
311
321
|
m.yield @ri_reader.methods_under_matching("", /::#{namespace}#{sep_re}#{method}$/i, true)
|
312
322
|
end
|
313
323
|
m.add_matcher(:partial) do
|
314
|
-
m.yield @ri_reader.methods_under_matching(
|
324
|
+
m.yield @ri_reader.methods_under_matching(unescaped_namespace, /#{sep_re}#{method}/, false)
|
315
325
|
end
|
316
326
|
m.add_matcher(:partial_ci) do
|
317
327
|
m.yield @ri_reader.methods_under_matching("", /^#{namespace}#{sep_re}#{method}/i, true)
|
@@ -322,11 +332,24 @@ class RiService
|
|
322
332
|
m.add_matcher(:nested_partial_ci) do
|
323
333
|
m.yield @ri_reader.methods_under_matching("", /::#{namespace}#{sep_re}#{method}/i, true)
|
324
334
|
end
|
335
|
+
m.add_matcher(:namespace_partial) do
|
336
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}$/, true)
|
337
|
+
end
|
338
|
+
m.add_matcher(:namespace_partial_ci) do
|
339
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}$/i, true)
|
340
|
+
end
|
341
|
+
m.add_matcher(:full_partial) do
|
342
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}/, true)
|
343
|
+
end
|
344
|
+
m.add_matcher(:full_partial_ci) do
|
345
|
+
m.yield @ri_reader.methods_under_matching("", /^#{namespace}[^:]*#{sep_re}#{method}/i, true)
|
346
|
+
end
|
325
347
|
end
|
326
348
|
matcher.get_matches(order)
|
327
349
|
end
|
328
350
|
|
329
351
|
def obtain_namespace_entries(name, order)
|
352
|
+
name = Regexp.escape(name)
|
330
353
|
matcher = MatchFinder.new do |m|
|
331
354
|
m.add_matcher(:exact){ m.yield @ri_reader.get_class_entry(name) }
|
332
355
|
m.add_matcher(:exact_ci) do
|
data/lib/fastri/util.rb
CHANGED
@@ -96,5 +96,74 @@ module Util
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
module_function :find_home
|
99
|
+
|
100
|
+
def change_query_method_type(query)
|
101
|
+
if md = /\A(.*)(#|\.|::)([^#.:]+)\z/.match(query)
|
102
|
+
namespace, sep, meth = md.captures
|
103
|
+
case sep
|
104
|
+
when /::/ then "#{namespace}##{meth}"
|
105
|
+
when /#/ then "#{namespace}::#{meth}"
|
106
|
+
else
|
107
|
+
query
|
108
|
+
end
|
109
|
+
else
|
110
|
+
query
|
111
|
+
end
|
112
|
+
end
|
113
|
+
module_function :change_query_method_type
|
114
|
+
|
115
|
+
|
116
|
+
module MagicHelp
|
117
|
+
def help_method_extract(m) # :nodoc:
|
118
|
+
unless m.inspect =~ %r[\A#<(?:Unbound)?Method: (.*?)>\Z]
|
119
|
+
raise "Cannot parse result of #{m.class}#inspect: #{m.inspect}"
|
120
|
+
end
|
121
|
+
$1.sub(/\A.*?\((.*?)\)(.*)\Z/){ "#{$1}#{$2}" }.sub(/\./, "::").sub(/#<Class:(.*?)>#/) { "#{$1}::" }
|
122
|
+
end
|
123
|
+
|
124
|
+
def magic_help(query)
|
125
|
+
if query =~ /\A(.*?)(#|::|\.)([^:#.]+)\Z/
|
126
|
+
c, k, m = $1, $2, $3
|
127
|
+
mid = m
|
128
|
+
begin
|
129
|
+
c = c.split(/::/).inject(Object){|s,x| s.const_get(x)}
|
130
|
+
m = case k
|
131
|
+
when "#"
|
132
|
+
c.instance_method(m)
|
133
|
+
when "::"
|
134
|
+
c.method(m)
|
135
|
+
when "."
|
136
|
+
begin
|
137
|
+
# if it's a private_instance_method, assume it was created
|
138
|
+
# with module_function
|
139
|
+
if c.private_instance_methods.include?(m)
|
140
|
+
c.instance_method(m)
|
141
|
+
else
|
142
|
+
c.method(m)
|
143
|
+
end
|
144
|
+
rescue NameError
|
145
|
+
c.instance_method(m)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
ret = help_method_extract(m)
|
150
|
+
if ret == 'Class#new' and
|
151
|
+
c.private_method_defined?(:initialize)
|
152
|
+
return c.name + "::new"
|
153
|
+
elsif ret =~ /^Kernel#/ and
|
154
|
+
Kernel.instance_methods(false).include? mid
|
155
|
+
return "Object##{mid}"
|
156
|
+
end
|
157
|
+
ret
|
158
|
+
rescue Exception
|
159
|
+
query
|
160
|
+
end
|
161
|
+
else
|
162
|
+
query
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
99
168
|
end # module Util
|
100
169
|
end # module FastRI
|
data/lib/fastri/version.rb
CHANGED
data/pre-install.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
FileUtils.cp "bin/fri", "bin/qri"
|
2
5
|
|
3
6
|
if /win/ =~ RUBY_PLATFORM and /darwin|cygwin/ !~ RUBY_PLATFORM
|
4
|
-
|
5
|
-
%w[fri fastri-server ri-emacs].each do |fname|
|
7
|
+
%w[fri qri fastri-server ri-emacs].each do |fname|
|
6
8
|
FileUtils.mv "bin/#{fname}", "bin/#{fname}.rb", :force => true
|
7
9
|
end
|
8
10
|
end
|
11
|
+
|
data/test/test_ri_index.rb
CHANGED
@@ -29,11 +29,30 @@ ABC::Zzz#foo 0
|
|
29
29
|
CDE.foo 1 2
|
30
30
|
FGH::Adfdsf#foo 2
|
31
31
|
================================================================================
|
32
|
+
EOF
|
33
|
+
INDEX_DATA2 =<<EOF
|
34
|
+
#{FastRI::RiIndex::MAGIC}
|
35
|
+
Sources:
|
36
|
+
system /usr/share/ri/system/
|
37
|
+
somegem-0.1.0 /long/path/somegem-0.1.0
|
38
|
+
stuff-1.1.0 /long/path/stuff-1.1.0
|
39
|
+
================================================================================
|
40
|
+
Namespaces:
|
41
|
+
ABC 0 1
|
42
|
+
ABCDEF 1 2
|
43
|
+
================================================================================
|
44
|
+
Methods:
|
45
|
+
ABC.bar 0
|
46
|
+
ABC#baz 1
|
47
|
+
ABCDEF#foo 1
|
48
|
+
ABCDEF.foo 1 2
|
49
|
+
================================================================================
|
32
50
|
EOF
|
33
51
|
|
34
52
|
require 'stringio'
|
35
53
|
def setup
|
36
54
|
@index = FastRI::RiIndex.new_from_IO(StringIO.new(INDEX_DATA))
|
55
|
+
@index2 = FastRI::RiIndex.new_from_IO(StringIO.new(INDEX_DATA2))
|
37
56
|
end
|
38
57
|
|
39
58
|
def test_dump
|
@@ -157,6 +176,11 @@ EOF
|
|
157
176
|
@index.source_paths_for(@index.get_method_entry("CDE.foo")))
|
158
177
|
end
|
159
178
|
|
179
|
+
def test_methods_under_same_prefix
|
180
|
+
results = @index2.methods_under("ABC", true, nil)
|
181
|
+
results.map{|x| x.full_name}
|
182
|
+
assert_equal(["ABC.bar", "ABC#baz"], results.map{|x| x.full_name})
|
183
|
+
end
|
160
184
|
|
161
185
|
def test_methods_under_scoped
|
162
186
|
results = @index.methods_under("ABC", true, 1)
|
data/test/test_util.rb
CHANGED
@@ -35,4 +35,57 @@ EOF
|
|
35
35
|
assert_equal("Foo::Bar#eql?", gem_relpath_to_full_name("Foo/Bar/eql%3f-i.yaml"))
|
36
36
|
assert_equal("Foo::Bar.eql?", gem_relpath_to_full_name("Foo/Bar/eql%3f-c.yaml"))
|
37
37
|
end
|
38
|
+
|
39
|
+
def test_change_query_method_type
|
40
|
+
assert_equal(".foo", change_query_method_type(".foo"))
|
41
|
+
assert_equal("::foo", change_query_method_type("#foo"))
|
42
|
+
assert_equal("#foo", change_query_method_type("::foo"))
|
43
|
+
assert_equal("A::B.foo", change_query_method_type("A::B.foo"))
|
44
|
+
assert_equal("A::B::foo", change_query_method_type("A::B#foo"))
|
45
|
+
assert_equal("A::B#foo", change_query_method_type("A::B::foo"))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class TestUtilMagicHelp < Test::Unit::TestCase
|
50
|
+
include FastRI::Util::MagicHelp
|
51
|
+
module TestModule
|
52
|
+
def foo; end
|
53
|
+
module_function :foo
|
54
|
+
|
55
|
+
def self.bar; end
|
56
|
+
def bar; end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_magic_help
|
60
|
+
assert_equal("IO::readlines", magic_help("IO::readlines"))
|
61
|
+
assert_equal("IO::readlines", magic_help("IO.readlines"))
|
62
|
+
|
63
|
+
assert_equal("Enumerable#inject", magic_help("File#inject"))
|
64
|
+
assert_equal("Enumerable#inject", magic_help("File.inject"))
|
65
|
+
|
66
|
+
assert_equal("IO::readlines", magic_help("File.readlines"))
|
67
|
+
assert_equal("IO::readlines", magic_help("File::readlines"))
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_magic_help_nested_namespaces
|
71
|
+
assert_equal("TestUtilMagicHelp::TestModule#foo",
|
72
|
+
magic_help("TestUtilMagicHelp::TestModule.foo"))
|
73
|
+
assert_equal("TestUtilMagicHelp::TestModule::bar",
|
74
|
+
magic_help("TestUtilMagicHelp::TestModule.bar"))
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_magic_help__new
|
78
|
+
assert_equal("Array::new", magic_help("Array::new"))
|
79
|
+
assert_equal("Array::new", magic_help("Array.new"))
|
80
|
+
assert_equal("Struct::new", magic_help("Struct.new"))
|
81
|
+
assert_equal("Struct::new", magic_help("Struct::new"))
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_magic_help__Kernel_public_instance_methods
|
85
|
+
# It is mysterious.
|
86
|
+
# Object.instance_method(:object_id) # => #<UnboundMethod: Object(Kernel)#object_id>
|
87
|
+
assert_equal("Object#object_id", magic_help("Object.object_id"))
|
88
|
+
assert_equal("Object#object_id", magic_help("Object#object_id"))
|
89
|
+
end
|
90
|
+
|
38
91
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: fastri
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date:
|
6
|
+
version: 0.3.0.1
|
7
|
+
date: 2007-01-29 00:00:00 +01:00
|
8
8
|
summary: RI docs across machines, faster and smarter than ri.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -55,6 +55,7 @@ authors:
|
|
55
55
|
- Mauricio Fernandez
|
56
56
|
files:
|
57
57
|
- bin/fri
|
58
|
+
- bin/qri
|
58
59
|
- bin/fastri-server
|
59
60
|
- bin/ri-emacs
|
60
61
|
- lib/fastri/ri_index.rb
|
@@ -94,6 +95,7 @@ extra_rdoc_files: []
|
|
94
95
|
|
95
96
|
executables:
|
96
97
|
- fri
|
98
|
+
- qri
|
97
99
|
- fastri-server
|
98
100
|
- ri-emacs
|
99
101
|
extensions: []
|