irb 1.8.0 → 1.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 485bdcb4daa6d3f0e0a70fc6ff6e93289d9d8b46c4a32094ab85fd7ad1ad6994
4
- data.tar.gz: d57ae530ec9c2db847b0f15ab10f01f23e4ae62cd9542770ddbbee83fe446195
3
+ metadata.gz: 811c1b4343957dac57d58178adbc13195e6d53de980f63810ec83eec09d1f8c1
4
+ data.tar.gz: bbc99d3f46a8e7afa6a7a8e32dc137300e3da15935631e05a56b203a901887fb
5
5
  SHA512:
6
- metadata.gz: e8d8f8a7444b8c6bb13d7ba2e4baf38df9ee4126be78188ddb15d5c97160908e43789b54df09d534badb8eb3a69a4f771687f46038033361fdccf0e1ffe3fa66
7
- data.tar.gz: 2658667bb3f923447d311f9c866b3fbcefd0f7451e8cdc61c9593b5c9e05d443558e87e7da7e706fdb998cc86fe601e1ec87d4ed21e32ea05552e66906dabca3
6
+ metadata.gz: 76b770cf0a8012031f11306cc96879173d819cbd0501efdd41c434571028c3dee8bc12b5928696232d61215f6e22ecec3b9a6e3ff93e085349282c035c9389ec
7
+ data.tar.gz: f48e1f3912d5bb59acfa041718014dafb24d7f39ac23e1f0b267047ca4e4f937af77bdb0d40a940813da71f5920f53b5bbdc987b9dc50d16e5bdb85a754e02b7
data/README.md CHANGED
@@ -150,7 +150,7 @@ Context
150
150
 
151
151
  Starting from version 1.8.0, IRB boasts a powerful integration with `debug.gem`, providing a debugging experience akin to `pry-byebug`.
152
152
 
153
- After hitting a `binding.irb` breakpoint, you can activate the debugger with the `debug` command.
153
+ After hitting a `binding.irb` breakpoint, you can activate the debugger with the `debug` command. Alternatively, if the `debug` method happens to already be defined in the current scope, you can call `irb_debug`.
154
154
 
