bond 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,10 +12,15 @@ Gem::Specification.new do |s|
12
12
  s.description = "Bond is on a mission to improve irb’s autocompletion. Aside from doing everything irb’s can do and fixing its quirks, Bond can autocomplete argument(s) to methods, uniquely completing per module, per method and per argument. Bond brings irb’s completion closer to bash/zsh as it provides a configuration system and a DSL for creating custom completions and completion rules. With this configuration system, users can customize their irb autocompletions and share it with others. Bond can also generate completions from yard documentation and load completions that ship with gems. Bond is able to offer more than irb’s completion since it uses a Readline C extension to get the full line of input when completing as opposed to irb’s last-word approach."
13
13
  s.required_rubygems_version = ">= 1.3.6"
14
14
  s.rubyforge_project = 'tagaholic'
15
- s.add_development_dependency 'bacon'
16
- s.add_development_dependency 'mocha'
15
+ s.has_rdoc = 'yard'
16
+ s.rdoc_options = ['--title', "Bond #{Bond::VERSION} Documentation"]
17
+ s.add_development_dependency 'bacon', '>= 1.1.0'
18
+ s.add_development_dependency 'mocha', '>= 0.9.8'
17
19
  s.add_development_dependency 'mocha-on-bacon'
18
- s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c}]) + %w{Rakefile gemspec}
20
+ s.add_development_dependency 'bacon-bits'
21
+ s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c}]) + %w{Rakefile .gemspec}
22
+ s.files += Dir.glob('**/*.rip')
19
23
  s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
20
- s.extensions = RUBY_VERSION < '1.9.2' ? ["ext/readline_line_buffer/extconf.rb"] : []
21
- end
24
+ s.extensions = ["ext/readline_line_buffer/extconf.rb"]
25
+ s.license = 'MIT'
26
+ end
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.2.2
2
+ * Moved documentation to yard.
3
+ * Better extconf.rb thanks to @timcharper
4
+ * Better error when failing to require extension.
5
+
1
6
  == 0.2.1
2
7
  * Added Bond.load_yard_gems which generates argument completions from yard documentation.
3
8
  * Added Bond.load_gems which loads completions from gem's directory.
