pry 0.9.12.2 → 0.14.2

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.
Files changed (237) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1141 -0
  3. data/LICENSE +2 -2
  4. data/README.md +466 -0
  5. data/bin/pry +4 -7
  6. data/lib/pry/basic_object.rb +10 -0
  7. data/lib/pry/block_command.rb +22 -0
  8. data/lib/pry/class_command.rb +194 -0
  9. data/lib/pry/cli.rb +97 -92
  10. data/lib/pry/code/code_file.rb +114 -0
  11. data/lib/pry/code/code_range.rb +7 -4
  12. data/lib/pry/code/loc.rb +27 -14
  13. data/lib/pry/code.rb +62 -90
  14. data/lib/pry/code_object.rb +83 -39
  15. data/lib/pry/color_printer.rb +66 -0
  16. data/lib/pry/command.rb +202 -371
  17. data/lib/pry/command_set.rb +151 -133
  18. data/lib/pry/command_state.rb +31 -0
  19. data/lib/pry/commands/amend_line.rb +86 -82
  20. data/lib/pry/commands/bang.rb +18 -14
  21. data/lib/pry/commands/bang_pry.rb +15 -11
  22. data/lib/pry/commands/cat/abstract_formatter.rb +23 -18
  23. data/lib/pry/commands/cat/exception_formatter.rb +85 -73
  24. data/lib/pry/commands/cat/file_formatter.rb +56 -63
  25. data/lib/pry/commands/cat/input_expression_formatter.rb +35 -30
  26. data/lib/pry/commands/cat.rb +64 -47
  27. data/lib/pry/commands/cd.rb +42 -26
  28. data/lib/pry/commands/change_inspector.rb +34 -0
  29. data/lib/pry/commands/change_prompt.rb +51 -0
  30. data/lib/pry/commands/clear_screen.rb +20 -0
  31. data/lib/pry/commands/code_collector.rb +148 -131
  32. data/lib/pry/commands/disable_pry.rb +23 -19
  33. data/lib/pry/commands/easter_eggs.rb +23 -34
  34. data/lib/pry/commands/edit/exception_patcher.rb +21 -17
  35. data/lib/pry/commands/edit/file_and_line_locator.rb +33 -24
  36. data/lib/pry/commands/edit.rb +183 -167
  37. data/lib/pry/commands/exit.rb +40 -35
  38. data/lib/pry/commands/exit_all.rb +24 -20
  39. data/lib/pry/commands/exit_program.rb +20 -17
  40. data/lib/pry/commands/find_method.rb +167 -167
  41. data/lib/pry/commands/fix_indent.rb +16 -12
  42. data/lib/pry/commands/help.rb +140 -133
  43. data/lib/pry/commands/hist.rb +153 -132
  44. data/lib/pry/commands/import_set.rb +20 -15
  45. data/lib/pry/commands/jump_to.rb +25 -21
  46. data/lib/pry/commands/list_inspectors.rb +42 -0
  47. data/lib/pry/commands/ls/constants.rb +75 -0
  48. data/lib/pry/commands/ls/formatter.rb +55 -0
  49. data/lib/pry/commands/ls/globals.rb +50 -0
  50. data/lib/pry/commands/ls/grep.rb +23 -0
  51. data/lib/pry/commands/ls/instance_vars.rb +40 -0
  52. data/lib/pry/commands/ls/interrogatable.rb +24 -0
  53. data/lib/pry/commands/ls/jruby_hacks.rb +55 -0
  54. data/lib/pry/commands/ls/local_names.rb +37 -0
  55. data/lib/pry/commands/ls/local_vars.rb +47 -0
  56. data/lib/pry/commands/ls/ls_entity.rb +65 -0
  57. data/lib/pry/commands/ls/methods.rb +55 -0
  58. data/lib/pry/commands/ls/methods_helper.rb +50 -0
  59. data/lib/pry/commands/ls/self_methods.rb +34 -0
  60. data/lib/pry/commands/ls.rb +100 -303
  61. data/lib/pry/commands/nesting.rb +21 -17
  62. data/lib/pry/commands/play.rb +93 -49
  63. data/lib/pry/commands/pry_backtrace.rb +22 -18
  64. data/lib/pry/commands/pry_version.rb +15 -11
  65. data/lib/pry/commands/raise_up.rb +33 -27
  66. data/lib/pry/commands/reload_code.rb +57 -48
  67. data/lib/pry/commands/reset.rb +16 -12
  68. data/lib/pry/commands/ri.rb +57 -38
  69. data/lib/pry/commands/save_file.rb +45 -43
  70. data/lib/pry/commands/shell_command.rb +66 -34
  71. data/lib/pry/commands/shell_mode.rb +22 -20
  72. data/lib/pry/commands/show_doc.rb +80 -65
  73. data/lib/pry/commands/show_info.rb +193 -159
  74. data/lib/pry/commands/show_input.rb +16 -11
  75. data/lib/pry/commands/show_source.rb +113 -33
  76. data/lib/pry/commands/stat.rb +35 -31
  77. data/lib/pry/commands/switch_to.rb +21 -15
  78. data/lib/pry/commands/toggle_color.rb +21 -13
  79. data/lib/pry/commands/watch_expression/expression.rb +43 -0
  80. data/lib/pry/commands/watch_expression.rb +110 -0
  81. data/lib/pry/commands/whereami.rb +157 -134
  82. data/lib/pry/commands/wtf.rb +78 -40
  83. data/lib/pry/config/attributable.rb +22 -0
  84. data/lib/pry/config/lazy_value.rb +29 -0
  85. data/lib/pry/config/memoized_value.rb +34 -0
  86. data/lib/pry/config/value.rb +24 -0
  87. data/lib/pry/config.rb +290 -220
  88. data/lib/pry/control_d_handler.rb +28 -0
  89. data/lib/pry/core_extensions.rb +50 -27
  90. data/lib/pry/editor.rb +130 -102
  91. data/lib/pry/env.rb +18 -0
  92. data/lib/pry/exception_handler.rb +43 -0
  93. data/lib/pry/exceptions.rb +73 -0
  94. data/lib/pry/forwardable.rb +27 -0
  95. data/lib/pry/helpers/base_helpers.rb +22 -151
  96. data/lib/pry/helpers/command_helpers.rb +55 -63
  97. data/lib/pry/helpers/documentation_helpers.rb +21 -13
  98. data/lib/pry/helpers/options_helpers.rb +15 -8
  99. data/lib/pry/helpers/platform.rb +55 -0
  100. data/lib/pry/helpers/table.rb +44 -32
  101. data/lib/pry/helpers/text.rb +96 -86
  102. data/lib/pry/helpers.rb +3 -0
  103. data/lib/pry/history.rb +101 -70
  104. data/lib/pry/hooks.rb +67 -137
  105. data/lib/pry/indent.rb +79 -73
  106. data/lib/pry/input_completer.rb +283 -0
  107. data/lib/pry/input_lock.rb +129 -0
  108. data/lib/pry/inspector.rb +39 -0
  109. data/lib/pry/last_exception.rb +61 -0
  110. data/lib/pry/method/disowned.rb +19 -5
  111. data/lib/pry/{commands/edit/method_patcher.rb → method/patcher.rb} +51 -42
  112. data/lib/pry/method/weird_method_locator.rb +80 -44
  113. data/lib/pry/method.rb +225 -176
  114. data/lib/pry/object_path.rb +91 -0
  115. data/lib/pry/output.rb +136 -0
  116. data/lib/pry/pager.rb +227 -68
  117. data/lib/pry/prompt.rb +214 -0
  118. data/lib/pry/pry_class.rb +216 -289
  119. data/lib/pry/pry_instance.rb +438 -500
  120. data/lib/pry/repl.rb +256 -0
  121. data/lib/pry/repl_file_loader.rb +34 -35
  122. data/lib/pry/ring.rb +89 -0
  123. data/lib/pry/slop/LICENSE +20 -0
  124. data/lib/pry/slop/commands.rb +190 -0
  125. data/lib/pry/slop/option.rb +210 -0
  126. data/lib/pry/slop.rb +672 -0
  127. data/lib/pry/syntax_highlighter.rb +26 -0
  128. data/lib/pry/system_command_handler.rb +17 -0
  129. data/lib/pry/testable/evalable.rb +24 -0
  130. data/lib/pry/testable/mockable.rb +22 -0
  131. data/lib/pry/testable/pry_tester.rb +88 -0
  132. data/lib/pry/testable/utility.rb +34 -0
  133. data/lib/pry/testable/variables.rb +52 -0
  134. data/lib/pry/testable.rb +68 -0
  135. data/lib/pry/version.rb +3 -1
  136. data/lib/pry/warning.rb +20 -0
  137. data/lib/pry/{module_candidate.rb → wrapped_module/candidate.rb} +36 -43
  138. data/lib/pry/wrapped_module.rb +102 -103
  139. data/lib/pry.rb +135 -261
  140. metadata +94 -283
  141. data/.document +0 -2
  142. data/.gitignore +0 -16
  143. data/.travis.yml +0 -21
  144. data/.yardopts +0 -1
  145. data/CHANGELOG +0 -534
  146. data/CONTRIBUTORS +0 -55
  147. data/Gemfile +0 -9
  148. data/Guardfile +0 -62
  149. data/README.markdown +0 -400
  150. data/Rakefile +0 -140
  151. data/TODO +0 -117
  152. data/lib/pry/commands/disabled_commands.rb +0 -2
  153. data/lib/pry/commands/gem_cd.rb +0 -26
  154. data/lib/pry/commands/gem_install.rb +0 -29
  155. data/lib/pry/commands/gem_list.rb +0 -33
  156. data/lib/pry/commands/gem_open.rb +0 -29
  157. data/lib/pry/commands/gist.rb +0 -102
  158. data/lib/pry/commands/install_command.rb +0 -51
  159. data/lib/pry/commands/simple_prompt.rb +0 -22
  160. data/lib/pry/commands.rb +0 -6
  161. data/lib/pry/completion.rb +0 -304
  162. data/lib/pry/custom_completions.rb +0 -6
  163. data/lib/pry/history_array.rb +0 -116
  164. data/lib/pry/plugins.rb +0 -103
  165. data/lib/pry/rbx_method.rb +0 -13
  166. data/lib/pry/rbx_path.rb +0 -22
  167. data/lib/pry/rubygem.rb +0 -74
  168. data/lib/pry/terminal.rb +0 -78
  169. data/lib/pry/test/helper.rb +0 -185
  170. data/man/pry.1 +0 -195
  171. data/man/pry.1.html +0 -204
  172. data/man/pry.1.ronn +0 -141
  173. data/pry.gemspec +0 -30
  174. data/spec/Procfile +0 -3
  175. data/spec/cli_spec.rb +0 -78
  176. data/spec/code_object_spec.rb +0 -277
  177. data/spec/code_spec.rb +0 -219
  178. data/spec/command_helpers_spec.rb +0 -29
  179. data/spec/command_integration_spec.rb +0 -644
  180. data/spec/command_set_spec.rb +0 -627
  181. data/spec/command_spec.rb +0 -821
  182. data/spec/commands/amend_line_spec.rb +0 -247
  183. data/spec/commands/bang_spec.rb +0 -19
  184. data/spec/commands/cat_spec.rb +0 -164
  185. data/spec/commands/cd_spec.rb +0 -250
  186. data/spec/commands/disable_pry_spec.rb +0 -25
  187. data/spec/commands/edit_spec.rb +0 -727
  188. data/spec/commands/exit_all_spec.rb +0 -34
  189. data/spec/commands/exit_program_spec.rb +0 -19
  190. data/spec/commands/exit_spec.rb +0 -34
  191. data/spec/commands/find_method_spec.rb +0 -70
  192. data/spec/commands/gem_list_spec.rb +0 -26
  193. data/spec/commands/gist_spec.rb +0 -79
  194. data/spec/commands/help_spec.rb +0 -56
  195. data/spec/commands/hist_spec.rb +0 -181
  196. data/spec/commands/jump_to_spec.rb +0 -15
  197. data/spec/commands/ls_spec.rb +0 -181
  198. data/spec/commands/play_spec.rb +0 -140
  199. data/spec/commands/raise_up_spec.rb +0 -56
  200. data/spec/commands/save_file_spec.rb +0 -177
  201. data/spec/commands/show_doc_spec.rb +0 -510
  202. data/spec/commands/show_input_spec.rb +0 -17
  203. data/spec/commands/show_source_spec.rb +0 -782
  204. data/spec/commands/whereami_spec.rb +0 -203
  205. data/spec/completion_spec.rb +0 -239
  206. data/spec/control_d_handler_spec.rb +0 -58
  207. data/spec/documentation_helper_spec.rb +0 -73
  208. data/spec/editor_spec.rb +0 -79
  209. data/spec/exception_whitelist_spec.rb +0 -21
  210. data/spec/fixtures/candidate_helper1.rb +0 -11
  211. data/spec/fixtures/candidate_helper2.rb +0 -8
  212. data/spec/fixtures/example.erb +0 -5
  213. data/spec/fixtures/example_nesting.rb +0 -33
  214. data/spec/fixtures/show_source_doc_examples.rb +0 -15
  215. data/spec/fixtures/testrc +0 -2
  216. data/spec/fixtures/testrcbad +0 -2
  217. data/spec/fixtures/whereami_helper.rb +0 -6
  218. data/spec/helper.rb +0 -34
  219. data/spec/helpers/bacon.rb +0 -86
  220. data/spec/helpers/mock_pry.rb +0 -43
  221. data/spec/helpers/table_spec.rb +0 -105
  222. data/spec/history_array_spec.rb +0 -67
  223. data/spec/hooks_spec.rb +0 -522
  224. data/spec/indent_spec.rb +0 -301
  225. data/spec/input_stack_spec.rb +0 -90
  226. data/spec/method_spec.rb +0 -482
  227. data/spec/prompt_spec.rb +0 -60
  228. data/spec/pry_defaults_spec.rb +0 -419
  229. data/spec/pry_history_spec.rb +0 -99
  230. data/spec/pry_output_spec.rb +0 -95
  231. data/spec/pry_spec.rb +0 -504
  232. data/spec/run_command_spec.rb +0 -25
  233. data/spec/sticky_locals_spec.rb +0 -157
  234. data/spec/syntax_checking_spec.rb +0 -81
  235. data/spec/wrapped_module_spec.rb +0 -261
  236. data/wiki/Customizing-pry.md +0 -397
  237. data/wiki/Home.md +0 -4
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ class Pry
6
+ # `ObjectPath` implements the resolution of "object paths", which are strings
7
+ # that are similar to filesystem paths but meant for traversing Ruby objects.
8
+ # Examples of valid object paths include:
9
+ #
10
+ # x
11
+ # @foo/@bar
12
+ # "string"/upcase
13
+ # Pry/Method
14
+ #
15
+ # Object paths are mostly relevant in the context of the `cd` command.
16
+ # @see https://github.com/pry/pry/wiki/State-navigation
17
+ class ObjectPath
18
+ SPECIAL_TERMS = ["", "::", ".", ".."].freeze
19
+
20
+ # @param [String] path_string The object path expressed as a string.
21
+ # @param [Array<Binding>] current_stack The current state of the binding
22
+ # stack.
23
+ def initialize(path_string, current_stack)
24
+ @path_string = path_string
25
+ @current_stack = current_stack
26
+ end
27
+
28
+ # @return [Array<Binding>] a new stack resulting from applying the given
29
+ # path to the current stack.
30
+ def resolve
31
+ scanner = StringScanner.new(@path_string.strip)
32
+ stack = @current_stack.dup
33
+
34
+ loop do
35
+ begin
36
+ next_segment = ""
37
+
38
+ loop do
39
+ # Scan for as long as we don't see a slash
40
+ next_segment += scanner.scan(%r{[^/]*})
41
+
42
+ if complete?(next_segment) || scanner.eos?
43
+ scanner.getch # consume the slash
44
+ break
45
+ else
46
+ next_segment += scanner.getch # append the slash
47
+ end
48
+ end
49
+
50
+ case next_segment.chomp
51
+ when ""
52
+ stack = [stack.first]
53
+ when "::"
54
+ stack.push(TOPLEVEL_BINDING)
55
+ when "."
56
+ next
57
+ when ".."
58
+ stack.pop unless stack.size == 1
59
+ else
60
+ stack.push(Pry.binding_for(stack.last.eval(next_segment)))
61
+ end
62
+ rescue RescuableException => e
63
+ return handle_failure(next_segment, e)
64
+ end
65
+
66
+ break if scanner.eos?
67
+ end
68
+
69
+ stack
70
+ end
71
+
72
+ private
73
+
74
+ def complete?(segment)
75
+ SPECIAL_TERMS.include?(segment) || Pry::Code.complete_expression?(segment)
76
+ end
77
+
78
+ def handle_failure(context, err)
79
+ msg = [
80
+ "Bad object path: #{@path_string.inspect}",
81
+ "Failed trying to resolve: #{context.inspect}",
82
+ "Exception: #{err.inspect}"
83
+ ].join("\n")
84
+
85
+ command_error = CommandError.new(msg)
86
+ command_error.set_backtrace(err.backtrace)
87
+
88
+ raise command_error
89
+ end
90
+ end
91
+ end
data/lib/pry/output.rb ADDED
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pry
4
+ class Output
5
+ # @return [Array<Integer>] default terminal screen size [rows, cols]
6
+ DEFAULT_SIZE = [27, 80].freeze
7
+
8
+ attr_reader :pry_instance
9
+
10
+ def initialize(pry_instance)
11
+ @output = pry_instance.config.output
12
+ @color = pry_instance.config.color
13
+ end
14
+
15
+ def puts(*objs)
16
+ return print "\n" if objs.empty?
17
+
18
+ objs.each do |obj|
19
+ if (ary = Array.try_convert(obj))
20
+ puts(*ary)
21
+ else
22
+ print "#{obj.to_s.chomp}\n"
23
+ end
24
+ end
25
+ nil
26
+ end
27
+
28
+ def print(*objs)
29
+ objs.each do |obj|
30
+ @output.print decolorize_maybe(obj.to_s)
31
+ end
32
+ nil
33
+ end
34
+ alias << print
35
+ alias write print
36
+
37
+ def tty?
38
+ @output.respond_to?(:tty?) && @output.tty?
39
+ end
40
+
41
+ def method_missing(method_name, *args, &block)
42
+ if @output.respond_to?(method_name)
43
+ @output.__send__(method_name, *args, &block)
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ def respond_to_missing?(method_name, include_private = false)
50
+ @output.respond_to?(method_name, include_private)
51
+ end
52
+
53
+ def decolorize_maybe(str)
54
+ return str if @color
55
+
56
+ Pry::Helpers::Text.strip_color(str)
57
+ end
58
+
59
+ # @return [Array<Integer>] a pair of [rows, columns] which gives the size of
60
+ # the window. If the window size cannot be determined, the default value.
61
+ def size
62
+ rows, cols = actual_screen_size
63
+ return [rows.to_i, cols.to_i] if rows.to_i != 0 && cols.to_i != 0
64
+
65
+ DEFAULT_SIZE
66
+ end
67
+
68
+ # Return a screen width or the default if that fails.
69
+ def width
70
+ size.last
71
+ end
72
+
73
+ # Return a screen height or the default if that fails.
74
+ def height
75
+ size.first
76
+ end
77
+
78
+ private
79
+
80
+ def actual_screen_size
81
+ # The best way, if possible (requires non-jruby >=1.9 or io-console gem).
82
+ io_console_size ||
83
+ # Fall back to the old standby, though it might be stale.
84
+ env_size ||
85
+ # Fall further back, though this one is also out of date without
86
+ # something calling Readline.set_screen_size.
87
+ readline_size ||
88
+ # Windows users can otherwise run ansicon and get a decent answer.
89
+ ansicon_env_size
90
+ end
91
+
92
+ def io_console_size
93
+ return if Pry::Helpers::Platform.jruby?
94
+
95
+ begin
96
+ require 'io/console'
97
+
98
+ begin
99
+ @output.winsize if tty? && @output.respond_to?(:winsize)
100
+ rescue Errno::EOPNOTSUPP # rubocop:disable Lint/HandleExceptions
101
+ # Output is probably a socket, which doesn't support #winsize.
102
+ end
103
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
104
+ # They probably don't have the io/console stdlib or the io-console gem.
105
+ # We'll keep trying.
106
+ end
107
+ end
108
+
109
+ def env_size
110
+ size = [Pry::Env['LINES'] || Pry::Env['ROWS'], Pry::Env['COLUMNS']]
111
+ size if nonzero_column?(size)
112
+ end
113
+
114
+ def readline_size
115
+ return unless defined?(Readline) && Readline.respond_to?(:get_screen_size)
116
+
117
+ size = Readline.get_screen_size
118
+ size if nonzero_column?(size)
119
+ rescue Java::JavaLang::NullPointerException
120
+ # This rescue won't happen on jrubies later than:
121
+ # https://github.com/jruby/jruby/pull/436
122
+ nil
123
+ end
124
+
125
+ def ansicon_env_size
126
+ return unless Pry::Env['ANSICON'] =~ /\((.*)x(.*)\)/
127
+
128
+ size = [Regexp.last_match(2), Regexp.last_match(1)]
129
+ size if nonzero_column?(size)
130
+ end
131
+
132
+ def nonzero_column?(size)
133
+ size[1].to_i > 0
134
+ end
135
+ end
136
+ end
data/lib/pry/pager.rb CHANGED
@@ -1,89 +1,248 @@
1
- class Pry::Pager
2
- # @param [String] text
3
- # A piece of text to run through a pager.
4
- # @param [Symbol?] pager
5
- # `:simple` -- Use the pure ruby pager.
6
- # `:system` -- Use the system pager (less) or the environment variable
7
- # $PAGER if set.
8
- # `nil` -- Infer what pager to use from the environment. What this
9
- # really means is that JRuby and systems that do not have
10
- # access to 'less' will run through the pure ruby pager.
11
- def self.page(text, pager = nil)
12
- case pager
13
- when nil
14
- no_pager = !SystemPager.available?
15
- if no_pager || Pry::Helpers::BaseHelpers.jruby?
16
- SimplePager.new(text).page
17
- else
18
- SystemPager.new(text).page
19
- end
20
- when :simple
21
- SimplePager.new(text).page
22
- when :system
23
- SystemPager.new(text).page
24
- else
25
- raise "'#{pager}' is not a recognized pager."
1
+ # frozen_string_literal: true
2
+
3
+ # A pager is an `IO`-like object that accepts text and either prints it
4
+ # immediately, prints it one page at a time, or streams it to an external
5
+ # program to print one page at a time.
6
+ class Pry
7
+ class Pager
8
+ class StopPaging < StandardError
26
9
  end
