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 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:
@@ -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.banner = "Usage: fri [options] <query>"
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
- def help_method_extract(m) # :nodoc:
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
- require 'rinda/ring'
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
- addr_spec = options[:addr]
264
- else
265
- addr_spec = ENV["FASTRI_ADDR"] || options[:addr]
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
- DRb.start_service(addr)
275
- ring_server = Rinda::RingFinger.primary
276
- rescue Exception
277
- $stderr.puts <<EOF
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
- exit(-1)
300
+ exit(-1)
301
+ end
302
+ service = ring_server.read([:name, :FastRI, nil, nil])[2]
289
303
  end
290
- service = ring_server.read([:name, :FastRI, nil, nil])[2]
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
- puts service.info(help_query, info_options)
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:
@@ -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 display_args(keyw)
171
- data = @ri.args(keyw)
172
- puts data if data
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
- data = @ri.info(keyw)
178
- puts data if data
179
- puts "RI_EMACS_END_OF_INFO"
188
+ display_ :info, keyw
180
189
  end
181
190
  end
182
191
 
@@ -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
 
@@ -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(namespace, /#{sep_re}#{method}/, false)
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
@@ -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
@@ -2,8 +2,8 @@
2
2
  #
3
3
 
4
4
  module FastRI
5
- FASTRI_VERSION = "0.2.1"
6
- FASTRI_RELEASE_DATE = "2006-11-23"
5
+ FASTRI_VERSION = "0.3.0"
6
+ FASTRI_RELEASE_DATE = "2007-01-29"
7
7
  FASTRI_INDEX_FORMAT = "0.1.0"
8
8
  FASTRI_FT_INDEX_FORMAT = "1.0.0"
9
9
  FASTRI_FT_INDEX_FORMAT_MAJOR = "1"
@@ -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
- require 'fileutils'
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
+
@@ -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)
@@ -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.2.1.1
7
- date: 2006-11-23 00:00:00 +01:00
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: []