irb 1.13.2 → 1.15.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.
data/lib/irb/history.rb CHANGED
@@ -1,6 +1,36 @@
1
1
  require "pathname"
2
2
 
3
3
  module IRB
4
+ module History
5
+ DEFAULT_ENTRY_LIMIT = 1000
6
+
7
+ class << self
8
+ # Integer representation of <code>IRB.conf[:HISTORY_FILE]</code>.
9
+ def save_history
10
+ return 0 if IRB.conf[:SAVE_HISTORY] == false
11
+ return DEFAULT_ENTRY_LIMIT if IRB.conf[:SAVE_HISTORY] == true
12
+ IRB.conf[:SAVE_HISTORY].to_i
13
+ end
14
+
15
+ def save_history?
16
+ !save_history.zero?
17
+ end
18
+
19
+ def infinite?
20
+ save_history.negative?
21
+ end
22
+
23
+ # Might be nil when HOME and XDG_CONFIG_HOME are not available.
24
+ def history_file
25
+ if (history_file = IRB.conf[:HISTORY_FILE])
26
+ File.expand_path(history_file)
27
+ else
28
+ IRB.rc_file("_history")
29
+ end
30
+ end
31
+ end
32
+ end
33
+
4
34
  module HistorySavingAbility # :nodoc:
5
35
  def support_history_saving?
6
36
  true
@@ -11,76 +41,75 @@ module IRB
11
41
  end
12
42
 
13
43
  def load_history
44
+ history_file = History.history_file
45
+ return unless File.exist?(history_file.to_s)
46
+
14
47
  history = self.class::HISTORY
15
48
 
16
- if history_file = IRB.conf[:HISTORY_FILE]
17
- history_file = File.expand_path(history_file)
18
- end
19
- history_file = IRB.rc_file("_history") unless history_file
20
- if history_file && File.exist?(history_file)
21
- File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
22
- f.each { |l|
23
- l = l.chomp
24
- if self.class == RelineInputMethod and history.last&.end_with?("\\")
25
- history.last.delete_suffix!("\\")
26
- history.last << "\n" << l
27
- else
28
- history << l
29
- end
30
- }
31
- end
32
- @loaded_history_lines = history.size
33
- @loaded_history_mtime = File.mtime(history_file)
49
+ File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
50
+ f.each { |l|
51
+ l = l.chomp
52
+ if self.class == RelineInputMethod and history.last&.end_with?("\\")
53
+ history.last.delete_suffix!("\\")
54
+ history.last << "\n" << l
55
+ else
56
+ history << l
57
+ end
58
+ }
34
59
  end
60
+ @loaded_history_lines = history.size
61
+ @loaded_history_mtime = File.mtime(history_file)
35
62
  end
36
63
 
37
64
  def save_history
65
+ return unless History.save_history?
66
+ return unless (history_file = History.history_file)
67
+ unless ensure_history_file_writable(history_file)
68
+ warn <<~WARN
69
+ Can't write history to #{History.history_file.inspect} due to insufficient permissions.
70
+ Please verify the value of `IRB.conf[:HISTORY_FILE]`. Ensure the folder exists and that both the folder and file (if it exists) are writable.
71
+ WARN
72
+ return
73
+ end
74
+
38
75
  history = self.class::HISTORY.to_a
39
76
 
40
- if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0
41
- if history_file = IRB.conf[:HISTORY_FILE]
42
- history_file = File.expand_path(history_file)
43
- end
44
- history_file = IRB.rc_file("_history") unless history_file
77
+ if File.exist?(history_file) &&
78
+ File.mtime(history_file) != @loaded_history_mtime
79
+ history = history[@loaded_history_lines..-1] if @loaded_history_lines
80
+ append_history = true
81
+ end
45
82
 