27
- end
28
10
 
29
- def self.page_size
30
- @page_size ||= Pry::Terminal.height!
31
- end
11
+ attr_reader :pry_instance
32
12
 
33
- def initialize(text)
34
- @text = text
35
- end
13
+ def initialize(pry_instance)
14
+ @pry_instance = pry_instance
15
+ end
36
16
 
37
- class SimplePager < Pry::Pager
38
- def page
39
- # The pager size minus the number of lines used by the simple pager info bar.
40
- page_size = Pry::Pager.page_size - 3
41
- text_array = @text.lines.to_a
42
-
43
- text_array.each_slice(page_size) do |chunk|
44
- puts chunk.join
45
- break if chunk.size < page_size
46
- if text_array.size > page_size
47
- puts "\n<page break> --- Press enter to continue ( q<enter> to break ) --- <page break>"
48
- break if $stdin.gets.chomp == "q"
49
- end
17
+ # Send the given text through the best available pager (if
18
+ # `Pry.config.pager` is enabled). If you want to send text through in
19
+ # chunks as you generate it, use `open` to get a writable object
20
+ # instead.
21
+ #
22
+ # @param [String] text
23
+ # Text to run through a pager.
24
+ #
25
+ def page(text)
26
+ open do |pager|
27
+ pager << text
28
+ end
29
+ end
30
+
31
+ # Yields a pager object (`NullPager`, `SimplePager`, or `SystemPager`).
32
+ # All pagers accept output with `#puts`, `#print`, `#write`, and `#<<`.
33
+ def open
34
+ pager = best_available
35
+ yield pager
36
+ rescue StopPaging # rubocop:disable Lint/HandleExceptions
37
+ ensure
38
+ pager.close if pager
39
+ end
40
+
41
+ private
42
+
43
+ def enabled?
44
+ !!@enabled
45
+ end
46
+
47
+ attr_reader :output
48
+
49
+ # Return an instance of the "best" available pager class --
50
+ # `SystemPager` if possible, `SimplePager` if `SystemPager` isn't
51
+ # available, and `NullPager` if the user has disabled paging. All
52
+ # pagers accept output with `#puts`, `#print`, `#write`, and `#<<`. You
53
+ # must call `#close` when you're done writing output to a pager, and
54
+ # you must rescue `Pry::Pager::StopPaging`. These requirements can be
55
+ # avoided by using `.open` instead.
56
+ def best_available
57
+ if !pry_instance.config.pager
58
+ NullPager.new(pry_instance.output)
59
+ elsif !SystemPager.available? || Helpers::Platform.jruby?
60
+ SimplePager.new(pry_instance.output)
61
+ else
62
+ SystemPager.new(pry_instance.output)
50
63
  end