data/README.rdoc CHANGED
@@ -206,6 +206,7 @@ has good instructions for reinstalling ruby with the official Readline.
206
206
  * Csaba Hank for {providing the C extension}[http://www.creo.hu/~csaba/ruby/irb-enhancements/doc/files/README.html] which Bond uses to read Readline's full buffer.
207
207
  * Takao Kouji for {commiting}[http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/ext/readline/readline.c?view=diff&r1=24018&r2=24019] this Readline enhancement to ruby 1.9.2.
208
208
  * pd for compatibility with emacs' inf-ruby mode.
209
+ * timcharper for improving extconf.rb.
209
210
 
210
211
  == Links
211
212
  * http://tagaholic.me/2010/05/07/screencast-of-argument-autocompletion-for-methods-in-irb.html
data/Rakefile CHANGED
@@ -2,12 +2,12 @@ require 'rake'
2
2
  require 'fileutils'
3
3
 
4
4
  def gemspec
5
- @gemspec ||= eval(File.read('gemspec'), binding, 'gemspec')
5
+ @gemspec ||= eval(File.read('.gemspec'), binding, '.gemspec')
6
6
  end
7
7
 
8
8
  desc "Build the gem"
9
9
  task :gem=>:gemspec do
10
- sh "gem build gemspec"
10
+ sh "gem build .gemspec"
11
11
  FileUtils.mkdir_p 'pkg'
12
12
  FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
13
13
  end
@@ -32,4 +32,4 @@ task :test do |t|
32
32
  sh 'bacon -q -Ilib -I. test/*_test.rb'
33
33
  end
34
34
 
35
- task :default => :test
35
+ task :default => :test
@@ -1,4 +1,19 @@
1
1
  require "mkmf"
2
- have_header 'readline/readline.h'
3
- dir_config 'readline_line_buffer'
4
- create_makefile 'readline_line_buffer'
2
+
3
+ if RUBY_VERSION < '1.9.2'
4
+ dir_config("readline")
5
+ unless have_header('readline/readline.h')
6
+ $stderr.puts "-" * 80
7
+ $stderr.puts "Error! Cannot find readline/readline.h."
8
+ $stderr.puts "Readline was probably installed in a non-standard directory.",
9
+ "Try `gem install bond -- --with-readline-dir=/path/to/readline`."
10
+ $stderr.puts "-" * 80
11
+ exit 1
12
+ end
13
+ create_makefile 'readline_line_buffer'
14
+ else
15
+ # Create dummy Makefile to placate rubygems when running `make install`.
16
+ File.open(File.join(File.dirname(__FILE__), "Makefile"), "w") {|f|
17
+ f.puts %[install:\n\techo "This is a dummy extension"]
18
+ }
19
+ end
data/lib/bond.rb CHANGED
@@ -17,38 +17,50 @@ require 'bond/yard'
17
17
  module Bond
18
18
  extend self
19
19
 
20
- # Defines a completion rule (Mission). A valid Mission consists of a condition and an action. A
20
+ # Creates a completion rule (Mission). A valid Mission consists of a condition and an action. A
21
21
  # condition is specified with one of the following options: :on, :object, :anywhere or :method(s). Each
22
- # of these options creates a different Mission class. An action is either this method's block or :action.
22
+ # of these options creates a different Mission class. An action is either the method's block or :action.
23
23
  # An action takes what the user has typed (Input) and returns an array of possible completions. Bond
24
24
  # searches these completions and returns matching completions. This searching behavior can be configured
25
25
  # or turned off per mission with :search. If turned off, the action must also handle searching.
26
- # ====Options:
27
- # [*:on*] Regular expression which matches the full line of input to create a Mission object.
28
- # [*:method*, *:methods*, *:class*] See MethodMission.
29
- # [*:anywhere*, *:prefix*] See AnywhereMission.
30
- # [*:object*] See ObjectMission.
31
- # [*:search*] A symbol or false which determines how completions are searched. Defaults to
32
- # Search.default_search. If false, search is turned off and assumed to be done in the action.
33
- # Possible symbols are :anywhere, :ignore_case, :underscore, :normal, :files and :modules.
34
- # See Search for more info.
35
- # [*:action*] Rc method name that takes an Input and returns possible completions. See MethodMission for
36
- # specific behavior with :method(s).
37
- # [*:place*] A number or :last which indicates where a mission is inserted amongst existing missions.
38
- # If the symbol :last, places the mission at the end regardless of missions defined after
39
- # it. Multiple declarations of :last are kept last in the order they are defined.
40
- # [*:name*] A symbol or string that serves a unique id for a mission. This unique id can be passed by
41
- # Bond.recomplete to identify and replace the mission.
26
+ #
42
27
  # ==== Examples:
43
28
  # Bond.complete(:method=>'shoot') {|input| %w{to kill} }
44
29
  # Bond.complete(:on=>/^((([a-z][^:.\(]*)+):)+/, :search=>false) {|input| Object.constants.grep(/#{input.matched[1]}/) }
45
30
  # Bond.complete(:object=>ActiveRecord::Base, :search=>:underscore, :place=>:last)
46
31
  # Bond.complete(:method=>'you', :search=>proc {|input, list| list.grep(/#{input}/i)} ) {|input| %w{Only Live Twice} }
47
32
  # Bond.complete(:method=>'system', :action=>:shell_commands)
33
+ #
34
+ # @param [Hash] options When using :method(s) or :object, some hash keys may have different behavior. See
35
+ # Bond.complete sections of {MethodMission} and {ObjectMission} respectively.
36
+ # @option options [Regexp] :on Matches the full line of input to create a {Mission} object.
37
+ # @option options [String] :method An instance (Class#method) or class method (Class.method). Creates
38
+ # {MethodMission} object. A method's class can be set by :class or detected automatically if '#' or '.' is
39
+ # present. If no class is detected, 'Kernel#' is assumed.
40
+ # @option options [Array<String>] :methods Instance or class method(s) in the format of :method. Creates
41
+ # {MethodMission} objects.
42
+ # @option options [String] :class Optionally used with :method or :methods to represent module/class.
43
+ # Must end in '#' or '.' to indicate instance/class method. Suggested for use with :methods.
44
+ # @option options [String] :object Module or class of an object whose methods are completed. Creates
45
+ # {ObjectMission} object.
46
+ # @option options [String] :anywhere String representing a regular expression to match a mission. Creates
47
+ # {AnywhereMission} object.
48
+ # @option options [String] :prefix Optional string to prefix :anywhere.
49
+ # @option options [Symbol,false] :search Determines how completions are searched. Defaults to
50
+ # Search.default_search. If false, search is turned off and assumed to be done in the action.
51
+ # Possible symbols are :anywhere, :ignore_case, :underscore, :normal, :files and :modules.
52
+ # See {Search} for more info.
53
+ # @option options [String,Symbol] :action Rc method name that takes an Input and returns possible completions.
54
+ # See {MethodMission} for specific behavior with :method(s).
55
+ # @option options [Integer,:last] :place Indicates where a mission is inserted amongst existing
56
+ # missions. If the symbol :last, places the mission at the end regardless of missions defined
57
+ # after it. Multiple declarations of :last are kept last in the order they are defined.
58
+ # @option options [Symbol,String] :name Unique id for a mission which can be passed by
59
+ # Bond.recomplete to identify and replace the mission.
48
60
  def complete(options={}, &block); M.complete(options, &block); end
49
61
 
50
62
  # Redefines an existing completion mission to have a different action. The condition can only be varied if :name is
51
- # used to identify and replace a mission. Takes same options as Bond.complete.
63
+ # used to identify and replace a mission. Takes same options as {#complete}.
52
64
  # ==== Example:
53
65
  # Bond.recomplete(:on=>/man/, :name=>:count) { %w{4 5 6}}
54
66
  def recomplete(options={}, &block); M.recomplete(options, &block); end
@@ -60,41 +72,45 @@ module Bond
60
72
  # Possible completions: ["octopussy"]
61
73
  def spy(*args); M.spy(*args); end
62
74
 
63
- # Global config with the following keys:
64
- # [*:readline_plugin*] Specifies a Bond plugin to interface with a Readline-like library. Available
65
- # plugins are Readline and Rawline. Defaults to Readline.
66
- # [*:default_mission*] A proc or name of an Rc method to use as the default completion when no
67
- # missions match.
68
- # [*:default_search*] Name of a *_search method in Rc to use as the default search in completions.
69
- # Default is :underscore. See Bond.complete's :search option for valid values.
70
- # [*:eval_binding*] Specifies a binding to use when evaluating objects in ObjectMission and MethodMission.
71
- # When in irb, defaults to irb's current binding. Otherwise defaults to TOPLEVEL_BINDING.
72
- # [*:debug*] Boolean to show the stacktrace when autocompletion fails and raise exceptions in Rc.eval.
73
- # Default is false.
74
- # [*:eval_debug*] Boolean to raise eval errors occuring when finding a matching completion. Useful to debug
75
- # an incorrect completion. Default is false.
75
+ # @return [Hash] Global config
76
76
  def config; M.config; end
77
77
 
78
78
  # Starts Bond with a default set of completions that replace and improve irb's completion. Loads completions
79
79
  # in this order: lib/bond/completion.rb, lib/bond/completions/*.rb and the following optional completions:
80
80
  # completions from :gems, completions from :yard_gems, ~/.bondrc, ~/.bond/completions/*.rb and from block. See
81
- # Rc for the DSL to use in completion files and in the block. Valid options are Bond.config keys and the following:
82
- # [*:gems*] Array of gems which have their completions loaded from lib/bond/completions/*.rb.
83
- # [*:yard_gems*] Array of gems using yard documentation to generate completions. See Yard.
81
+ # {Rc} for the DSL to use in completion files and in the block.
82
+ #
84
83
  # ==== Examples:
85
84
  # Bond.start :gems=>%w{hirb}
86
85
  # Bond.start(:default_search=>:ignore_case) do
87
86
  # complete(:method=>"Object#respond_to?") {|e| e.object.methods }
88
87
  # end
88
+ #
89
+ # @param [Hash] options Sets global keys in {#config}, some which specify what completions to load.
90
+ # @option options [Array<String>] :gems Gems which have their completions loaded from
91
+ # @gem_source/lib/bond/completions/*.rb.
92
+ # @option options [Array<String>] :yard_gems Gems using yard documentation to generate completions. See {Yard}.
93
+ # @option options [Module] :readline_plugin (Bond::Readline) Specifies a Bond plugin to interface with a Readline-like
94
+ # library. Available plugins are Bond::Readline and Bond::Rawline.
95
+ # @option options [Proc] :default_mission (DefaultMission) Sets default completion to use when no missions match.
96
+ # See {Agent#default_mission}.
97
+ # @option options [Symbol] :default_search (:underscore) Name of a *_search method in Rc to use as the default
98
+ # search in completions. See {#complete}'s :search option for valid values.
99
+ # @option options [Binding] :eval_binding (TOPLEVEL_BINDING) Binding to use when evaluating objects in
100
+ # ObjectMission and MethodMission. When in irb, defaults to irb's current binding.
101
+ # @option options [Boolean] :debug (false) Shows the stacktrace when autocompletion fails and raises exceptions
102
+ # in Rc.eval.
103
+ # @option options [Boolean] :eval_debug (false) Raises eval errors occuring when finding a matching completion.
104
+ # Useful to debug an incorrect completion.
89
105
  def start(options={}, &block); M.start(options, &block); end
90
106
 
91
107
  # Loads completions for gems that ship with them under lib/bond/completions/, relative to the gem's base directory.
92
108
  def load_gems(*gems); M.load_gems(*gems); end
93
109
 
94
110
  # Generates and loads completions for yardoc documented gems.
95
- # ==== Options:
96
- # [*:verbose*] Boolean which displays additional information when building yardoc. Default is false.
97
- # [*:reload*] Rebuilds yard databases. Use when gems have changed versions. Default is false.
111
+ # @param *gems Gem(s) with optional options hash at the end
112
+ # @option *gems :verbose[Boolean] (false) Displays additional information when building yardoc.
113
+ # @option *gems :reload[Boolean] (false) Rebuilds yard databases. Use when gems have changed versions.
98
114
  def load_yard_gems(*gems); Yard.load_yard_gems(*gems); end
99
115
 
100
116
  # An Agent who saves all Bond.complete missions and executes the correct one when a completion is called.
data/lib/bond/agent.rb CHANGED
@@ -7,7 +7,7 @@ module Bond
7
7
  # An agent's best friend a.k.a. the readline plugin.
8
8
  attr_reader :weapon
9
9
 
10
- def initialize(options={}) #:nodoc:
10
+ def initialize(options={}) #@private
11
11
  setup_readline_plugin(options[:readline_plugin])
12
12
  @default_mission_action = options[:default_mission] if options[:default_mission]
13
13
  Mission.eval_binding = options[:eval_binding] if options[:eval_binding]
@@ -15,14 +15,6 @@ module Bond
15
15
  @missions = []
16
16
  end
17
17
 
18
- def setup_readline_plugin(plugin) #:nodoc:
19
- raise ArgumentError unless plugin.is_a?(Module)
20
- @weapon = plugin.extend(plugin)
21
- @weapon.setup(self)
22
- rescue
23
- $stderr.puts "Bond Error: Failed #{plugin.to_s[/[^:]+$/]} setup with '#{$!.message}'"
24
- end
25
-
26
18
  # Creates a mission.
27
19
  def complete(options={}, &block)
28
20
  if (mission = create_mission(options, &block)).is_a?(Mission)
@@ -32,14 +24,6 @@ module Bond
32
24
  mission
33
25
  end
34
26
 
35
- def create_mission(options, &block) #:nodoc:
36
- Mission.create options.merge!(:action=>options[:action] || block)
37
- rescue InvalidMissionError
38
- "Invalid #{$!.message} for completion with options: #{options.inspect}"
39
- rescue
40
- "Unexpected error while creating completion with options #{options.inspect} and message:\n#{$!}"
41
- end
42
-
43
27
  # Creates a mission and replaces the mission it matches if possible.
44
28
  def recomplete(options={}, &block)
45
29
  if (mission = create_mission(options, &block)).is_a?(Mission)
@@ -53,14 +37,6 @@ module Bond
53
37
  mission
54
38
  end
55
39
 
56
- def sort_last_missions #:nodoc:
57
- @missions.replace @missions.partition {|e| e.place != :last }.flatten
58
- end
59
-
60
- def reset #:nodoc:
61
- @missions = []
62
- end
63
-
64
40
  # This is where the action starts when a completion is initiated. Optional line_buffer
65
41
  # overrides line buffer from readline plugin.
66
42
  def call(input, line_buffer=nil)
@@ -74,12 +50,6 @@ module Bond
74
50
  "Please report this issue with debug on: Bond.config[:debug] = true."
75
51
  end
76
52
 
77
- def completion_error(desc, message) #:nodoc:
78
- arr = ["Bond Error: #{desc}", message]
79
- arr << "Stack Trace: #{$!.backtrace.inspect}" if Bond.config[:debug]
80
- arr
81
- end
82
-
83
53
  # Given a hypothetical user input, reports back what mission it would have found and executed.
84
54
  def spy(input)
85
55
  if (mission = find_mission(input))
@@ -93,7 +63,7 @@ module Bond
93
63
  "Matches for #{e.mission.condition.inspect} are #{e.mission.matched.to_a.inspect}"
94
64
  end
95
65
 
96
- def find_mission(input) #:nodoc:
66
+ def find_mission(input) #@private
97
67
  @missions.find {|mission| mission.matches?(input) }
98
68
  end
99
69
 
@@ -101,5 +71,37 @@ module Bond
101
71
  def default_mission
102
72
  @default_mission ||= DefaultMission.new(:action=>@default_mission_action)
103
73
  end
74
+
75
+ # Resets an agent's missions
76
+ def reset
77
+ @missions = []
78
+ end
79
+
80
+ protected
81
+ def setup_readline_plugin(plugin)
82
+ raise ArgumentError unless plugin.is_a?(Module)
83
+ @weapon = plugin.extend(plugin)
84
+ @weapon.setup(self)
85
+ rescue
86
+ $stderr.puts "Bond Error: Failed #{plugin.to_s[/[^:]+$/]} setup with '#{$!.message}'"
87
+ end
88
+
89
+ def create_mission(options, &block)
90
+ Mission.create options.merge!(:action=>options[:action] || block)
91
+ rescue InvalidMissionError
92
+ "Invalid #{$!.message} for completion with options: #{options.inspect}"
93
+ rescue
94
+ "Unexpected error while creating completion with options #{options.inspect} and message:\n#{$!}"
95
+ end
96
+
97
+ def sort_last_missions
98
+ @missions.replace @missions.partition {|e| e.place != :last }.flatten
99
+ end
100
+
101
+ def completion_error(desc, message)
102
+ arr = ["Bond Error: #{desc}", message]
103
+ arr << "Stack Trace: #{$!.backtrace.inspect}" if Bond.config[:debug]
104
+ arr
105
+ end
104
106
  end
105
107
  end
@@ -1,2 +1,9 @@
1
1
  complete(:methods=>%w{Bond.complete Bond.recomplete}) {
2
- %w{on action method methods class object prefix search place name} }
2
+ ["on", "method", "methods", "class", "object", "anywhere", "prefix", "search", "action", "place", "name"]
3
+ }
4
+ complete(:method=>'Bond.start') {
5
+ ["gems", "yard_gems", "readline_plugin", "default_mission", "default_search", "eval_binding", "debug", "eval_debug"]
6
+ }
7
+ complete(:method=>'Bond.load_yard_gems') {
8
+ ["verbose", "reload"]
9
+ }
data/lib/bond/input.rb CHANGED
@@ -11,7 +11,7 @@ module Bond
11
11
  attr_accessor :argument, :arguments