155
155
  ```shell
156
156
  From: test.rb @ line 3 :
data/irb.gemspec CHANGED
@@ -41,6 +41,6 @@ Gem::Specification.new do |spec|
41
41
 
42
42
  spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
43
43
 
44
- spec.add_dependency "reline", ">= 0.3.6"
45
- spec.add_dependency "rdoc", "~> 6.5"
44
+ spec.add_dependency "reline", ">= 0.3.8"
45
+ spec.add_dependency "rdoc"
46
46
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "nop"
4
4
  require_relative "../source_finder"
5
+ require_relative "../pager"
5
6
  require_relative "../color"
6
7
 
7
8
  module IRB
@@ -40,12 +41,16 @@ module IRB
40
41
  private
41
42
 
42
43
  def show_source(source)
43
- puts
44
- puts "#{bold("From")}: #{source.file}:#{source.first_line}"
45
- puts
46
- code = IRB::Color.colorize_code(File.read(source.file))
47
- puts code.lines[(source.first_line - 1)...source.last_line].join
48
- puts
44
+ file_content = IRB::Color.colorize_code(File.read(source.file))
45
+ code = file_content.lines[(source.first_line - 1)...source.last_line].join
46
+ content = <<~CONTENT
47
+
48
+ #{bold("From")}: #{source.file}:#{source.first_line}
49
+
50
+ #{code}
51
+ CONTENT
52
+
53
+ Pager.page_content(content)
49
54
  end
50
55
 
51
56
  def bold(str)
@@ -8,55 +8,14 @@
8
8
  require_relative 'ruby-lex'
9
9
 
10
10
  module IRB
11
- module InputCompletor # :nodoc:
12
- using Module.new {
13
- refine ::Binding do
14
- def eval_methods
15
- ::Kernel.instance_method(:methods).bind(eval("self")).call
16
- end
17
-
18
- def eval_private_methods
19
- ::Kernel.instance_method(:private_methods).bind(eval("self")).call
20
- end
21
-
22
- def eval_instance_variables
23
- ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
24
- end
25
-
26
- def eval_global_variables
27
- ::Kernel.instance_method(:global_variables).bind(eval("self")).call
28
- end
29
-
30
- def eval_class_constants
31
- ::Module.instance_method(:constants).bind(eval("self.class")).call
32
- end
33
- end
34
- }
35
-
36
- # Set of reserved words used by Ruby, you should not use these for
37
- # constants or variables
38
- ReservedWords = %w[
39
- __ENCODING__ __LINE__ __FILE__
40
- BEGIN END
41
- alias and
42
- begin break
43
- case class
44
- def defined? do
45
- else elsif end ensure
46
- false for
47
- if in
48
- module
49
- next nil not
50
- or
51
- redo rescue retry return
52
- self super
53
- then true
54
- undef unless until
55
- when while
56
- yield
57
- ]
11
+ class BaseCompletor # :nodoc:
12
+ def completion_candidates(preposing, target, postposing, bind:)
13
+ raise NotImplementedError
14
+ end
58
15
 
59
- BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
16
+ def doc_namespace(preposing, matched, postposing, bind:)
17
+ raise NotImplementedError
18
+ end
60
19
 
61
20
  GEM_PATHS =
62
21
  if defined?(Gem::Specification)
@@ -73,7 +32,7 @@ module IRB
73
32
  []
74
33
  end.freeze
75
34
 
76
- def self.retrieve_gem_and_system_load_path
35
+ def retrieve_gem_and_system_load_path
77
36
  candidates = (GEM_PATHS | $LOAD_PATH)
78
37
  candidates.map do |p|
79
38
  if p.respond_to?(:to_path)
@@ -84,8 +43,8 @@ module IRB
84
43
  end.compact.sort
85
44
  end
86
45
 
87
- def self.retrieve_files_to_require_from_load_path
88
- @@files_from_load_path ||=
46
+ def retrieve_files_to_require_from_load_path
47
+ @files_from_load_path ||=
89
48
  (
90
49
  shortest = []
91
50
  rest = retrieve_gem_and_system_load_path.each_with_object([]) { |path, result|
@@ -103,13 +62,62 @@ module IRB
103
62
  )
104
63
  end
105
64
 
106
- def self.retrieve_files_to_require_relative_from_current_dir
107
- @@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
65
+ def retrieve_files_to_require_relative_from_current_dir
66
+ @files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
108
67
  path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
109
68
  }
110
69
  end
70
+ end
71
+
72
+ class RegexpCompletor < BaseCompletor # :nodoc:
73
+ using Module.new {
74
+ refine ::Binding do
75
+ def eval_methods
76
+ ::Kernel.instance_method(:methods).bind(eval("self")).call
77
+ end
78
+
79
+ def eval_private_methods
80
+ ::Kernel.instance_method(:private_methods).bind(eval("self")).call
81
+ end
82
+
83
+ def eval_instance_variables
84
+ ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
85
+ end
86
+
87
+ def eval_global_variables
88
+ ::Kernel.instance_method(:global_variables).bind(eval("self")).call
89
+ end
90
+
91
+ def eval_class_constants
92
+ ::Module.instance_method(:constants).bind(eval("self.class")).call
93
+ end
94
+ end
95
+ }
111
96
 
112
- CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
97
+ # Set of reserved words used by Ruby, you should not use these for
98
+ # constants or variables
99
+ ReservedWords = %w[
100
+ __ENCODING__ __LINE__ __FILE__
101
+ BEGIN END
102
+ alias and
103
+ begin break
104
+ case class
105
+ def defined? do
106
+ else elsif end ensure
107
+ false for
108
+ if in
109
+ module
110
+ next nil not
111
+ or
112
+ redo rescue retry return
113
+ self super
114
+ then true
115
+ undef unless until
116
+ when while
117
+ yield
118
+ ]
119
+
120
+ def complete_require_path(target, preposing, postposing)
113
121
  if target =~ /\A(['"])([^'"]+)\Z/
114
122
  quote = $1
115
123
  actual_target = $2
@@ -124,39 +132,37 @@ module IRB
124
132
  break
125
133
  end
126
134
  end
127
- result = []
128
- if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
129
- case tok.tok
130
- when 'require'
131
- result = retrieve_files_to_require_from_load_path.select { |path|
132
- path.start_with?(actual_target)
133
- }.map { |path|
134
- quote + path
135
- }
136
- when 'require_relative'
137
- result = retrieve_files_to_require_relative_from_current_dir.select { |path|
138
- path.start_with?(actual_target)
139
- }.map { |path|
140
- quote + path
141
- }
142
- end
135
+ return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
136
+
137
+ case tok.tok
138
+ when 'require'
139
+ retrieve_files_to_require_from_load_path.select { |path|
140
+ path.start_with?(actual_target)
141
+ }.map { |path|
142
+ quote + path
143
+ }
144
+ when 'require_relative'
145
+ retrieve_files_to_require_relative_from_current_dir.select { |path|
146
+ path.start_with?(actual_target)
147
+ }.map { |path|
148
+ quote + path
149
+ }
143
150
  end
144
- result
145
- }
151
+ end
146
152
 
147
- CompletionProc = lambda { |target, preposing = nil, postposing = nil|
153
+ def completion_candidates(preposing, target, postposing, bind:)
148
154
  if preposing && postposing
149
- result = CompletionRequireProc.(target, preposing, postposing)
150
- unless result
151
- result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
152
- end
153
- result
154
- else
155
- retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
155
+ result = complete_require_path(target, preposing, postposing)
156
+ return result if result
156
157
  end
157
- }
158
+ retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
159
+ end
160
+
161
+ def doc_namespace(_preposing, matched, _postposing, bind:)
162
+ retrieve_completion_data(matched, bind: bind, doc_namespace: true)
163
+ end
158
164
 
159
- def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
165
+ def retrieve_completion_data(input, bind:, doc_namespace:)
160
166
  case input
161
167
  # this regexp only matches the closing character because of irb's Reline.completer_quote_characters setting
162
168
  # details are described in: https://github.com/ruby/irb/pull/523
@@ -394,44 +400,10 @@ module IRB
394
400
  end
395
401
  end
396
402
 
397
- PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
398
- begin
399
- require 'rdoc'
400
- rescue LoadError
401
- return
402
- end
403
-
404
- RDocRIDriver ||= RDoc::RI::Driver.new
405
-
406
- if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
407
- IRB.__send__(:easter_egg)
408
- return
409
- end
410
-
411
- namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
412
- return unless namespace
413
-
414
- if namespace.is_a?(Array)
415
- out = RDoc::Markup::Document.new
416
- namespace.each do |m|
417
- begin
418
- RDocRIDriver.add_method(out, m)
419
- rescue RDoc::RI::Driver::NotFoundError
420
- end
421
- end
422
- RDocRIDriver.display(out)
423
- else
424
- begin
425
- RDocRIDriver.display_names([namespace])
426
- rescue RDoc::RI::Driver::NotFoundError
427
- end
428
- end
429
- }
430
-
431
403
  # Set of available operators in Ruby
432
404
  Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
433
405
 
434
- def self.select_message(receiver, message, candidates, sep = ".")
406
+ def select_message(receiver, message, candidates, sep = ".")
435
407
  candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
436
408
  case e
437
409
  when /^[a-zA-Z_]/
data/lib/irb/debug/ui.rb CHANGED
@@ -4,8 +4,7 @@ require 'debug/console'
4
4
  module IRB
5
5
  module Debug
6
6
  class UI < DEBUGGER__::UI_Base
7
- def initialize(thread, irb)
8
- @thread = thread
7
+ def initialize(irb)
9
8
  @irb = irb
10
9
  end
11
10
 
@@ -56,7 +55,7 @@ module IRB
56
55
 
57
56
  def readline _
58
57
  setup_interrupt do
59
- tc = DEBUGGER__::SESSION.get_thread_client(@thread)
58
+ tc = DEBUGGER__::SESSION.instance_variable_get(:@tc)
60
59
  cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
61
60
 
62
61
  case cmd
data/lib/irb/debug.rb CHANGED
@@ -32,17 +32,14 @@ module IRB
32
32
  end
33
33
  DEBUGGER__::CONFIG.set_config
34
34
  configure_irb_for_debugger(irb)
35
- thread = Thread.current
36
35
 
37
- DEBUGGER__.initialize_session{ IRB::Debug::UI.new(thread, irb) }
36
+ DEBUGGER__.initialize_session{ IRB::Debug::UI.new(irb) }
38
37
  end
39
38
 
40
39
  # When debug session was previously started but not by IRB
41
40
  if defined?(DEBUGGER__::SESSION) && !irb.context.with_debugger
42
41
  configure_irb_for_debugger(irb)
43
- thread = Thread.current
44
-
45
- DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(thread, irb))
42
+ DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(irb))
46
43
  end
47
44
 
48
45
  # Apply patches to debug gem so it skips IRB frames
data/lib/irb/history.rb CHANGED
@@ -10,6 +10,7 @@ module IRB
10
10
 
11
11
  def load_history
12
12
  history = self.class::HISTORY
13
+
13
14
  if history_file = IRB.conf[:HISTORY_FILE]
14
15
  history_file = File.expand_path(history_file)
15
16
  end
@@ -32,7 +33,8 @@ module IRB
32
33
  end
33
34
 
34
35
  def save_history
35
- history = self.class::HISTORY
36
+ history = self.class::HISTORY.to_a
37
+
36
38
  if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0
37
39
  if history_file = IRB.conf[:HISTORY_FILE]
38
40
  history_file = File.expand_path(history_file)
@@ -11,6 +11,8 @@ require 'reline'
11
11
 
12
12
  module IRB
13
13
  class InputMethod
14
+ BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
15
+
14
16
  # The irb prompt associated with this input method
15
17
  attr_accessor :prompt
16
18
 
@@ -179,12 +181,16 @@ module IRB
179
181
  super
180
182
 
181
183
  @eof = false
184
+ @completor = RegexpCompletor.new
182
185
 
183
186
  if Readline.respond_to?("basic_word_break_characters=")
184
- Readline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
187
+ Readline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS
185
188
  end
186
189
  Readline.completion_append_character = nil
187
- Readline.completion_proc = IRB::InputCompletor::CompletionProc
190
+ Readline.completion_proc = ->(target) {
191
+ bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
192
+ @completor.completion_candidates('', target, '', bind: bind)
193
+ }
188
194
  end
189
195
 
190
196
  # Reads the next line from this input method.
@@ -230,11 +236,16 @@ module IRB
230
236
  super
231
237
 
232
238
  @eof = false
239
+ @completor = RegexpCompletor.new
233
240
 
234
- Reline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
241
+ Reline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS
235
242
  Reline.completion_append_character = nil
236
243
  Reline.completer_quote_characters = ''
237
- Reline.completion_proc = IRB::InputCompletor::CompletionProc
244
+ Reline.completion_proc = ->(target, preposing, postposing) {
245
+ bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
246
+ @completion_params = [preposing, target, postposing, bind]
247
+ @completor.completion_candidates(preposing, target, postposing, bind: bind)
248
+ }
238
249
  Reline.output_modifier_proc =
239
250
  if IRB.conf[:USE_COLORIZE]
240
251
  proc do |output, complete: |
@@ -247,13 +258,13 @@ module IRB
247
258
  Reline::Unicode.escape_for_print(output)
248
259
  end
249
260
  end
250
- Reline.dig_perfect_match_proc = IRB::InputCompletor::PerfectMatchedProc
261
+ Reline.dig_perfect_match_proc = ->(matched) { display_document(matched) }
251
262
  Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]
252
263
 
253
264
  if IRB.conf[:USE_AUTOCOMPLETE]
254
265
  begin
255
266
  require 'rdoc'
256
- Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
267
+ Reline.add_dialog_proc(:show_doc, show_doc_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
257
268
  rescue LoadError
258
269
  end
259
270
  end
@@ -271,100 +282,140 @@ module IRB
271
282
  @auto_indent_proc = block
272
283
  end
273
284
 
274
- SHOW_DOC_DIALOG = ->() {
275
- dialog.trap_key = nil
276
- alt_d = [
277
- [Reline::Key.new(nil, 0xE4, true)], # Normal Alt+d.
278
- [27, 100], # Normal Alt+d when convert-meta isn't used.
279
- [195, 164], # The "ä" that appears when Alt+d is pressed on xterm.
280
- [226, 136, 130] # The "∂" that appears when Alt+d in pressed on iTerm2.
281
- ]
285
+ def show_doc_dialog_proc
286
+ doc_namespace = ->(matched) {
287
+ preposing, _target, postposing, bind = @completion_params
288
+ @completor.doc_namespace(preposing, matched, postposing, bind: bind)
289
+ }
290
+ ->() {
291
+ dialog.trap_key = nil
292
+ alt_d = [
293
+ [Reline::Key.new(nil, 0xE4, true)], # Normal Alt+d.
294
+ [27, 100], # Normal Alt+d when convert-meta isn't used.
295
+ [195, 164], # The "ä" that appears when Alt+d is pressed on xterm.
296
+ [226, 136, 130] # The "∂" that appears when Alt+d in pressed on iTerm2.
297
+ ]
298
+
299
+ if just_cursor_moving and completion_journey_data.nil?
300
+ return nil
301
+ end
302
+ cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
303
+ return nil if result.nil? or pointer.nil? or pointer < 0
282
304
 
283
- if just_cursor_moving and completion_journey_data.nil?
284
- return nil
285
- end
286
- cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
287
- return nil if result.nil? or pointer.nil? or pointer < 0
288
- name = result[pointer]
289
- name = IRB::InputCompletor.retrieve_completion_data(name, doc_namespace: true)
305
+ name = doc_namespace.call(result[pointer])
290
306
 
291
- options = {}
292
- options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
293
- driver = RDoc::RI::Driver.new(options)
307
+ options = {}
308
+ options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
309
+ driver = RDoc::RI::Driver.new(options)
294
310
 
295
- if key.match?(dialog.name)
296
- begin
297
- driver.display_names([name])
298
- rescue RDoc::RI::Driver::NotFoundError
311
+ if key.match?(dialog.name)
312
+ begin
313
+ driver.display_names([name])
314
+ rescue RDoc::RI::Driver::NotFoundError
315
+ end
299
316
  end
300
- end
301
317
 
302
- begin
303
- name = driver.expand_name(name)
304
- rescue RDoc::RI::Driver::NotFoundError
305
- return nil
306
- rescue
307
- return nil # unknown error
308
- end
309
- doc = nil
310
- used_for_class = false
311
- if not name =~ /#|\./
312
- found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
313
- if not found.empty?
314
- doc = driver.class_document(name, found, klasses, includes, extends)
315
- used_for_class = true
316
- end
317
- end
318
- unless used_for_class
319
- doc = RDoc::Markup::Document.new
320
318
  begin
321
- driver.add_method(doc, name)
319
+ name = driver.expand_name(name)
322
320
  rescue RDoc::RI::Driver::NotFoundError
323
- doc = nil
321
+ return nil
324
322
  rescue
325
323
  return nil # unknown error
326
324
  end
327
- end
328
- return nil if doc.nil?
329
- width = 40
330
-
331
- right_x = cursor_pos_to_render.x + autocomplete_dialog.width
332
- if right_x + width > screen_width
333
- right_width = screen_width - (right_x + 1)
334
- left_x = autocomplete_dialog.column - width
335
- left_x = 0 if left_x < 0
336
- left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
337
- if right_width.positive? and left_width.positive?
338
- if right_width >= left_width
325
+ doc = nil
326
+ used_for_class = false
327
+ if not name =~ /#|\./
328
+ found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
329
+ if not found.empty?
330
+ doc = driver.class_document(name, found, klasses, includes, extends)
331
+ used_for_class = true
332
+ end
333
+ end
334
+ unless used_for_class
335
+ doc = RDoc::Markup::Document.new
336
+ begin
337
+ driver.add_method(doc, name)
338
+ rescue RDoc::RI::Driver::NotFoundError
339
+ doc = nil
340
+ rescue
341
+ return nil # unknown error
342
+ end
343
+ end
344
+ return nil if doc.nil?
345
+ width = 40
346
+
347
+ right_x = cursor_pos_to_render.x + autocomplete_dialog.width
348
+ if right_x + width > screen_width
349
+ right_width = screen_width - (right_x + 1)
350
+ left_x = autocomplete_dialog.column - width
351
+ left_x = 0 if left_x < 0
352
+ left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
353
+ if right_width.positive? and left_width.positive?
354
+ if right_width >= left_width
355
+ width = right_width
356
+ x = right_x
357
+ else
358
+ width = left_width
359
+ x = left_x
360
+ end
361
+ elsif right_width.positive? and left_width <= 0
339
362
  width = right_width
340
363
  x = right_x
341
- else
364
+ elsif right_width <= 0 and left_width.positive?
342
365
  width = left_width
343
366
  x = left_x
367
+ else # Both are negative width.
368
+ return nil
344
369
  end
345
- elsif right_width.positive? and left_width <= 0
346
- width = right_width
370
+ else
347
371
  x = right_x
348
- elsif right_width <= 0 and left_width.positive?
349
- width = left_width
350
- x = left_x
351
- else # Both are negative width.
352
- return nil
353
372
  end
373
+ formatter = RDoc::Markup::ToAnsi.new
374
+ formatter.width = width
375
+ dialog.trap_key = alt_d
376
+ mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
377
+ message = "Press #{mod_key}+d to read the full document"
378
+ contents = [message] + doc.accept(formatter).split("\n")
379
+ contents = contents.take(preferred_dialog_height)
380
+
381
+ y = cursor_pos_to_render.y
382
+ Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
383
+ }
384
+ end
385
+
386
+ def display_document(matched, driver: nil)
387
+ begin
388
+ require 'rdoc'
389
+ rescue LoadError
390
+ return
391
+ end
392
+
393
+ if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
394
+ IRB.__send__(:easter_egg)
395
+ return
396
+ end
397
+
398
+ _target, preposing, postposing, bind = @completion_params
399
+ namespace = @completor.doc_namespace(preposing, matched, postposing, bind: bind)
400
+ return unless namespace
401
+
402
+ driver ||= RDoc::RI::Driver.new
403
+ if namespace.is_a?(Array)
404
+ out = RDoc::Markup::Document.new
405
+ namespace.each do |m|
406
+ begin
407
+ driver.add_method(out, m)
408
+ rescue RDoc::RI::Driver::NotFoundError
409
+ end
410
+ end
411
+ driver.display(out)
354
412
  else
355
- x = right_x
413
+ begin
414
+ driver.display_names([namespace])
415
+ rescue RDoc::RI::Driver::NotFoundError
416
+ end
356
417
  end
357
- formatter = RDoc::Markup::ToAnsi.new
358
- formatter.width = width
359
- dialog.trap_key = alt_d
360
- mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
361
- message = "Press #{mod_key}+d to read the full document"
362
- contents = [message] + doc.accept(formatter).split("\n")
363
- contents = contents.take(preferred_dialog_height)
364
-
365
- y = cursor_pos_to_render.y
366
- Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
367
- }
418
+ end
368
419
 
369
420
  # Reads the next line from this input method.
370
421
  #
data/lib/irb/ruby-lex.rb CHANGED
@@ -42,14 +42,6 @@ module IRB
42
42
  end
43
43
  end
44
44
 
45
- attr_reader :line_no
46
-
47
- def initialize(context)
48
- @context = context
49
- @line_no = 1
50
- @prompt = nil
51
- end
52
-
53
45
  def self.compile_with_errors_suppressed(code, line_no: 1)
54
46
  begin
55
47
  result = yield code, line_no
@@ -67,10 +59,6 @@ module IRB
67
59
  result
68
60
  end
69
61
 
70
- def set_prompt(&block)
71
- @prompt = block
72
- end
73
-
74
62
  ERROR_TOKENS = [
75
63
  :on_parse_error,
76
64
  :compile_error,
@@ -116,9 +104,9 @@ module IRB
116
104
  interpolated
117
105
  end
118
106
 
119
- def self.ripper_lex_without_warning(code, context: nil)
107
+ def self.ripper_lex_without_warning(code, local_variables: [])
120
108
  verbose, $VERBOSE = $VERBOSE, nil
121
- lvars_code = generate_local_variables_assign_code(context&.local_variables || [])
109
+ lvars_code = generate_local_variables_assign_code(local_variables)
122
110
  original_code = code
123
111
  if lvars_code
124
112
  code = "#{lvars_code}\n#{code}"
@@ -146,20 +134,14 @@ module IRB
146
134
  $VERBOSE = verbose
147
135
  end
148
136
 
149
- def prompt(opens, continue, line_num_offset)
150
- ltype = ltype_from_open_tokens(opens)
151
- indent_level = calc_indent_level(opens)
152
- @prompt&.call(ltype, indent_level, opens.any? || continue, @line_no + line_num_offset)
153
- end
154
-
155
- def check_code_state(code)
156
- tokens = self.class.ripper_lex_without_warning(code, context: @context)
137
+ def check_code_state(code, local_variables:)
138
+ tokens = self.class.ripper_lex_without_warning(code, local_variables: local_variables)
157
139
  opens = NestingParser.open_tokens(tokens)
158
- [tokens, opens, code_terminated?(code, tokens, opens)]
140
+ [tokens, opens, code_terminated?(code, tokens, opens, local_variables: local_variables)]
159
141
  end
160
142
 
161
- def code_terminated?(code, tokens, opens)
162
- case check_code_syntax(code)
143
+ def code_terminated?(code, tokens, opens, local_variables:)
144
+ case check_code_syntax(code, local_variables: local_variables)
163
145
  when :unrecoverable_error
164
146
  true
165
147
  when :recoverable_error
@@ -171,16 +153,7 @@ module IRB
171
153
  end
172
154
  end
173
155
 
174
- def save_prompt_to_context_io(opens, continue, line_num_offset)
175
- # Implicitly saves prompt string to `@context.io.prompt`. This will be used in the next `@input.call`.
176
- prompt(opens, continue, line_num_offset)
177
- end
178
-
179
- def increase_line_no(addition)
180
- @line_no += addition
181
- end
182
-
183
- def assignment_expression?(code)
156
+ def assignment_expression?(code, local_variables:)
184
157
  # Try to parse the code and check if the last of possibly multiple
185
158
  # expressions is an assignment type.
186
159
 
@@ -190,7 +163,7 @@ module IRB
190
163
  # array of parsed expressions. The first element of each expression is the
191
164
  # expression's type.
192
165
  verbose, $VERBOSE = $VERBOSE, nil
193
- code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{code}"
166
+ code = "#{RubyLex.generate_local_variables_assign_code(local_variables) || 'nil;'}\n#{code}"
194
167
  # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
195
168
  node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
196
169
  ASSIGNMENT_NODE_TYPES.include?(node_type)
@@ -222,8 +195,8 @@ module IRB
222
195
  false
223
196
  end
224
197
 
225
- def check_code_syntax(code)
226
- lvars_code = RubyLex.generate_local_variables_assign_code(@context.local_variables)
198
+ def check_code_syntax(code, local_variables:)
199
+ lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
227
200
  code = "#{lvars_code}\n#{code}"
228
201
 
229
202
  begin # check if parser error are available
@@ -455,8 +428,8 @@ module IRB
455
428
  end
456
429
  end
457
430
 
458
- def check_termination_in_prev_line(code)
459
- tokens = self.class.ripper_lex_without_warning(code, context: @context)
431
+ def check_termination_in_prev_line(code, local_variables:)
432
+ tokens = self.class.ripper_lex_without_warning(code, local_variables: local_variables)
460
433
  past_first_newline = false
461
434
  index = tokens.rindex do |t|
462
435
  # traverse first token before last line
@@ -486,7 +459,7 @@ module IRB
486
459
  tokens_without_last_line = tokens[0..index]
487
460
  code_without_last_line = tokens_without_last_line.map(&:tok).join
488
461
  opens_without_last_line = NestingParser.open_tokens(tokens_without_last_line)
489
- if code_terminated?(code_without_last_line, tokens_without_last_line, opens_without_last_line)
462
+ if code_terminated?(code_without_last_line, tokens_without_last_line, opens_without_last_line, local_variables: local_variables)
490
463
  return last_line_tokens.map(&:tok).join
491
464
  end
492
465
  end
@@ -43,7 +43,7 @@ module IRB
43
43
  private
44
44
 
45
45
  def find_end(file, first_line)
46
- lex = RubyLex.new(@irb_context)
46
+ lex = RubyLex.new
47
47
  lines = File.read(file).lines[(first_line - 1)..-1]
48
48
  tokens = RubyLex.ripper_lex_without_warning(lines.join)
49
49
  prev_tokens = []
@@ -53,7 +53,7 @@ module IRB
53
53
  code = lines[0..lnum].join
54
54
  prev_tokens.concat chunk
55
55
  continue = lex.should_continue?(prev_tokens)
56
- syntax = lex.check_code_syntax(code)
56
+ syntax = lex.check_code_syntax(code, local_variables: [])
57
57
  if !continue && syntax == :valid
58
58
  return first_line + lnum
59
59
  end
data/lib/irb/version.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  #
6
6
 
7
7
  module IRB # :nodoc:
8
- VERSION = "1.8.0"
8
+ VERSION = "1.8.2"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2023-08-30"
10
+ @LAST_UPDATE_DATE = "2023-10-12"
11
11
  end
data/lib/irb.rb CHANGED
@@ -436,7 +436,8 @@ module IRB
436
436
  @context = Context.new(self, workspace, input_method)
437
437
  @context.workspace.load_commands_to_main
438
438
  @signal_status = :IN_IRB
439
- @scanner = RubyLex.new(@context)
439
+ @scanner = RubyLex.new
440
+ @line_no = 1
440
441
  end
441
442
 
442
443
  # A hook point for `debug` command's breakpoint after :IRB_EXIT as well as its clean-up
@@ -454,7 +455,7 @@ module IRB
454
455
  workspace = IRB::WorkSpace.new(binding)
455
456
  context.workspace = workspace
456
457
  context.workspace.load_commands_to_main
457
- scanner.increase_line_no(1)
458
+ @line_no += 1
458
459
 
459
460
  # When users run:
460
461
  # 1. Debugging commands, like `step 2`
@@ -476,7 +477,7 @@ module IRB
476
477
  end
477
478
 
478
479
  if input&.include?("\n")
479
- scanner.increase_line_no(input.count("\n") - 1)
480
+ @line_no += input.count("\n") - 1
480
481
  end
481
482
 
482
483
  input
@@ -513,34 +514,38 @@ module IRB
513
514
  # The lexer used by this irb session
514
515
  attr_accessor :scanner
515
516
 
516
- # Evaluates input for this session.
517
- def eval_input
518
- @scanner.set_prompt do
519
- |ltype, indent, continue, line_no|
520
- if ltype
521
- f = @context.prompt_s
522
- elsif continue
523
- f = @context.prompt_c
524
- else
525
- f = @context.prompt_i
526
- end
527
- f = "" unless f
528
- if @context.prompting?
529
- @context.io.prompt = p = prompt(f, ltype, indent, line_no)
530
- else
531
- @context.io.prompt = p = ""
532
- end
533
- if @context.auto_indent_mode and !@context.io.respond_to?(:auto_indent)
534
- unless ltype
535
- prompt_i = @context.prompt_i.nil? ? "" : @context.prompt_i
536
- ind = prompt(prompt_i, ltype, indent, line_no)[/.*\z/].size +
537
- indent * 2 - p.size
538
- @context.io.prompt = p + " " * ind if ind > 0
539
- end
517
+ private def generate_prompt(opens, continue, line_offset)
518
+ ltype = @scanner.ltype_from_open_tokens(opens)
519
+ indent = @scanner.calc_indent_level(opens)
520
+ continue = opens.any? || continue
521
+ line_no = @line_no + line_offset
522
+
523
+ if ltype
524
+ f = @context.prompt_s
525
+ elsif continue
526
+ f = @context.prompt_c
527
+ else
528
+ f = @context.prompt_i
529
+ end
530
+ f = "" unless f
531
+ if @context.prompting?
532
+ p = format_prompt(f, ltype, indent, line_no)
533
+ else
534
+ p = ""
535
+ end
536
+ if @context.auto_indent_mode and !@context.io.respond_to?(:auto_indent)
537
+ unless ltype
538
+ prompt_i = @context.prompt_i.nil? ? "" : @context.prompt_i
539
+ ind = format_prompt(prompt_i, ltype, indent, line_no)[/.*\z/].size +
540
+ indent * 2 - p.size
541
+ p += " " * ind if ind > 0
540
542
  end
541
- @context.io.prompt
542
543
  end
544
+ p
545
+ end
543
546
 
547
+ # Evaluates input for this session.
548
+ def eval_input
544
549
  configure_io
545
550
 
546
551
  each_top_level_statement do |statement, line_no|
@@ -572,8 +577,9 @@ module IRB
572
577
  end
573
578
  end
574
579
 
575
- def read_input
580
+ def read_input(prompt)
576
581
  signal_status(:IN_INPUT) do
582
+ @context.io.prompt = prompt
577
583
  if l = @context.io.gets
578
584
  print l if @context.verbose?
579
585
  else
@@ -591,16 +597,16 @@ module IRB
591
597
  end
592
598
 
593
599
  def readmultiline
594
- @scanner.save_prompt_to_context_io([], false, 0)
600
+ prompt = generate_prompt([], false, 0)
595
601
 
596
602
  # multiline
597
- return read_input if @context.io.respond_to?(:check_termination)
603
+ return read_input(prompt) if @context.io.respond_to?(:check_termination)
598
604
 
599
605
  # nomultiline
600
606
  code = ''
601
607
  line_offset = 0
602
608
  loop do
603
- line = read_input
609
+ line = read_input(prompt)
604
610
  unless line
605
611
  return code.empty? ? nil : code
606
612
  end
@@ -610,12 +616,12 @@ module IRB
610
616
  # Accept any single-line input for symbol aliases or commands that transform args
611
617
  return code if single_line_command?(code)
612
618
 
613
- tokens, opens, terminated = @scanner.check_code_state(code)
619
+ tokens, opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
614
620
  return code if terminated
615
621
 
616
622
  line_offset += 1
617
623
  continue = @scanner.should_continue?(tokens)
618
- @scanner.save_prompt_to_context_io(opens, continue, line_offset)
624
+ prompt = generate_prompt(opens, continue, line_offset)
619
625
  end
620
626
  end
621
627
 
@@ -625,9 +631,9 @@ module IRB
625
631
  break unless code
626
632
 
627
633
  if code != "\n"
628
- yield build_statement(code), @scanner.line_no
634
+ yield build_statement(code), @line_no
629
635
  end
630
- @scanner.increase_line_no(code.count("\n"))
636
+ @line_no += code.count("\n")
631
637
  rescue RubyLex::TerminateLineInput
632
638
  end
633
639
  end
@@ -643,7 +649,8 @@ module IRB
643
649
  if command_class
644
650
  Statement::Command.new(code, command, arg, command_class)
645
651
  else
646
- Statement::Expression.new(code, @scanner.assignment_expression?(code))
652
+ is_assignment_expression = @scanner.assignment_expression?(code, local_variables: @context.local_variables)
653
+ Statement::Expression.new(code, is_assignment_expression)
647
654
  end
648
655
  end
649
656
 
@@ -656,7 +663,7 @@ module IRB
656
663
  if @context.io.respond_to?(:check_termination)
657
664
  @context.io.check_termination do |code|
658
665
  if Reline::IOGate.in_pasting?
659
- rest = @scanner.check_termination_in_prev_line(code)
666
+ rest = @scanner.check_termination_in_prev_line(code, local_variables: @context.local_variables)
660
667
  if rest
661
668
  Reline.delete_text
662
669
  rest.bytes.reverse_each do |c|
@@ -670,7 +677,7 @@ module IRB
670
677
  # Accept any single-line input for symbol aliases or commands that transform args
671
678
  next true if single_line_command?(code)
672
679
 
673
- _tokens, _opens, terminated = @scanner.check_code_state(code)
680
+ _tokens, _opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
674
681
  terminated
675
682
  end
676
683
  end
@@ -678,7 +685,7 @@ module IRB
678
685
  if @context.io.respond_to?(:dynamic_prompt)
679
686
  @context.io.dynamic_prompt do |lines|
680
687
  lines << '' if lines.empty?
681
- tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, context: @context)
688
+ tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, local_variables: @context.local_variables)
682
689
  line_results = IRB::NestingParser.parse_by_line(tokens)
683
690
  tokens_until_line = []
684
691
  line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
@@ -687,7 +694,7 @@ module IRB
687
694
  tokens_until_line << token if token != tokens_until_line.last
688
695
  end
689
696
  continue = @scanner.should_continue?(tokens_until_line)
690
- @scanner.prompt(next_opens, continue, line_num_offset)
697
+ generate_prompt(next_opens, continue, line_num_offset)
691
698
  end
692
699
  end
693
700
  end
@@ -698,7 +705,7 @@ module IRB
698
705
  next nil if !is_newline && lines[line_index]&.byteslice(0, byte_pointer)&.match?(/\A\s*\z/)
699
706
 
700
707
  code = lines[0..line_index].map { |l| "#{l}\n" }.join
701
- tokens = RubyLex.ripper_lex_without_warning(code, context: @context)
708
+ tokens = RubyLex.ripper_lex_without_warning(code, local_variables: @context.local_variables)
702
709
  @scanner.process_indent_level(tokens, lines, line_index, is_newline)
703
710
  end
704
711
  end
@@ -873,7 +880,7 @@ module IRB
873
880
  end
874
881
  end
875
882
 
876
- def truncate_prompt_main(str) # :nodoc:
883
+ private def truncate_prompt_main(str) # :nodoc:
877
884
  str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
878
885
  if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH
879
886
  str
@@ -882,9 +889,8 @@ module IRB
882
889
  end
883
890
  end
884
891
 
885
- def prompt(prompt, ltype, indent, line_no) # :nodoc:
886
- p = prompt.dup
887
- p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
892
+ private def format_prompt(format, ltype, indent, line_no) # :nodoc:
893
+ format.gsub(/%([0-9]+)?([a-zA-Z])/) do
888
894
  case $2
889
895
  when "N"
890
896
  @context.irb_name
@@ -918,7 +924,6 @@ module IRB
918
924
  "%"
919
925
  end
920
926
  end
921
- p
922
927
  end
923
928
 
924
929
  def output_value(omit = false) # :nodoc:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-08-30 00:00:00.000000000 Z
12
+ date: 2023-10-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: reline
@@ -17,28 +17,28 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: 0.3.6
20
+ version: 0.3.8
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: 0.3.6
27
+ version: 0.3.8
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: rdoc
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: '6.5'
34
+ version: '0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - "~>"
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: '6.5'
41
+ version: '0'
42
42
  description: Interactive Ruby command-line tool for REPL (Read Eval Print Loop).
43
43
  email:
44
44
  - aycabta@gmail.com