51
64
  end
52
- end
53
65
 
54
- class SystemPager < Pry::Pager
55
- def self.default_pager
56
- pager = ENV["PAGER"] || ""
66
+ # `NullPager` is a "pager" that actually just prints all output as it
67
+ # comes in. Used when `Pry.config.pager` is false.
68
+ class NullPager
69
+ def initialize(out)
70
+ @out = out
71
+ end
57
72
 
58
- # Default to less, and make sure less is being passed the correct options
59
- if pager.strip.empty? or pager =~ /^less\s*/
60
- pager = "less -R -S -F -X"
73
+ def puts(str)
74
+ print "#{str.chomp}\n"
61
75
  end
62
76
 
63
- pager
77
+ def print(str)
78
+ write str
79
+ end
80
+ alias << print
81
+
82
+ def write(str)
83
+ @out.write str
84
+ end
85
+
86
+ def close; end
87
+
88
+ private
89
+
90
+ def height
91
+ @height ||= @out.height
92
+ end
93
+
94
+ def width
95
+ @width ||= @out.width
96
+ end
64
97
  end
65
98
 
66
- def self.available?
67
- if @system_pager.nil?
68
- @system_pager = begin
69
- pager_executable = default_pager.split(' ').first
70
- `which #{ pager_executable }`
71
- rescue
72
- false
99
+ # `SimplePager` is a straightforward pure-Ruby pager. We use it on
100
+ # JRuby and when we can't find a usable external pager.
101
+ class SimplePager < NullPager
102
+ def initialize(*)
103
+ super
104
+ @tracker = PageTracker.new(height - 3, width)
105
+ end
106
+
107
+ def write(str)
108
+ str.lines.each do |line|
109
+ @out.print line
110
+ @tracker.record line
111
+
112
+ next unless @tracker.page?
113
+
114
+ @out.print "\n"
115
+ @out.print "\e[0m"
116
+ @out.print "<page break> --- Press enter to continue " \
117
+ "( q<enter> to break ) --- <page break>\n"
118
+ raise StopPaging if Readline.readline("").chomp == "q"
119
+
120
+ @tracker.reset
73
121
  end