12
12
  # The full line the user has typed.
13
13
  attr_reader :line
14
- def initialize(str, options={}) #:nodoc:
14
+ def initialize(str, options={}) #@private
15
15
  super(str || '')
16
16
  @matched = options[:matched]
17
17
  @line = options[:line]
@@ -20,7 +20,7 @@ module Bond
20
20
  @arguments = options[:arguments] if options[:arguments]
21
21
  end
22
22
 
23
- def inspect #:nodoc:
23
+ def inspect #@private
24
24
  "#<Bond::Input #{self.to_s.inspect} @matched=#{@matched.to_a.inspect} @line=#{@line.inspect} "+
25
25
  "@argument=#{@argument.inspect} @arguments=#{@arguments.inspect} @object=#{@object.inspect}>"
26
26
  end
data/lib/bond/m.rb CHANGED
@@ -3,7 +3,7 @@ module Bond
3
3
  module M
4
4
  extend self
5
5
 
6
- # See Bond.complete
6
+ # See {Bond#complete}
7
7
  def complete(options={}, &block)
8
8
  if (result = agent.complete(options, &block)).is_a?(String)
9
9
  $stderr.puts "Bond Error: "+result
@@ -13,7 +13,7 @@ module Bond
13
13
  end
14
14
  end
15
15
 