46
- # When HOME and XDG_CONFIG_HOME are not available, history_file might be nil
47
- return unless history_file
83
+ File.open(history_file, (append_history ? "a" : "w"), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
84
+ hist = history.map { |l| l.scrub.split("\n").join("\\\n") }
48
85
 
49
- # Change the permission of a file that already exists[BUG #7694]
50
- begin
51
- if File.stat(history_file).mode & 066 != 0
52
- File.chmod(0600, history_file)
53
- end
54
- rescue Errno::ENOENT
55
- rescue Errno::EPERM
56
- return
57
- rescue
58
- raise
86
+ unless append_history || History.infinite?
87
+ # Check size before slicing because array.last(huge_number) raises RangeError.
88
+ hist = hist.last(History.save_history) if hist.size > History.save_history
59
89
  end
60
90
 
61
- if File.exist?(history_file) &&
62
- File.mtime(history_file) != @loaded_history_mtime
63
- history = history[@loaded_history_lines..-1] if @loaded_history_lines
64
- append_history = true
65
- end
91
+ f.puts(hist)
92
+ end
93
+ end
66
94
 
67
- pathname = Pathname.new(history_file)
68
- unless Dir.exist?(pathname.dirname)
69
- warn "Warning: The directory to save IRB's history file does not exist. Please double check `IRB.conf[:HISTORY_FILE]`'s value."
70
- return
71
- end
95
+ private
72
96
 
73
- File.open(history_file, (append_history ? 'a' : 'w'), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
74
- hist = history.map{ |l| l.scrub.split("\n").join("\\\n") }
75
- unless append_history
76
- begin
77
- hist = hist.last(num) if hist.size > num and num > 0
78
- rescue RangeError # bignum too big to convert into `long'
79
- # Do nothing because the bignum should be treated as infinity
80
- end
81
- end
82
- f.puts(hist)
97
+ # Returns boolean whether writing to +history_file+ will be possible.
98
+ # Permissions of already existing +history_file+ are changed to
99
+ # owner-only-readable if necessary [BUG #7694].
100
+ def ensure_history_file_writable(history_file)
101
+ history_file = Pathname.new(history_file)
102
+
103
+ return false unless history_file.dirname.writable?
104
+ return true unless history_file.exist?
105
+
106
+ begin
107
+ if history_file.stat.mode & 0o66 != 0
108
+ history_file.chmod 0o600
83
109
  end
110
+ true
111
+ rescue Errno::EPERM # no permissions
112
+ false
84
113
  end
85
114
  end
86
115
  end
data/lib/irb/init.rb CHANGED
@@ -80,7 +80,7 @@ module IRB # :nodoc:
80
80
  @CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
81
81
  @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
82
82
  @CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false"
83
- @CONF[:COMPLETOR] = ENV.fetch("IRB_COMPLETOR", "regexp").to_sym
83
+ @CONF[:COMPLETOR] = ENV["IRB_COMPLETOR"]&.to_sym
84
84
  @CONF[:INSPECT_MODE] = true
85
85
  @CONF[:USE_TRACER] = false
86
86
  @CONF[:USE_LOADER] = false
@@ -93,7 +93,7 @@ module IRB # :nodoc:
93
93
  @CONF[:VERBOSE] = nil
94
94
 
95
95
  @CONF[:EVAL_HISTORY] = nil
96
- @CONF[:SAVE_HISTORY] = 1000
96
+ @CONF[:SAVE_HISTORY] = History::DEFAULT_ENTRY_LIMIT
97
97
 
98
98
  @CONF[:BACK_TRACE_LIMIT] = 16
99
99
 
@@ -194,6 +194,8 @@ module IRB # :nodoc:
194
194
  :'$' => :show_source,
195
195
  :'@' => :whereami,
196
196
  }
197
+
198
+ @CONF[:COPY_COMMAND] = ENV.fetch("IRB_COPY_COMMAND", nil)
197
199
  end
198
200
 
199
201
  def IRB.set_measure_callback(type = nil, arg = nil, &block)
@@ -26,10 +26,11 @@ module IRB
26
26
 
27
27
  def winsize
28
28
  if instance_variable_defined?(:@stdout) && @stdout.tty?
29
- @stdout.winsize
30
- else
31
- [24, 80]
29
+ winsize = @stdout.winsize
30
+ # If width or height is 0, something is wrong.
31
+ return winsize unless winsize.include? 0
32
32
  end
33
+ [24, 80]
33
34
  end
34
35
 
35
36
  # Whether this input method is still readable when there is no more data to
@@ -67,7 +68,9 @@ module IRB
67
68
  #
68
69
  # See IO#gets for more information.
69
70
  def gets
70
- puts if @stdout.tty? # workaround for debug compatibility test
71
+ # Workaround for debug compatibility test https://github.com/ruby/debug/pull/1100
72
+ puts if ENV['RUBY_DEBUG_TEST_UI']
73
+
71
74
  print @prompt
72
75
  line = @stdin.gets
73
76
  @line[@line_no += 1] = line
@@ -171,11 +174,18 @@ module IRB
171
174
  end
172
175
 
173
176
  class ReadlineInputMethod < StdioInputMethod
174
- def self.initialize_readline
175
- require "readline"
176
- rescue LoadError
177
- else
178
- include ::Readline
177
+ class << self
178
+ def initialize_readline
179
+ return if defined?(self::Readline)
180
+
181
+ begin
182
+ require 'readline'
183
+ const_set(:Readline, ::Readline)
184
+ rescue LoadError
185
+ const_set(:Readline, ::Reline)
186
+ end
187
+ const_set(:HISTORY, self::Readline::HISTORY)
188
+ end
179
189
  end
180
190
 
181
191
  include HistorySavingAbility
@@ -212,8 +222,8 @@ module IRB
212
222
  def gets
213
223
  Readline.input = @stdin
214
224
  Readline.output = @stdout
215
- if l = readline(@prompt, false)
216
- HISTORY.push(l) if !l.empty?
225
+ if l = Readline.readline(@prompt, false)
226
+ Readline::HISTORY.push(l) if !l.empty?
217
227
  @line[@line_no += 1] = l + "\n"
218
228
  else
219
229
  @eof = true
@@ -235,7 +245,7 @@ module IRB
235
245
 
236
246
  # For debug message
237
247
  def inspect
238
- readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline'
248
+ readline_impl = Readline == ::Reline ? 'Reline' : 'ext/readline'
239
249
  str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}"