74
- else
75
- @system_pager
76
122
  end
77
123
  end
78
124
 
79
- def initialize(*)
80
- super
81
- @pager = SystemPager.default_pager
125
+ # `SystemPager` buffers output until we're pretty sure it's at least a
126
+ # page long, then invokes an external pager and starts streaming output
127
+ # to it. If `#close` is called before then, it just prints out the
128
+ # buffered content.
129
+ class SystemPager < NullPager
130
+ def self.default_pager
131
+ pager = Pry::Env['PAGER'] || ''
132
+
133
+ # Default to less, and make sure less is being passed the correct
134
+ # options
135
+ pager = "less -R -F -X" if pager.strip.empty? || pager =~ /^less\b/
136
+
137
+ pager
138
+ end
139
+
140
+ @system_pager = nil
141
+
142
+ def self.available?
143
+ if @system_pager.nil?
144
+ @system_pager =
145
+ begin
146
+ pager_executable = default_pager.split(' ').first
147
+ if Helpers::Platform.windows? || Helpers::Platform.windows_ansi?
148
+ `where /Q #{pager_executable}`
149
+ else
150
+ `which #{pager_executable}`
151
+ end
152
+ $CHILD_STATUS.success?
153
+ rescue StandardError
154
+ false
155
+ end
156
+ else
157
+ @system_pager
158
+ end
159
+ end
160
+
161
+ def initialize(*)
162
+ super
163
+ @tracker = PageTracker.new(height, width)
164
+ @buffer = ""
165
+ @pager = nil
166
+ end
167
+
168
+ def write(str)
169
+ if invoked_pager?
170
+ write_to_pager str
171
+ else
172
+ @tracker.record str
173
+ @buffer += str
174
+
175
+ write_to_pager @buffer if @tracker.page?
176
+ end
177
+ rescue Errno::EPIPE
178
+ raise StopPaging
179
+ end
180
+
181
+ def close
182
+ if invoked_pager?
183
+ pager.close
184
+ else
185
+ @out.puts @buffer
186
+ end
187
+ end
188
+
189
+ private
190
+
191
+ def write_to_pager(text)
192
+ pager.write @out.decolorize_maybe(text)
193
+ end
194
+
195
+ def invoked_pager?
196
+ @pager
197
+ end
198
+
199
+ def pager
200
+ @pager ||= IO.popen(self.class.default_pager, 'w')
201
+ end
82
202
  end