16
- # See Bond.recomplete
16
+ # See {Bond#recomplete}
17
17
  def recomplete(options={}, &block)
18
18
  if (result = agent.recomplete(options, &block)).is_a?(String)
19
19
  $stderr.puts "Bond Error: "+result
@@ -23,12 +23,12 @@ module Bond
23
23
  end
24
24
  end
25
25
 
26
- # See Bond.agent
26
+ # See {Bond#agent}
27
27
  def agent
28
28
  @agent ||= Agent.new(config)
29
29
  end
30
30
 
31
- # See Bond.config
31
+ # See {Bond#config}
32
32
  def config
33
33
  @config ||= {:readline_plugin=>Bond::Readline, :debug=>false, :default_search=>:underscore}
34
34
  end
@@ -39,7 +39,7 @@ module Bond
39
39
  @agent = nil
40
40
  end
41
41
 
42
- # See Bond.spy
42
+ # See {Bond#spy}
43
43
  def spy(input)
44
44
  agent.spy(input)
45
45
  end
@@ -54,7 +54,7 @@ module Bond
54
54
  end
55
55
  end
56
56
 
57
- # See Bond.start
57
+ # See {Bond#start}
58
58
  def start(options={}, &block)
59
59
  debrief options
60
60
  load_completions
@@ -62,30 +62,12 @@ module Bond
62
62
  true
63
63
  end
64
64
 
65
- def load_gem_completion(rubygem) #:nodoc:
66
- (dir = find_gem_file(rubygem, File.join(rubygem, '..', 'bond'))) ?
67
- load_dir(dir) : $stderr.puts("Bond Error: No completions found for gem '#{rubygem}'.")
68
- end
69
-
70
65
  # Finds the full path to a gem's file relative it's load path directory. Returns nil if not found.
71
66
  def find_gem_file(rubygem, file)
72
67
  begin gem(rubygem); rescue Exception; end
73
68
  (dir = $:.find {|e| File.exists?(File.join(e, file)) }) && File.join(dir, file)
74
69
  end
75
70
 
76
- def load_gems(*gems) #:nodoc:
77
- gems.select {|e| load_gem_completion(e) }
78
- end
79
-
80
- def load_completions #:nodoc:
81
- load_file File.join(File.dirname(__FILE__), 'completion.rb')
82
- load_dir File.dirname(__FILE__)
83
- load_gems *config[:gems] if config[:gems]
84
- Yard.load_yard_gems *config[:yard_gems] if config[:yard_gems]
85
- load_file(File.join(home,'.bondrc')) if File.exists?(File.join(home, '.bondrc'))
86
- load_dir File.join(home, '.bond')
87
- end
88
-
89
71
  # Loads a completion file in Rc namespace.
90
72
  def load_file(file)
91
73
  Rc.module_eval File.read(file)
@@ -108,5 +90,24 @@ module Bond
108
90
  rescue
109
91
  File::ALT_SEPARATOR ? "C:/" : "/"