240
250
  inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
241
251
  str += " and #{inputrc_path}" if File.exist?(inputrc_path)
@@ -263,18 +273,9 @@ module IRB
263
273
  @completion_params = [preposing, target, postposing, bind]
264
274
  @completor.completion_candidates(preposing, target, postposing, bind: bind)
265
275
  }
266
- Reline.output_modifier_proc =
267
- if IRB.conf[:USE_COLORIZE]
268
- proc do |output, complete: |
269
- next unless IRB::Color.colorable?
270
- lvars = IRB.CurrentContext&.local_variables || []
271
- IRB::Color.colorize_code(output, complete: complete, local_variables: lvars)
272
- end
273
- else
274
- proc do |output|
275
- Reline::Unicode.escape_for_print(output)
276
- end
277
- end
276
+ Reline.output_modifier_proc = proc do |input, complete:|
277
+ IRB.CurrentContext.colorize_input(input, complete: complete)
278
+ end
278
279
  Reline.dig_perfect_match_proc = ->(matched) { display_document(matched) }
279
280
  Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]
280
281
 
@@ -353,9 +354,15 @@ module IRB
353
354
  if show_easter_egg
354
355
  IRB.__send__(:easter_egg)
355
356
  else
357
+ # RDoc::RI::Driver#display_names uses pager command internally.
358
+ # Some pager command like `more` doesn't use alternate screen
359
+ # so we need to turn on and off alternate screen manually.
356
360
  begin
361
+ print "\e[?1049h"
357
362
  driver.display_names([name])
358
363
  rescue RDoc::RI::Driver::NotFoundError
364
+ ensure
365
+ print "\e[?1049l"
359
366
  end
360
367
  end
361
368
  end
data/lib/irb/inspector.rb CHANGED
@@ -6,7 +6,6 @@
6
6
 
7
7
  module IRB # :nodoc:
8
8
 
9
-
10
9
  # Convenience method to create a new Inspector, using the given +inspect+
11
10
  # proc, and optional +init+ proc and passes them to Inspector.new
12
11
  #
@@ -43,38 +42,40 @@ module IRB # :nodoc:
43
42
  # +:marshal+:: Using Marshal.dump
44
43
  INSPECTORS = {}
45
44
 
46
- # Determines the inspector to use where +inspector+ is one of the keys passed
47
- # during inspector definition.
48
- def self.keys_with_inspector(inspector)
49
- INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k}
50
- end
51
-
52
- # Example
53
- #
54
- # Inspector.def_inspector(key, init_p=nil){|v| v.inspect}
55
- # Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect}
56
- # Inspector.def_inspector(key, inspector)
57
- # Inspector.def_inspector([key1,...], inspector)
58
- def self.def_inspector(key, arg=nil, &block)
59
- if block_given?
60
- inspector = IRB::Inspector(block, arg)
61
- else
62
- inspector = arg
45
+ class << self
46
+ # Determines the inspector to use where +inspector+ is one of the keys passed
47
+ # during inspector definition.
48
+ def keys_with_inspector(inspector)
49
+ INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k}
63
50
  end