83
203
 
84
- def page
85
- IO.popen(@pager, 'w') do |io|
86
- io.write @text
204
+ # `PageTracker` tracks output to determine whether it's likely to take
205
+ # up a whole page. This doesn't need to be super precise, but we can
206
+ # use it for `SimplePager` and to avoid invoking the system pager
207
+ # unnecessarily.
208
+ #
209
+ # One simplifying assumption is that we don't need `#page?` to return
210
+ # `true` on the basis of an incomplete line. Long lines should be
211
+ # counted as multiple lines, but we don't have to transition from
212
+ # `false` to `true` until we see a newline.
213
+ class PageTracker
214
+ def initialize(rows, cols)
215
+ @rows = rows
216
+ @cols = cols
217
+ reset
218
+ end
219
+
220
+ def record(str)
221
+ str.lines.each do |line|
222
+ if line.end_with? "\n"
223
+ @row += ((@col + line_length(line) - 1) / @cols) + 1
224
+ @col = 0
225
+ else
226
+ @col += line_length(line)
227
+ end
228
+ end
229
+ end
230
+
231
+ def page?
232
+ @row >= @rows
233
+ end
234
+
235
+ def reset
236
+ @row = 0
237
+ @col = 0
238
+ end
239
+
240
+ private
241
+
242
+ # Approximation of the printable length of a given line, without the
243
+ # newline and without ANSI color codes.
244
+ def line_length(line)
245
+ line.chomp.gsub(/\e\[[\d;]*m/, '').length
87
246
  end
88
247
  end
89
248
  end