110
92
  end
93
+
94
+ protected
95
+ def load_gem_completion(rubygem)
96
+ (dir = find_gem_file(rubygem, File.join(rubygem, '..', 'bond'))) ?
97
+ load_dir(dir) : $stderr.puts("Bond Error: No completions found for gem '#{rubygem}'.")
98
+ end
99
+
100
+ def load_gems(*gems)
101
+ gems.select {|e| load_gem_completion(e) }
102
+ end
103
+
104
+ def load_completions
105
+ load_file File.join(File.dirname(__FILE__), 'completion.rb')
106
+ load_dir File.dirname(__FILE__)
107
+ load_gems *config[:gems] if config[:gems]
108
+ Yard.load_yard_gems *config[:yard_gems] if config[:yard_gems]
109
+ load_file(File.join(home,'.bondrc')) if File.exists?(File.join(home, '.bondrc'))
110
+ load_dir File.join(home, '.bond')
111
+ end
111
112
  end
112
113
  end
data/lib/bond/mission.rb CHANGED
@@ -5,7 +5,7 @@ module Bond
5
5
  class FailedMissionError < StandardError
6
6
  # Mission that failed
7
7
  attr_reader :mission
8
- def initialize(mission); @mission = mission; end #:nodoc:
8
+ def initialize(mission); @mission = mission; end #@private
9
9
  end
10
10
 
11
11
  # Represents a completion rule, given a condition (:on) on which to match and an action
@@ -30,7 +30,7 @@ module Bond
30
30
  eval(string, ebinding)
31
31
  end
32
32
 
33
- def eval_binding #:nodoc:
33
+ def eval_binding #@private
34
34
  @eval_binding || IRB.CurrentContext.workspace.binding rescue ::TOPLEVEL_BINDING
35
35
  end
36
36
  end
@@ -44,13 +44,13 @@ module Bond
44
44
  # Generates array of possible completions and searches them if search is disabled. Any values
45
45
  # that aren't strings are automatically converted with to_s.
46
46
  attr_reader :action
47
- # See Bond.complete's :place.
47
+ # See {Bond#complete}'s :place.
48
48
  attr_reader :place
49
49
  # A MatchData object generated from matching the user input with the condition.
50
50
  attr_reader :matched
51
51
  # Regexp condition
52
52
  attr_reader :on
53
- # Takes same options as Bond.complete.
53
+ # Takes same options as {Bond#complete}.
54
54
  def initialize(options)
55
55
  raise InvalidMissionError, ":action" unless (options[:action] || respond_to?(:default_action))
56
56
  raise InvalidMissionError, ":on" unless (options[:on] && options[:on].is_a?(Regexp)) || respond_to?(:default_on)
@@ -126,7 +126,7 @@ module Bond
126
126
  create_input(input[/\S+$/])
127
127
  end
128
128
 
129
- #:stopdoc:
129
+ private
130
130
  def condition_with_objects
131
131
  self.class.const_get(:CONDITION).sub('OBJECTS', self.class.const_get(:OBJECTS).join('|'))
132
132
  end
@@ -150,6 +150,5 @@ module Bond
150
150
  def create_input(input, options={})
151
151
  @input = Input.new(input, options.merge(:line=>@line, :matched=>@matched))
152
152
  end
153
- #:startdoc:
154
153
  end
155
154
  end
@@ -1,17 +1,13 @@
1
1
  # A mission which completes anywhere i.e. even after non word break characters such as '[' or '}'.
2
- # It generates the following regexp condition /#{prefix}(#{anywhere})$/ and passes the first
3
- # capture group to the mission action.
4
- #
5
- # ==== Bond.complete Options:
6
- # [*:anywhere*] A regexp string which generates the first capture group in the above regexp.
7
- # [*:prefix*] An optional string which prefixes the first capture group in the above regexp.
2
+ # With options :prefix and :anywhere, this mission matches on the following regexp condition
3
+ # /:prefix?(:anywhere)$/ and passes the first capture group to the mission action.
8
4
  class Bond::AnywhereMission < Bond::Mission
9
- def initialize(options={}) #:nodoc:
5
+ def initialize(options={}) #@private
10
6
  options[:on] = Regexp.new("#{options[:prefix]}(#{options[:anywhere]})$")
11
7
  super
12
8
  end
13
9
 
14
- def after_match(input) #:nodoc:
10
+ def after_match(input) #@private
15
11
  @completion_prefix = input.to_s.sub(/#{Regexp.escape(@matched[1])}$/, '')
16
12
  create_input @matched[1]
17
13
  end
@@ -6,13 +6,11 @@ class Bond::DefaultMission < Bond::Mission
6
6
  "then", "true", "undef", "unless", "until", "when", "while", "yield"
7
7
  ]
8
8
 
9
- #:stopdoc:
10
- def initialize(options={})
9
+ def initialize(options={}) #@private
11
10
  options[:action] ||= method(:default)
12
11
  super
13
12
  end
14
- def default_on; end
15
- #:startdoc:
13
+ def default_on; end #@private
16
14
 
17
15
  # Default action which generates methods, private methods, reserved words, local variables and constants.
18
16
  def default(input)
@@ -5,24 +5,13 @@ module Bond
5
5
  # has two ancestors that have completions for the same method, the ancestor closer to the object is
6
6
  # picked. For example, if Array#collect and Enumerable#collect have completions, argument completion on
7
7
  # '[].collect ' would use Array#collect.
8
- #--
9
- # Unlike other missions, creating these missions with Bond.complete doesn't add more completion rules
10
- # for an Agent to look through. Instead, all :method(s) completions are handled by one MethodMission
11
- # object which looks them up with its own hashes. In the same way, all operator methods are
12
- # handled by one OperatorMethodMission object.
13
- #++
8
+ #
14
9
  # ==== Bond.complete Options:
