pry 0.9.0pre3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,22 @@
1
+ 17/6/2011 version 0.9.0
2
+ * plugin system
3
+ * regex commands
4
+ * show-method works on methods defined in REPL
5
+ * new command system/API
6
+ * rubinius core support
7
+ * inp/out special locals
8
+ * _ex_ backtrace navigation object (_ex_.line, _ex_.file)
9
+ * readline history saving/loading
10
+ * prompt stack
11
+ * more hooks
12
+ * amend-line
13
+ * play
14
+ * show-input
15
+ * edit
16
+ * much more comprehensive test suite
17
+ * support for new and old rubygems API
18
+ * changed -s behaviour of ls (now excludes Object methods)
19
+
1
20
  26/3/2011 version 0.7.6.1
2
21
  * added slightly better support for YARD
3
22
  * now @param and @return tags are colored green and markdown `code` is syntax highlighted using coderay
@@ -24,11 +43,11 @@
24
43
  * --color mode for pry commandline
25
44
  * clean up requires (put them all in one place)
26
45
  * simple-prompt command and toggle-color commandd.
27
-
46
+
28
47
  28/2/2011 version 0.6.3
29
48
  * Using MethodSource 0.3.4 so 1.8 show-method support provided
30
49
  * `Set` class added to list of classes that are inspected
31
-
50
+
32
51
  26/2/2011 version 0.6.1
33
52
  * !@ command alias for exit_all
34
53
  * `cd /` for breaking out to pry top level (jump-to 0)
@@ -40,11 +59,11 @@
40
59
  22/2/2011 version 0.5.8
41
60
  * Added -c (context) option to show-doc, show-methods and eval-file
42
61
  * Fixed up ordering issue of -c and -r parameters to command line pry
43
-
62
+
44
63
  21/2/2011 version 0.5.7
45
64
  * Added pry executable, auto-loads .pryrc in user's home directory, if it
46
65
  exists.
47
-
66
+
48
67
  19/2/2011 version 0.5.5
49
68
  * Added Pry.run_command
50
69
  * More useful error messages
@@ -65,7 +84,7 @@
65
84
  * Get rid of ls_method and ls_imethods (subsumed by more powerful ls)
66
85
  * Get rid of show_idoc and show_imethod
67
86
  * Add special eval-file command that evals target file in current context
68
-
87
+
69
88
  27/1/2011 version 0.4.5
