fastri 0.2.1.1 → 0.3.0.1

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/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: []