15
- # [*:method*] String representing an instance (Class#method) or class method (Class.method). Gets
16
- # its class from :class or from substring prefixing '#' or '.'. If no class is given,
17
- # 'Kernel#' is assumed.
18
- # [*:methods*] Array of instance and/or class methods in the format of :method.
19
- # [*:class*] Optional string representing module/class of :method(s). Must end in '#' or '.' to
20
- # indicate instance/class method. Suggested for use with :methods.
21
- # [*:action*] If a string, value is assumed to be a :method and that method's action is copied.
22
- # Otherwise defaults to normal :action behavior.
23
- # [*:search*] If :action is a :method string, defaults to copying its search.
24
- # Otherwise defaults to normal :search behavior.
25
- # [*:name*, *:place*] These options aren't supported by a MethodMission/OperatorMethodMission completion.
10
+ # [:action] If a string, value is assumed to be a :method and that method's action is copied.
11
+ # Otherwise defaults to normal :action behavior.
12
+ # [:search] If :action is a :method string, defaults to copying its search.
13
+ # Otherwise defaults to normal :search behavior.
14
+ # [:name, :place] These options aren't supported by a MethodMission/OperatorMethodMission completion.
26
15
  # ==== Examples:
27
16
  # Bond.complete(:methods=>%w{delete index rindex}, :class=>"Array#") {|e| e.object }
28
17
  # Bond.complete(:method=>"Hash#index") {|e| e.object.values }
@@ -54,6 +43,12 @@ module Bond
54
43
  # >> FileUtils.chown 'root', 'admin', 'some_file', :v[TAB]
55
44
  # >> FileUtils.chown 'root', 'admin', 'some_file', :verbose
56
45
  # >> FileUtils.chown 'root', 'admin', 'some_file', :verbose=>true
46
+ #
47
+ # ==== Developer Notes
48
+ # Unlike other missions, creating these missions with Bond.complete doesn't add more completion rules
49
+ # for an Agent to look through. Instead, all :method(s) completions are handled by one MethodMission
50
+ # object which looks them up with its own hashes. In the same way, all operator methods are
51
+ # handled by one OperatorMethodMission object.
57
52
  class MethodMission < Bond::Mission
58
53
  class<<self
59
54
  # Hash of instance method completions which maps methods to hashes of modules to arrays ([action, search])
@@ -97,25 +92,17 @@ module Bond
97
92
  @class_actions = {}
98
93
  end
99
94
 
100
- def action_methods #:nodoc:
95
+ # Lists method names
96
+ def action_methods
101
97
  (actions.keys + class_actions.keys).uniq
102
98
  end
103
99
 
104
- # Lists all methods that have argument completions.
100
+ # Lists full method names, prefixed with class/module
105
101
  def all_methods
106
102
  (class_actions.map {|m,h| h.map {|k,v| "#{k}.#{m}" } } +
107
103
  actions.map {|m,h| h.map {|k,v| "#{k}##{m}" } }).flatten.sort
108
104
  end
109
105
 