70
89
  * fixed show_method (though fragile as it references __binding_impl__
71
90
  directly, making a name change to that method difficult
@@ -99,4 +118,4 @@
99
118
  * now rescuing SyntaxError as well as Racc::Parser error in valid_expression?
100
119
  8/12/2010 version 0.1.0
101
120
  * release!
102
-
121
+
@@ -4,6 +4,15 @@
4
4
 
5
5
  _Get to the code_
6
6
 
7
+ ## These docs are now out of date for the 0.9.0 release.
8
+
9
+ ** New documentation will be available shortly, please see the CHANGELOG and Pry's own livehelp system for information in the meantime.
10
+
11
+ Also note that JRuby is not yet supported in this release, but will be soon.
12
+
13
+ Thanks **
14
+
15
+
7
16
  Pry is a powerful alternative to the standard IRB shell for Ruby. It is
8
17
  written from scratch to provide a number of advanced features, some of
9
18
  these include:
@@ -549,8 +558,6 @@ Problems or questions contact me at [github](http://github.com/banister)
549
558
  The Pry team consists of:
550
559
 
551
560
  * [banisterfiend](http://github.com/banister)
552
- * [epitron](http://github.com/epitron)
553
561
  * [injekt](http://github.com/injekt)
554
562
  * [Mon_Ouie](http://github.com/mon-ouie)
555
-
556
-
563
+ * [Rob Gleeson](https://github.com/robgleeson)
data/Rakefile CHANGED
@@ -22,12 +22,13 @@ def apply_spec_defaults(s)
22
22
  s.test_files = `git ls-files -- test/*`.split("\n")
23
23
  s.add_dependency("ruby_parser",">=2.0.5")
24
24
  s.add_dependency("coderay",">=0.9.8")
25
- s.add_dependency("slop","~>1.6.0")
26
- s.add_dependency("method_source",">=0.4.0")
25
+ s.add_dependency("slop","~>1.9.0")
26
+ s.add_dependency("method_source",">=0.6.0")
27
27
  s.add_development_dependency("bacon",">=1.1.0")
28
28
  s.add_development_dependency("open4", "~>1.0.1")
29
29
  end
30
30
 
31
+ desc "Run tests"
31
32
  task :test do
32
33
  sh "bacon -Itest -rubygems -a"
33
34
  end
@@ -52,10 +53,10 @@ namespace :ruby do
52
53
  pkg.need_zip = false
53
54
  pkg.need_tar = false
54
55
  end
55
-
56
+
56
57
  desc "Generate gemspec file"
57
58
  task :gemspec do
58
- File.open("#{spec.name}-#{spec.version}.gemspec", "w") do |f|
59
+ File.open("#{spec.name}.gemspec", "w") do |f|
59
60
  f << spec.to_ruby
60
61
  end
61
62
  end
@@ -76,20 +77,8 @@ end
76
77
  end
77
78
  end
78
79
 
79
- namespace :jruby do
80
- spec = Gem::Specification.new do |s|
81
- apply_spec_defaults(s)
82
- s.platform = "java"
83
- end
84
-
85
- Rake::GemPackageTask.new(spec) do |pkg|
86
- pkg.need_zip = false
87
- pkg.need_tar = false
88
- end
89
- end
90
-
91
80
  desc "build all platform gems at once"
92
- task :gems => [:clean, :rmgems, "ruby:gem", "jruby:gem", "mswin32:gem", "mingw32:gem"]
81
+ task :gems => [:clean, :rmgems, "ruby:gem", "mswin32:gem", "mingw32:gem"]
93
82
 
94
83
  desc "remove all platform gems"
95
84
  task :rmgems => ["ruby:clobber_package"]
data/bin/pry CHANGED
@@ -39,6 +39,8 @@ See: `https://github.com/banister` for more information.
39
39
  Pry.config.plugins.enabled = false
40
40
  end
41
41
 
42
+ on "installed-plugins", "List installed plugins."
43
+
42
44
  on "simple-prompt", "Enable simple prompt mode" do
43
45
  Pry.prompt = Pry::SIMPLE_PROMPT
44
46
  end
@@ -63,6 +65,15 @@ See: `https://github.com/banister` for more information.
63
65
  )
64
66
  end
65
67
 
68
+ if opts["installed-plugins"]
69
+ puts "Installed Plugins:"
70
+ puts "--"
71
+ Pry.locate_plugins.each do |plugin|
72
+ puts "#{plugin.name}".ljust(18) + plugin.spec.summary
73
+ end
74
+ exit
75
+ end
76
+
66
77
  # invoked via cli
67
78
  Pry.cli = true
68
79
 
data/lib/pry.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # MIT License
3
3
 
4
4
  require 'pp'
5
-
5
+ require 'pry/helpers/base_helpers'
6
6
  class Pry
7
7
  # The default hooks - display messages when beginning and ending Pry sessions.
8
8
  DEFAULT_HOOKS = {
@@ -20,11 +20,7 @@ class Pry
20
20
 
21
21
  # The default prints
22
22
  DEFAULT_PRINT = proc do |output, value|
23
- if Pry.color
24
- output.puts "=> #{CodeRay.scan(value.pretty_inspect, :ruby).term}"
25
- else
26
- output.puts "=> #{Pry.view(value)}"
27
- end
23
+ Helpers::BaseHelpers.stagger_output("=> #{Helpers::BaseHelpers.colorize_code(value.pretty_inspect)}", output)
28
24
  end
29
25
 
30
26
  # Will only show the first line of the backtrace
@@ -53,7 +49,7 @@ class Pry
53
49
  ]
54
50
 
55
51
  # A simple prompt - doesn't display target or nesting level
56
- SIMPLE_PROMPT = [proc { ">> " }, proc { ">* " }]
52
+ SIMPLE_PROMPT = [proc { ">> " }, proc { " | " }]
57
53
 
58
54
  SHELL_PROMPT = [
59
55
  proc { |target_self, _| "pry #{Pry.view_clip(target_self)}:#{Dir.pwd} $ " },
@@ -14,9 +14,11 @@ class Pry
14
14
 
15
15
  # Is the string a valid command?
16
16
  # @param [String] val The string passed in from the Pry prompt.
17
+ # @param [Binding] target The context where the string should be
18
+ # interpolated in.
17
19
  # @return [Boolean] Whether the string is a valid command.
18
- def valid_command?(val)
19
- !!(command_matched(val, binding)[0])
20
+ def valid_command?(val, target=binding)
21
+ !!(command_matched(val, target)[0])
20
22
  end
21
23
 
22
24
  # Convert the object to a form that can be interpolated into a
@@ -34,7 +36,7 @@ class Pry
34
36
  # Revaluate the string (str) and perform interpolation.
35
37
  # @param [String] str The string to reevaluate with interpolation.
36
38
  # @param [Binding] target The context where the string should be
37
- # reevaluated in.
39
+ # interpolated in.
38
40
  # @return [String] The reevaluated string with interpolations
39
41
  # applied (if any).
40
42
  def interpolate_string(str, target)
@@ -53,11 +55,18 @@ class Pry
53
55
  def command_matched(val, target)
54
56
  _, cmd_data = commands.commands.find do |name, data|
55
57
 
56
- interp_val = interpolate_string(val, target)
57
58
  command_regex = /^#{convert_to_regex(name)}(?!\S)/
58
59
 
59
- if data.options[:interpolate] && (command_regex =~ interp_val)
60
- val.replace interp_val
60
+ if data.options[:interpolate]
61
+ # If interpolation fails then the command cannot be matched,
62
+ # so early exit.
63
+ begin
64
+ interp_val = interpolate_string(val, target)
65
+ rescue NameError
66
+ next
67
+ end
68
+
69
+ val.replace interp_val if command_regex =~ interp_val
61
70
  else
62
71
  command_regex =~ val
63
72
  end
@@ -80,7 +89,10 @@ class Pry
80
89
  # no command was matched, so return to caller
81
90
  command, captures, pos = command_matched(val, target)
82
91
  return if !command
83
- arg_string = val[pos..-1].strip
92
+ arg_string = val[pos..-1]
93
+
94
+ # remove the one leading space if it exists
95
+ arg_string.slice!(0) if arg_string.start_with?(" ")
84
96
 
85
97
  args = arg_string ? Shellwords.shellwords(arg_string) : []
86
98
 
@@ -22,8 +22,5 @@ class Pry
22
22
  import DefaultCommands::Shell
23
23
  import DefaultCommands::Introspection
24
24
  import DefaultCommands::EasterEggs
25
-
26
- # Helpers::CommandHelpers.try_to_load_pry_doc
27
-
28
25
  end
29
26
  end
@@ -75,9 +75,9 @@ class Pry
75
75
  # sub-options include hist.file, hist.load, and hist.save
76
76
  # hist.file is the file to save/load history too, e.g
77
77
  # Pry.config.history.file = "~/.pry_history".
78
- # hist.load is a boolean that determines whether history will be
78
+ # hist.should_load is a boolean that determines whether history will be
79
79
  # loaded from hist.file at session start.
80
- # hist.save is a boolean that determines whether history will be
80
+ # hist.should_save is a boolean that determines whether history will be
81
81
  # saved to hist.file at session end.
82
82
  # @return [OpenStruct]
83
83
  attr_accessor :history
@@ -89,8 +89,12 @@ class Pry
89
89
  # @return [OpenStruct]
90
90
  attr_accessor :plugins
91
91
 
92
- # @return [Integer] Amount of results that will be stored into _out_
92
+ # @return [Integer] Amount of results that will be stored into out
93
93
  attr_accessor :memory_size
94
+
95
+ # @return [Boolean] Whether or not evalation results (`=>`) are sent
96
+ # through a pager.
97
+ attr_accessor :result_pager
94
98
  end
95
99
  end
96
100
 
@@ -67,6 +67,7 @@ class Pry
67
67
  alias_command "!!@", "exit-all", ""
68
68
 
69
69
  command "exit-program", "End the current program. Aliases: quit-program, !!!" do
70
+ Pry.active_instance.save_history if Pry.config.history.should_save
70
71
  exit
71
72
  end
72
73
 
@@ -40,13 +40,16 @@ class Pry
40
40
  next output.puts("No documentation found.") if doc.empty?
41
41
  doc = process_comment_markup(doc, code_type)
42
42
  output.puts make_header(meth, code_type, doc)
43
+ if meth.respond_to?(:parameters)
44
+ output.puts "#{text.bold("signature")}: #{signature_for(meth)}"
45
+ output.puts
46
+ end
43
47
  render_output(opts.flood?, false, doc)
44
48
  doc
45
49
  end
46
50
 
47
51
  alias_command "?", "show-doc", ""
48
52
 
49
-
50
53
  command "stat", "View method information and set _file_ and _dir_ locals. Type `stat --help` for more info." do |*args|
51
54
  target = target()
52
55
 
@@ -73,23 +76,21 @@ class Pry
73
76
  next
74
77
  end
75
78
 
76
- code, code_type = code_and_code_type_for(meth)
77
- next if !code
78
- doc, code_type = doc_and_code_type_for(meth)
79
+ if !is_a_c_method?(meth) && !is_a_dynamically_defined_method?(meth)
80
+ set_file_and_dir_locals(path_line_for(meth).first)
81
+ end
79
82
 
80
- output.puts make_header(meth, code_type, code)
81
- output.puts text.bold("Method Name: ") + meth_name
82
- output.puts text.bold("Method Owner: ") + (meth.owner.to_s ? meth.owner.to_s : "Unknown")
83
- output.puts text.bold("Method Language: ") + code_type.to_s.capitalize
84
- output.puts text.bold("Method Type: ") + (meth.is_a?(Method) ? "Bound" : "Unbound")
85
- output.puts text.bold("Method Arity: ") + meth.arity.to_s
83
+ output.puts "Method Information:"
84
+ output.puts "--"
85
+ output.puts "Name: " + meth_name
86
+ output.puts "Owner: " + (meth.owner.to_s ? meth.owner.to_s : "Unknown")
87
+ output.puts "Type: " + (meth.is_a?(Method) ? "Bound" : "Unbound")
88
+ output.puts "Arity: " + meth.arity.to_s
86
89
 
87
- name_map = { :req => "Required:", :opt => "Optional:", :rest => "Rest:" }
88
90
  if meth.respond_to?(:parameters)
89
- output.puts text.bold("Method Parameters: ") + meth.parameters.group_by(&:first).
90
- map { |k, v| "#{name_map[k]} #{v.map { |kk, vv| vv ? vv.to_s : "noname" }.join(", ")}" }.join(". ")
91
+ output.puts "Method Signature: " + signature_for(meth)
91
92
  end
92
- output.puts text.bold("Comment length: ") + (doc.empty? ? 'No comment.' : (doc.lines.count.to_s + ' lines.'))
93
+
93
94
  end
94
95
 
95
96
  command "gist-method", "Gist a method to github. Type `gist-method --help` for more info.", :requires_gem => "gist" do |*args|
@@ -140,6 +141,23 @@ class Pry
140
141
  output.puts "Gist created at #{link}"
141
142
  end
142
143
 
144
+ helpers do
145
+ def signature_for(meth)
146
+ param_strings = []
147
+ meth.parameters.each do |kind, name|
148
+ case kind
149
+ when :req
150
+ param_strings << name
151
+ when :opt
152
+ param_strings << "#{name}=?"
153
+ when :rest
154
+ param_strings << "*#{name}"
155
+ end
156
+ end
157
+ "#{meth.name}(#{param_strings.join(", ")})"
158
+ end
159
+ end
160
+
143
161
  end
144
162
 
145
163
  end
@@ -8,28 +8,101 @@ class Pry
8
8
  eval_string.replace("")
9
9
  end
10
10
 
11
- command "show-input", "Show the current eval_string" do
12
- render_output(false, 0, Pry.color ? CodeRay.scan(eval_string, :ruby).term : eval_string)
11
+ command "show-input", "Show the contents of the input buffer for the current multi-line expression." do
12
+ render_output(false, 1, Pry.color ? CodeRay.scan(eval_string, :ruby).term : eval_string)
13
13
  end
14
14
 
15
- command(/amend-line-?(\d+)?/, "Experimental amend-line, where the N in amend-line-N represents line to replace. Aliases: %N",
16
- :interpolate => false, :listing => "amend-line-N") do |line_number, replacement_line|
15
+ command(/amend-line.?(-?\d+)?(?:\.\.(-?\d+))?/, "Amend a line of input in multi-line mode. Type `amend-line --help` for more information. Aliases %",
16
+ :interpolate => false, :listing => "amend-line") do |*args|
17
+ start_line_number, end_line_number, replacement_line = *args
18
+
19
+ opts = Slop.parse!(args.compact) do |opt|
20
+ opt.banner %{Amend a line of input in multi-line mode. `amend-line N`, where the N in `amend-line N` represents line to replace.
21
+
22
+ Can also specify a range of lines using `amend-line N..M` syntax. Passing '!' as replacement content deletes the line(s) instead. Aliases: %N
23
+ e.g amend-line 1 puts 'hello world! # replace line 1'
24
+ e.g amend-line 1..4 ! # delete lines 1..4
25
+ e.g amend-line 3 >puts 'goodbye' # insert before line 3
26
+ e.g amend-line puts 'hello again' # no line number modifies immediately preceding line
27
+ }
28
+ opt.on :h, :help, "This message." do
29
+ output.puts opt
30
+ end
31
+ end
32
+
33
+ next if opts.h?
34
+ next output.puts "No input to amend." if eval_string.empty?
35
+
17
36
  replacement_line = "" if !replacement_line
18
37
  input_array = eval_string.each_line.to_a
19
- line_num = line_number ? line_number.to_i : input_array.size - 1
20
- input_array[line_num] = arg_string + "\n"
38
+
39
+ end_line_number = start_line_number.to_i if !end_line_number
40
+ line_range = start_line_number ? (one_index_number(start_line_number.to_i)..one_index_number(end_line_number.to_i)) : input_array.size - 1
41
+
42
+ # delete selected lines if replacement line is '!'
43
+ if arg_string == "!"
44
+ input_array.slice!(line_range)
45
+ elsif arg_string.start_with?(">")
46
+ insert_slot = Array(line_range).first
47
+ input_array.insert(insert_slot, arg_string[1..-1] + "\n")
48
+ else
49
+ input_array[line_range] = arg_string + "\n"
50
+ end
21
51
  eval_string.replace input_array.join
52
+ run "show-input"
22
53
  end
23
54
 
24
- alias_command(/%(\d+)?/, /amend-line-?(\d+)?/, "")
55
+ alias_command(/%.?(-?\d+)?(?:\.\.(-?\d+))?/, /amend-line.?(-?\d+)?(?:\.\.(-?\d+))?/, "")
56
+
57
+ command "play", "Play back a string or a method or a file as input. Type `play --help` for more information." do |*args|
58
+ opts = Slop.parse!(args) do |opt|
59
+ opt.banner "Usage: play [OPTIONS] [--help]\nDefault action (no options) is to play the provided string\ne.g `play puts 'hello world'` #=> \"hello world\"\ne.g `play -m Pry#repl --lines 1..-1`\ne.g `play -f Rakefile --lines 5`\n"
60
+
61
+ opt.on :l, :lines, 'The line (or range of lines) to replay.', true, :as => Range
62
+ opt.on :m, :method, 'Play a method.', true
63
+ opt.on :f, "file", 'The line (or range of lines) to replay.', true
64
+ opt.on :o, "open", 'When used with the -m switch, it plays the entire method except the last line, leaving the method definition "open". `amend-line` can then be used to modify the method.'
65
+ opt.on :h, :help, "This message." do
66
+ output.puts opt
67
+ end
68
+
69
+ opt.on_noopts { Pry.active_instance.input = StringIO.new(arg_string) }
70
+ end
71
+
72
+ if opts.m?
73
+ meth_name = opts[:m]
74
+ if (meth = get_method_object(meth_name, target, {})).nil?
75
+ output.puts "Invalid method name: #{meth_name}."
76
+ next
77
+ end
78
+ code, code_type = code_and_code_type_for(meth)
79
+ next if !code
80
+
81
+ range = opts.l? ? one_index_range_or_number(opts[:l]) : (0..-1)
82
+ range = (0..-2) if opts.o?
83
+
84
+ Pry.active_instance.input = StringIO.new(Array(code.each_line.to_a[range]).join)
85
+ end
86
+
87
+ if opts.f?
88
+ file_name = File.expand_path(opts[:f])
89
+ next output.puts "No such file: #{opts[:f]}" if !File.exists?(file_name)
90
+ text_array = File.readlines(file_name)
91
+ range = opts.l? ? one_index_range_or_number(opts[:l]) : (0..-1)
92
+ range = (0..-2) if opts.o?
93
+
94
+ Pry.active_instance.input = StringIO.new(Array(text_array[range]).join)
95
+ end
96
+ end
25
97
 
26
98
  command "hist", "Show and replay Readline history. Type `hist --help` for more info." do |*args|
27
- Slop.parse(args) do |opt|
28
- history = Readline::HISTORY.to_a
29
- opt.banner "Usage: hist [--replay START..END] [--clear] [--grep PATTERN] [--head N] [--tail N] [--help]\n"
99
+ history = Readline::HISTORY.to_a
100
+
101
+ opts = Slop.parse!(args) do |opt|
102
+ opt.banner "Usage: hist [--replay START..END] [--clear] [--grep PATTERN] [--head N] [--tail N] [--help] [--save [START..END] file.txt]\n"
30
103
 
31
104
  opt.on :g, :grep, 'A pattern to match against the history.', true do |pattern|
32
- pattern = Regexp.new arg_string.split(/ /)[1]
105
+ pattern = Regexp.new arg_string.strip.split(/ /, 2).last.strip
33
106
  history.pop
34
107
 
35
108
  history.map!.with_index do |element, index|
@@ -41,64 +114,67 @@ class Pry
41
114
  stagger_output history.compact.join "\n"
42
115
  end
43
116
 
44
- opt.on :head, 'Display the first N items of history', :optional => true, :as => Integer do |limit|
45
- unless opt.grep?
46
- limit ||= 10
47
- list = history.first limit
48
- lines = text.with_line_numbers list.join("\n"), 0
49
- stagger_output lines
50
- end
117
+ opt.on :head, 'Display the first N items of history',
118
+ :optional => true,
119
+ :as => Integer,
120
+ :unless => :grep do |limit|
121
+
122
+ limit ||= 10
123
+ list = history.first limit
124
+ lines = text.with_line_numbers list.join("\n"), 0
125
+ stagger_output lines
51
126
  end
52
127
 
53
- opt.on :t, :tail, 'Display the last N items of history', :optional => true, :as => Integer do |limit|
54
- unless opt.grep?
55
- limit ||= 10
56
- offset = history.size-limit
57
- offset = offset < 0 ? 0 : offset
128
+ opt.on :t, :tail, 'Display the last N items of history',
129
+ :optional => true,
130
+ :as => Integer,
131
+ :unless => :grep do |limit|
58
132
 
59
- list = history.last limit
60
- lines = text.with_line_numbers list.join("\n"), offset
61
- stagger_output lines
62
- end
133
+ limit ||= 10
134
+ offset = history.size-limit
135
+ offset = offset < 0 ? 0 : offset
136
+
137
+ list = history.last limit
138
+ lines = text.with_line_numbers list.join("\n"), offset
139
+ stagger_output lines
63
140
  end
64
141
 
65
- opt.on :s, :show, 'Show the history corresponding to the history line (or range of lines).', true, :as => Range do |range|
66
- unless opt.grep?
67
- start_line = range.is_a?(Range) ? range.first : range
68
- lines = text.with_line_numbers Array(history[range]).join("\n"), start_line
69
- stagger_output lines
70
- end
142
+ opt.on :s, :show, 'Show the history corresponding to the history line (or range of lines).',
143
+ true,
144
+ :as => Range,
145
+ :unless => :grep do |range|
146
+
147
+ start_line = range.is_a?(Range) ? range.first : range
148
+ lines = text.with_line_numbers Array(history[range]).join("\n"), start_line
149
+ stagger_output lines
71
150
  end
72
151
 
73
- opt.on :e, :exclude, 'Exclude pry commands from the history.' do
74
- unless opt.grep?
75
- history.map!.with_index do |element, index|
76
- unless command_processor.valid_command? element
77
- "#{text.blue index}: #{element}"
78
- end
152
+ opt.on :e, :exclude, 'Exclude pry commands from the history.', :unless => :grep do
153
+ history.map!.with_index do |element, index|
154
+ unless command_processor.valid_command? element
155
+ "#{text.blue index}: #{element}"
79
156
  end
80
- stagger_output history.compact.join "\n"
81
157
  end
158
+ stagger_output history.compact.join "\n"
82
159
  end
83
160
 
84
- opt.on :r, :replay, 'The line (or range of lines) to replay.', true, :as => Range do |range|
85
- unless opt.grep?
86
- actions = Array(history[range]).join("\n") + "\n"
87
- Pry.active_instance.input = StringIO.new(actions)
88
- end
161
+ opt.on :r, :replay, 'The line (or range of lines) to replay.',
162
+ true,
163
+ :as => Range,
164
+ :unless => :grep do |range|
165
+ actions = Array(history[range]).join("\n") + "\n"
166
+ Pry.active_instance.input = StringIO.new(actions)
89
167
  end
90
168
 
91
- opt.on :c, :clear, 'Clear the history' do
92
- unless opt.grep?
93
- Readline::HISTORY.shift until Readline::HISTORY.empty?
94
- output.puts 'History cleared.'
95
- end
169
+ opt.on "save", "Save history to a file. --save [start..end] output.txt. Pry commands are excluded from saved history.", true, :as => Range
170
+
171
+ opt.on :c, :clear, 'Clear the history', :unless => :grep do
172
+ Readline::HISTORY.shift until Readline::HISTORY.empty?
173
+ output.puts 'History cleared.'
96
174
  end
97
175
 
98
- opt.on :h, :help, 'Show this message.', :tail => true do
99
- unless opt.grep?
100
- output.puts opt.help
101
- end
176
+ opt.on :h, :help, 'Show this message.', :tail => true, :unless => :grep do
177
+ output.puts opt.help
102
178
  end
103
179
 
104
180
  opt.on_empty do
@@ -106,6 +182,62 @@ class Pry
106
182
  stagger_output lines
107
183
  end
108
184
  end
185
+
186
+ # FIXME: hack to save history (this must be refactored)
187
+ if opts["save"]
188
+ file_name = nil
189
+ hist_array = nil
190
+
191
+ case opts["save"]
192
+ when Range
193
+ hist_array = Array(history[opts["save"]])
194
+ next output.puts "Must provide a file name." if !args.first
195
+ file_name = File.expand_path(args.first)
196
+ when String
197
+ hist_array = history
198
+ file_name = File.expand_path(opts["save"])
199
+ end
200
+
201
+ output.puts "Saving history in #{file_name} ..."
202
+ # exclude pry commands
203
+ hist_array.reject! do |element|
204
+ command_processor.valid_command?(element)
205
+ end
206
+
207
+ File.open(file_name, 'w') do |f|
208
+ f.write hist_array.join("\n")
209
+ end
210
+
211
+ output.puts "... history saved."
212
+ end
213
+
214
+ end
215
+
216
+
217
+ helpers do
218
+ def one_index_number(line_number)
219
+ if line_number > 0
220
+ line_number - 1
221
+ elsif line_number < 0
222
+ line_number
223
+ else
224
+ line_number
225
+ end
226
+ end
227
+
228
+ def one_index_range(range)
229
+ Range.new(one_index_number(range.begin), one_index_number(range.end))
230
+ end
231
+
232
+ def one_index_range_or_number(range_or_number)
233
+ case range_or_number
234
+ when Range
235
+ one_index_range(range_or_number)
236
+ else
237
+ one_index_number(range_or_number)
238
+ end
239
+ end
240
+
109
241
  end
110
242
 
111
243
  end