irb 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b6bfef2fbb8e5e7d7587a567055e2b7f019678d2355f6ae52f7a7d8161df0c0
4
- data.tar.gz: 653be685d9f249341c3e165cb4303baa05563c5556cc2bf2584243c39ec0cb92
3
+ metadata.gz: 811c1b4343957dac57d58178adbc13195e6d53de980f63810ec83eec09d1f8c1
4
+ data.tar.gz: bbc99d3f46a8e7afa6a7a8e32dc137300e3da15935631e05a56b203a901887fb
5
5
  SHA512:
6
- metadata.gz: 215ce7d8130e1a43382706858c487d7835971e326e93eb1c2ccb559b7dec5f1ecd216b451ad535beaba5e908419ec3e1c13bcc8dea8a0e6d2d7c45fed4639fdb
7
- data.tar.gz: c950ff10929df5ac8365ec0b86fcafe1ac85685ef955b6d12ec7febaf2a9bc85be8b325a58b60583c2a7567889cc6fbe9c6c6eb9b16a10f35409b7da21c7cd32
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 :
@@ -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.1"
8
+ VERSION = "1.8.2"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2023-09-04"
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.1
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-09-05 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