110
- def current_actions(meth) #:nodoc:
111
- meth.include?('.') ? @class_actions : @actions
112
- end
113
-
114
- def split_method(meth) #:nodoc:
115
- meth = "Kernel##{meth}" if !meth.to_s[/[.#]/]
116
- meth.split(/[.#]/,2)
117
- end
118
-
119
106
  # Returns the first completion by looking up the object's ancestors and finding the closest
120
107
  # one that has a completion definition for the given method. Completion is returned
121
108
  # as an array containing action proc and optional search to go with it.
@@ -126,16 +113,6 @@ module Bond
126
113
  @last_find = last_find ? last_find[1] : last_find
127
114
  end
128
115
 
129
- def find_with(obj, meth, find_meth, actions) #:nodoc:
130
- (actions[meth] || {}).select {|k,v| get_class(k) }.
131
- sort {|a,b| get_class(a[0]) <=> get_class(b[0]) || -1 }.
132
- find {|k,v| obj.send(find_meth, get_class(k)) }
133
- end
134
-
135
- def get_class(klass) #:nodoc:
136
- (@klasses ||= {})[klass] ||= any_const_get(klass)
137
- end
138
-
139
116
  # Returns a constant like Module#const_get no matter what namespace it's nested in.
140
117
  # Returns nil if the constant is not found.
141
118
  def any_const_get(name)
@@ -146,13 +123,37 @@ module Bond
146
123
  rescue
147
124
  nil
148
125
  end
126
+
127
+ protected
128
+ def current_actions(meth)
129
+ meth.include?('.') ? @class_actions : @actions
130
+ end
131
+
132
+ def split_method(meth)
133
+ meth = "Kernel##{meth}" if !meth.to_s[/[.#]/]
134
+ meth.split(/[.#]/,2)
135
+ end
136
+
137
+ def find_with(obj, meth, find_meth, actions)
138
+ (actions[meth] || {}).select {|k,v| get_class(k) }.
139
+ sort {|a,b| get_class(a[0]) <=> get_class(b[0]) || -1 }.
140
+ find {|k,v| obj.send(find_meth, get_class(k)) }
141
+ end
142
+
143
+ def get_class(klass)
144
+ (@klasses ||= {})[klass] ||= any_const_get(klass)
145
+ end
149
146
  end
150
147
 
151
148
  self.reset
152
149
  OBJECTS = Mission::OBJECTS + %w{\S*?}
153
150
  CONDITION = %q{(OBJECTS)\.?(METHODS)(?:\s+|\()(['":])?(.*)$}
154
151
 
155
- #:stopdoc:
152
+ def match_message #@private
153
+ "Matches completion for method '#{@meth}' in '#{MethodMission.last_class}'."
154
+ end
155
+
156
+ protected
156
157
  def do_match(input)
157
158
  (@on = default_on) && super && eval_object(@matched[1] ? @matched[1] : 'self') &&
158
159
  MethodMission.find(@evaled_object, @meth = matched_method)
@@ -192,9 +193,5 @@ module Bond
192
193
  create_input typed, input_options
193
194
  end
194
195
 
195
- def match_message
196
- "Matches completion for method '#{@meth}' in '#{MethodMission.last_class}'."
197
- end
198
- #:startdoc:
199
196
  end
200
197
  end
@@ -4,22 +4,25 @@
4
4
  # object attribute.
5
5
  #
6
6
  # ==== Bond.complete Options:
7
- # [*:object*] String representing a module/class of object whose methods are completed.
8
- # [*:action*] If an action is not specified, the default action is to complete an object's
7
+ # [:action] If an action is not specified, the default action is to complete an object's
9
8
  # non-operator methods.
10
9
  #
11
10
  # ===== Example:
12
- # Bond.complete(:object=>ActiveRecord::Base) {|input| input.object.class.instance_methods(false) }
11
+ # Bond.complete(:object=>'ActiveRecord::Base') {|input| input.object.class.instance_methods(false) }
13
12
  class Bond::ObjectMission < Bond::Mission
14
- #:stopdoc:
15
13
  OBJECTS = %w<\S+> + Bond::Mission::OBJECTS
16
14
  CONDITION = '(OBJECTS)\.(\w*(?:\?|!)?)$'
17
- def initialize(options={})
15
+ def initialize(options={}) #@private
18
16
  @object_condition = /^#{options[:object]}$/
19
17
  options[:on] ||= Regexp.new condition_with_objects
20
18
  super
21
19
  end
22
20
 
21
+ def match_message #@private
22
+ "Matches completion for object with ancestor matching #{@object_condition.inspect}."
23
+ end
24
+
25
+ protected
23
26
  def unique_id
24
27
  "#{@object_condition.inspect}+#{@on.inspect}"
25
28
  end
@@ -38,9 +41,4 @@ class Bond::ObjectMission < Bond::Mission
38
41
  def default_action(obj)
39
42
  obj.methods.map {|e| e.to_s} - OPERATORS
40
43
  end
41
-
42
- def match_message
43
- "Matches completion for object with ancestor matching #{@object_condition.inspect}."
44
- end
45
- #:startdoc:
46
44
  end
@@ -7,7 +7,7 @@ module Bond
7
7
  OBJECTS = Mission::OBJECTS + %w{\S+}
8
8
  CONDITION = %q{(OBJECTS)\s*(METHODS)\s*(['":])?(.*)$}
9
9
 
10
- #:stopdoc:
10
+ protected
11
11
  def current_methods
12
12
  (OPERATORS & MethodMission.action_methods) + ['[']
13
13
  end
@@ -21,6 +21,5 @@ module Bond
21
21
  @completion_prefix, typed = input.to_s.sub(/#{Regexp.quote(@matched[-1])}$/, ''), @matched[-1]
22
22
  create_input typed, :object=>@evaled_object, :argument=>1
23
23
  end
24
- #:startdoc:
25
24
  end
26
25
  end
data/lib/bond/rc.rb CHANGED
@@ -18,9 +18,9 @@ module Bond
18
18
  module Rc
19
19
  extend self, Search
20
20
 
21
- # See Bond.complete
21
+ # See {Bond#complete}
22
22
  def complete(*args, &block); M.complete(*args, &block); end
23
- # See Bond.recomplete
23
+ # See {Bond#recomplete}
24
24
  def recomplete(*args, &block); M.recomplete(*args, &block); end
25
25
 
26
26
  # Action method with search which returns array of files that match current input.
data/lib/bond/readline.rb CHANGED
@@ -24,24 +24,7 @@ module Bond
24
24
  def load_extension
25
25
  require 'readline_line_buffer'
26
26
  rescue LoadError
27
- $stderr.puts "Bond Error: Failed to load readline_line_buffer extension. Falling back on RubyInline extension."
28
- require 'inline'
29
- eval %[
30
- module ::Readline
31
- inline do |builder|
32
- %w(<errno.h> <stdio.h> <readline/readline.h>).each{|h| builder.include h }
33
- builder.c_raw_singleton <<-EOC
34
- static VALUE line_buffer(VALUE self)
35
- {
36
- rb_secure(4);
37
- if (rl_line_buffer == NULL)
38
- return Qnil;
39
- return rb_tainted_str_new2(rl_line_buffer);
40
- }
41
- EOC
42
- end
43
- end
44
- ]
27
+ $stderr.puts "Bond Error: Failed to load readline_line_buffer.bundle. Ensure that it exists and was built correctly."
45
28
  end
46
29
 
47
30
  # Returns full line of what the user has typed.
data/lib/bond/search.rb CHANGED
@@ -37,7 +37,7 @@ module Bond
37
37
  # at the beginning of an underscored word. For example, to choose the first completion between 'so_long'
38
38
  # and 'so_larger', type 's_lo'.
39
39
  def underscore_search(input, list)
40
- if input[/_(.+)$/]
40
+ if input[/_([^_]+)$/]
41
41
  regex = input.split('_').map {|e| Regexp.escape(e) }.join("([^_]+)?_")
42
42
  list.select {|e| e =~ /^#{regex}/ }
43
43
  else
data/lib/bond/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Bond
2
- VERSION = '0.2.1'
2
+ VERSION = '0.2.2'
3
3
  end
data/lib/bond/yard.rb CHANGED
@@ -4,7 +4,7 @@ module Bond
4
4
  module Yard
5
5
  extend self
6
6
 
7
- # :stopdoc:
7
+ # Loads completions for yard gem(s)
8
8
  def load_yard_gems(*gems)
9
9
  @options = gems[-1].is_a?(Hash) ? gems.pop : {}
10
10
  require 'yard'
@@ -14,6 +14,7 @@ module Bond
14
14
  $stderr.puts "Bond Error: yard gem (version >= 0.5.2) not installed "
15
15
  end
16
16
 
17
+ protected
17
18
  def load_yard_gem(rubygem)
18
19
  raise("Unable to find gem.") unless (yardoc = find_yardoc(rubygem))
19
20
  completion_file = File.join(dir('yard_completions'), rubygem+'.rb')
@@ -68,6 +69,5 @@ module Bond
68
69
  %Q[complete(:method=>'#{meth}') {\n #{options.inspect}\n}]
69
70
  end.join("\n")
70
71
  end
71
- #:startdoc:
72
72
  end
73
73
  end
data/test/deps.rip ADDED
@@ -0,0 +1,4 @@
1
+ bacon >=1.1.0
2
+ mocha >=0.9.8
3
+ mocha-on-bacon >=0
4
+ bacon-bits >=0
data/test/search_test.rb CHANGED
@@ -38,6 +38,14 @@ describe "Search" do
38
38
  tab("blah i").should == ["include?", "instance_variable_defined?", "include_and_exclude?"]
39
39
  end
40
40
 
41
+ it "underscore search autocompletes strings starting with __" do
42
+ completions = ["include?", "__id__", "__send__"]
43
+ complete(:on=>/blah/, :search=>:underscore) { completions }
44
+ tab('blah _').should == ["__id__", "__send__"]
45
+ tab('blah __').should == ["__id__", "__send__"]
46
+ tab('blah __i').should == ["__id__"]
47
+ end
48
+
41
49
  it "underscore search can match first unique strings of each underscored word" do
42
50
  completions = %w{so_long so_larger so_louder}
43
51
  complete(:on=>/blah/, :search=>:underscore) { completions }
data/test/test_helper.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'bacon'
2
- require File.dirname(__FILE__)+'/bacon_extensions'
2
+ require 'bacon/bits'
3
3
  require 'mocha'
4
4
  require 'mocha-on-bacon'
5
5
  require 'bond'
@@ -56,9 +56,8 @@ end
56
56
 
57
57
  class Bacon::Context
58
58
  include TestHelpers
59
- include BaconExtensions
60
59
  end
61
60
 
62
61
  # Default settings
63
62
  Bond::M.debrief(:readline_plugin=>TestHelpers.valid_readline_plugin, :debug=>true)
64
- include Bond
63
+ include Bond
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bond
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 19
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 2
8
- - 1
9
- version: 0.2.1
9
+ - 2
10
+ version: 0.2.2
10
11
  platform: ruby
11
12
  authors:
12
13
  - Gabriel Horner
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-05-19 00:00:00 -04:00
18
+ date: 2010-07-17 00:00:00 -04:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
@@ -25,9 +26,12 @@ dependencies:
25
26
  requirements:
26
27
  - - ">="
27
28
  - !ruby/object:Gem::Version
29
+ hash: 19
28
30
  segments:
31
+ - 1
32
+ - 1
29
33
  - 0
30
- version: "0"
34
+ version: 1.1.0
31
35
  type: :development
32
36
  version_requirements: *id001
33
37
  - !ruby/object:Gem::Dependency
@@ -38,9 +42,12 @@ dependencies:
38
42
  requirements:
39
43
  - - ">="
40
44
  - !ruby/object:Gem::Version
45
+ hash: 43
41
46
  segments:
42
47
  - 0
43
- version: "0"
48
+ - 9
49
+ - 8
50
+ version: 0.9.8
44
51
  type: :development
45
52
  version_requirements: *id002
46
53
  - !ruby/object:Gem::Dependency
@@ -51,11 +58,26 @@ dependencies:
51
58
  requirements:
52
59
  - - ">="
53
60
  - !ruby/object:Gem::Version
61
+ hash: 3
54
62
  segments:
55
63
  - 0
56
64
  version: "0"
57
65
  type: :development
58
66
  version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: bacon-bits
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id004
59
81
  description: "Bond is on a mission to improve irb\xE2\x80\x99s autocompletion. Aside from doing everything irb\xE2\x80\x99s can do and fixing its quirks, Bond can autocomplete argument(s) to methods, uniquely completing per module, per method and per argument. Bond brings irb\xE2\x80\x99s completion closer to bash/zsh as it provides a configuration system and a DSL for creating custom completions and completion rules. With this configuration system, users can customize their irb autocompletions and share it with others. Bond can also generate completions from yard documentation and load completions that ship with gems. Bond is able to offer more than irb\xE2\x80\x99s completion since it uses a Readline C extension to get the full line of input when completing as opposed to irb\xE2\x80\x99s last-word approach."
60
82
  email: gabriel.horner@gmail.com
61
83
  executables: []
@@ -93,7 +115,6 @@ files:
93
115
  - lib/bond.rb
94
116
  - test/agent_test.rb
95
117
  - test/anywhere_mission_test.rb
96
- - test/bacon_extensions.rb
97
118
  - test/bond_test.rb
98
119
  - test/completion_test.rb
99
120
  - test/completions_test.rb
@@ -110,14 +131,16 @@ files:
110
131
  - ext/readline_line_buffer/extconf.rb
111
132
  - ext/readline_line_buffer/readline_line_buffer.c
112
133
  - Rakefile
113
- - gemspec
114
- has_rdoc: true
134
+ - .gemspec
135
+ - test/deps.rip
136
+ has_rdoc: yard
115
137
  homepage: http://tagaholic.me/bond/
116
- licenses: []
117
-
138
+ licenses:
139
+ - MIT
118
140
  post_install_message:
119
- rdoc_options: []
120
-
141
+ rdoc_options:
142
+ - --title
143
+ - Bond 0.2.2 Documentation
121
144
  require_paths:
122
145
  - lib
123
146
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -125,6 +148,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
148
  requirements:
126
149
  - - ">="
127
150
  - !ruby/object:Gem::Version
151
+ hash: 3
128
152
  segments:
129
153
  - 0
130
154
  version: "0"
@@ -133,6 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
157
  requirements:
134
158
  - - ">="
135
159
  - !ruby/object:Gem::Version
160
+ hash: 23
136
161
  segments:
137
162
  - 1
138
163
  - 3
@@ -1,26 +0,0 @@
1
- module BaconExtensions
2
- def self.included(mod)
3
- mod.module_eval do
4
- # nested context methods automatically inherit methods from parent contexts
5
- def describe(*args, &block)
6
- context = Bacon::Context.new(args.join(' '), &block)
7
- (parent_context = self).methods(false).each {|e|
8
- class<<context; self end.send(:define_method, e) {|*args| parent_context.send(e, *args)}
9
- }
10
- @before.each { |b| context.before(&b) }
11
- @after.each { |b| context.after(&b) }
12
- context.run
13
- end
14
- end
15
- end
16
-
17
- def xit(*args); end
18
- def xdescribe(*args); end
19
- def before_all; yield; end
20
- def after_all; yield; end
21
- def assert(description, &block)
22
- it(description) do
23
- block.call.should == true
24
- end
25
- end
26
- end