64
51
 
65
- case key
66
- when Array
67
- for k in key
68
- def_inspector(k, inspector)
52
+ # Example
53
+ #
54
+ # Inspector.def_inspector(key, init_p=nil){|v| v.inspect}
55
+ # Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect}
56
+ # Inspector.def_inspector(key, inspector)
57
+ # Inspector.def_inspector([key1,...], inspector)
58
+ def def_inspector(key, arg=nil, &block)
59
+ if block_given?
60
+ inspector = IRB::Inspector(block, arg)
61
+ else
62
+ inspector = arg
63
+ end
64
+
65
+ case key
66
+ when Array
67
+ for k in key
68
+ def_inspector(k, inspector)
69
+ end
70
+ when Symbol
71
+ INSPECTORS[key] = inspector
72
+ INSPECTORS[key.to_s] = inspector
73
+ when String
74
+ INSPECTORS[key] = inspector
75
+ INSPECTORS[key.intern] = inspector
76
+ else
77
+ INSPECTORS[key] = inspector
69
78
  end
70
- when Symbol
71
- INSPECTORS[key] = inspector
72
- INSPECTORS[key.to_s] = inspector
73
- when String
74
- INSPECTORS[key] = inspector
75
- INSPECTORS[key.intern] = inspector
76
- else
77
- INSPECTORS[key] = inspector
78
79
  end
79
80
  end
80
81
 
@@ -91,9 +92,14 @@ module IRB # :nodoc:
91
92
  @init.call if @init
92
93
  end
93
94
 
95
+ def support_stream_output?
96
+ second_parameter_type = @inspect.parameters[1]&.first
97
+ second_parameter_type == :req || second_parameter_type == :opt
98
+ end
99
+
94
100
  # Proc to call when the input is evaluated and output in irb.
95
- def inspect_value(v)
96
- @inspect.call(v)
101
+ def inspect_value(v, output, colorize: true)
102
+ support_stream_output? ? @inspect.call(v, output, colorize: colorize) : output << @inspect.call(v, colorize: colorize)
97
103
  rescue => e
98
104
  puts "An error occurred when inspecting the object: #{e.inspect}"
99
105
 
@@ -109,11 +115,11 @@ module IRB # :nodoc:
109
115
  end
110
116
 
111
117
  Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s}
112
- Inspector.def_inspector([:p, :inspect]){|v|
113
- Color.colorize_code(v.inspect, colorable: Color.colorable? && Color.inspect_colorable?(v))
118
+ Inspector.def_inspector([:p, :inspect]){|v, colorize: true|
119
+ Color.colorize_code(v.inspect, colorable: colorize && Color.colorable? && Color.inspect_colorable?(v))
114
120
  }
115
- Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v|
116
- IRB::ColorPrinter.pp(v, +'').chomp
121
+ Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v, output, colorize: true|
122
+ IRB::ColorPrinter.pp(v, output, colorize: colorize)
117
123
  }
118
124
  Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
119
125
  begin
@@ -8,7 +8,7 @@ Usage: irb.rb [options] [programfile] [arguments]
8
8
  -w ruby -w と同じ.
9
9
  -W[level=2] ruby -W と同じ.
10
10
  --context-mode n 新しいワークスペースを作成した時に関連する Binding
11
- オブジェクトの作成方法を 0 から 3 のいずれかに設定する.
11
+ オブジェクトの作成方法を 0 から 4 のいずれかに設定する.
12
12
  --extra-doc-dir 指定したディレクトリのドキュメントを追加で読み込む.
13
13
  --echo 実行結果を表示する(デフォルト).
14
14
  --noecho 実行結果を表示しない.
@@ -33,9 +33,9 @@ Usage: irb.rb [options] [programfile] [arguments]
33
33
  補完に正規表現を利用する.
34
34
  --type-completor 補完に型情報を利用する.
35
35
  --prompt prompt-mode/--prompt-mode prompt-mode
36
- プロンプトモードを切替えます. 現在定義されているプ
37
- ロンプトモードは, default, simple, xmp, inf-rubyが
38
- 用意されています.
36
+ プロンプトモードを切り替える.
37
+ 現在定義されているプロンプトモードは,
38
+ default, classic, simple, inf-ruby, xmp, null.
39
39
  --inf-ruby-mode emacsのinf-ruby-mode用のプロンプト表示を行なう. 特
40
40
  に指定がない限り, シングルラインエディタとマルチラ
41
41
  インエディタは使わなくなる.