irb 1.7.1 → 1.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -1
  3. data/Gemfile +10 -1
  4. data/README.md +265 -20
  5. data/Rakefile +13 -10
  6. data/doc/irb/irb.rd.ja +1 -3
  7. data/irb.gemspec +2 -1
  8. data/lib/irb/cmd/nop.rb +3 -52
  9. data/lib/irb/color.rb +4 -2
  10. data/lib/irb/command/backtrace.rb +17 -0
  11. data/lib/irb/command/base.rb +62 -0
  12. data/lib/irb/command/break.rb +17 -0
  13. data/lib/irb/command/catch.rb +17 -0
  14. data/lib/irb/command/chws.rb +40 -0
  15. data/lib/irb/command/context.rb +16 -0
  16. data/lib/irb/{cmd → command}/continue.rb +3 -3
  17. data/lib/irb/command/debug.rb +71 -0
  18. data/lib/irb/{cmd → command}/delete.rb +3 -3
  19. data/lib/irb/command/disable_irb.rb +19 -0
  20. data/lib/irb/command/edit.rb +63 -0
  21. data/lib/irb/command/exit.rb +18 -0
  22. data/lib/irb/{cmd → command}/finish.rb +3 -3
  23. data/lib/irb/command/force_exit.rb +18 -0
  24. data/lib/irb/command/help.rb +83 -0
  25. data/lib/irb/command/history.rb +45 -0
  26. data/lib/irb/command/info.rb +17 -0
  27. data/lib/irb/command/internal_helpers.rb +27 -0
  28. data/lib/irb/{cmd → command}/irb_info.rb +7 -7
  29. data/lib/irb/{cmd → command}/load.rb +23 -8
  30. data/lib/irb/{cmd → command}/ls.rb +42 -19
  31. data/lib/irb/{cmd → command}/measure.rb +18 -17
  32. data/lib/irb/{cmd → command}/next.rb +3 -3
  33. data/lib/irb/command/pushws.rb +65 -0
  34. data/lib/irb/command/show_doc.rb +51 -0
  35. data/lib/irb/command/show_source.rb +74 -0
  36. data/lib/irb/{cmd → command}/step.rb +3 -3
  37. data/lib/irb/command/subirb.rb +123 -0
  38. data/lib/irb/{cmd → command}/whereami.rb +3 -5
  39. data/lib/irb/command.rb +23 -0
  40. data/lib/irb/completion.rb +133 -102
  41. data/lib/irb/context.rb +182 -66
  42. data/lib/irb/debug/ui.rb +103 -0
  43. data/lib/irb/{cmd/debug.rb → debug.rb} +53 -59
  44. data/lib/irb/default_commands.rb +265 -0
  45. data/lib/irb/easter-egg.rb +16 -6
  46. data/lib/irb/ext/change-ws.rb +6 -8
  47. data/lib/irb/ext/{history.rb → eval_history.rb} +7 -7
  48. data/lib/irb/ext/loader.rb +4 -4
  49. data/lib/irb/ext/multi-irb.rb +5 -5
  50. data/lib/irb/ext/tracer.rb +12 -51
  51. data/lib/irb/ext/use-loader.rb +6 -8
  52. data/lib/irb/ext/workspaces.rb +10 -34
  53. data/lib/irb/frame.rb +1 -1
  54. data/lib/irb/help.rb +3 -3
  55. data/lib/irb/helper_method/base.rb +16 -0
  56. data/lib/irb/helper_method/conf.rb +11 -0
  57. data/lib/irb/helper_method.rb +29 -0
  58. data/lib/irb/{ext/save-history.rb → history.rb} +20 -58
  59. data/lib/irb/init.rb +154 -58
  60. data/lib/irb/input-method.rb +238 -203
  61. data/lib/irb/inspector.rb +3 -3
  62. data/lib/irb/lc/error.rb +1 -11
  63. data/lib/irb/lc/help-message +4 -0
  64. data/lib/irb/lc/ja/error.rb +1 -11
  65. data/lib/irb/lc/ja/help-message +13 -0
  66. data/lib/irb/locale.rb +2 -2
  67. data/lib/irb/nesting_parser.rb +13 -3
  68. data/lib/irb/notifier.rb +1 -1
  69. data/lib/irb/output-method.rb +2 -8
  70. data/lib/irb/pager.rb +91 -0
  71. data/lib/irb/ruby-lex.rb +391 -471
  72. data/lib/irb/ruby_logo.aa +43 -0
  73. data/lib/irb/source_finder.rb +139 -0
  74. data/lib/irb/statement.rb +80 -0
  75. data/lib/irb/version.rb +3 -3
  76. data/lib/irb/workspace.rb +24 -4
  77. data/lib/irb/ws-for-case-2.rb +1 -1
  78. data/lib/irb/xmp.rb +3 -3
  79. data/lib/irb.rb +1232 -604
  80. data/man/irb.1 +7 -0
  81. metadata +60 -32
  82. data/lib/irb/cmd/backtrace.rb +0 -21
  83. data/lib/irb/cmd/break.rb +0 -21
  84. data/lib/irb/cmd/catch.rb +0 -21
  85. data/lib/irb/cmd/chws.rb +0 -36
  86. data/lib/irb/cmd/edit.rb +0 -61
  87. data/lib/irb/cmd/help.rb +0 -23
  88. data/lib/irb/cmd/info.rb +0 -21
  89. data/lib/irb/cmd/pushws.rb +0 -45
  90. data/lib/irb/cmd/show_cmds.rb +0 -39
  91. data/lib/irb/cmd/show_doc.rb +0 -48
  92. data/lib/irb/cmd/show_source.rb +0 -113
  93. data/lib/irb/cmd/subirb.rb +0 -66
  94. data/lib/irb/extend-command.rb +0 -356
  95. data/lib/irb/src_encoding.rb +0 -7
data/lib/irb/context.rb CHANGED
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  #
3
3
  # irb/context.rb - irb context
4
4
  # by Keiju ISHITSUKA(keiju@ruby-lang.org)
@@ -22,10 +22,11 @@ module IRB
22
22
  # +other+:: uses this as InputMethod
23
23
  def initialize(irb, workspace = nil, input_method = nil)
24
24
  @irb = irb
25
+ @workspace_stack = []
25
26
  if workspace
26
- @workspace = workspace
27
+ @workspace_stack << workspace
27
28
  else
28
- @workspace = WorkSpace.new
29
+ @workspace_stack << WorkSpace.new
29
30
  end
30
31
  @thread = Thread.current
31
32
 
@@ -61,7 +62,7 @@ module IRB
61
62
  @io = nil
62
63
 
63
64
  self.inspect_mode = IRB.conf[:INSPECT_MODE]
64
- self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER]
65
+ self.use_tracer = IRB.conf[:USE_TRACER]
65
66
  self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER]
66
67
  self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY]
67
68
 
@@ -72,33 +73,34 @@ module IRB
72
73
 
73
74
  self.prompt_mode = IRB.conf[:PROMPT_MODE]
74
75
 
75
- if IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager)
76
- @irb_name = IRB.conf[:IRB_NAME]
77
- else
78
- @irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s
76
+ @irb_name = IRB.conf[:IRB_NAME]
77
+
78
+ unless IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager)
79
+ @irb_name = @irb_name + "#" + IRB.JobManager.n_jobs.to_s
79
80
  end
80
- @irb_path = "(" + @irb_name + ")"
81
+
82
+ self.irb_path = "(" + @irb_name + ")"
81
83
 
82
84
  case input_method
83
85
  when nil
84
86
  @io = nil
85
87
  case use_multiline?
86
88
  when nil
87
- if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
89
+ if term_interactive? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
88
90
  # Both of multiline mode and singleline mode aren't specified.
89
- @io = RelineInputMethod.new
91
+ @io = RelineInputMethod.new(build_completor)
90
92
  else
91
93
  @io = nil
92
94
  end
93
95
  when false
94
96
  @io = nil
95
97
  when true
96
- @io = RelineInputMethod.new
98
+ @io = RelineInputMethod.new(build_completor)
97
99
  end
98
100
  unless @io
99
101
  case use_singleline?
100
102
  when nil
101
- if (defined?(ReadlineInputMethod) && STDIN.tty? &&
103
+ if (defined?(ReadlineInputMethod) && term_interactive? &&
102
104
  IRB.conf[:PROMPT_MODE] != :INF_RUBY)
103
105
  @io = ReadlineInputMethod.new
104
106
  else
@@ -121,16 +123,14 @@ module IRB
121
123
  when '-'
122
124
  @io = FileInputMethod.new($stdin)
123
125
  @irb_name = '-'
124
- @irb_path = '-'
126
+ self.irb_path = '-'
125
127
  when String
126
128
  @io = FileInputMethod.new(input_method)
127
129
  @irb_name = File.basename(input_method)
128
- @irb_path = input_method
130
+ self.irb_path = input_method
129
131
  else
130
132
  @io = input_method
131
133
  end
132
- self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY]
133
-
134
134
  @extra_doc_dirs = IRB.conf[:EXTRA_DOC_DIRS]
135
135
 
136
136
  @echo = IRB.conf[:ECHO]
@@ -148,18 +148,112 @@ module IRB
148
148
  @newline_before_multiline_output = true
149
149
  end
150
150
 
151
- @command_aliases = IRB.conf[:COMMAND_ALIASES]
151
+ @user_aliases = IRB.conf[:COMMAND_ALIASES].dup
152
+ @command_aliases = @user_aliases.merge(KEYWORD_ALIASES)
153
+ end
154
+
155
+ private def term_interactive?
156
+ return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
157
+ STDIN.tty? && ENV['TERM'] != 'dumb'
158
+ end
159
+
160
+ # because all input will eventually be evaluated as Ruby code,
161
+ # command names that conflict with Ruby keywords need special workaround
162
+ # we can remove them once we implemented a better command system for IRB
163
+ KEYWORD_ALIASES = {
164
+ :break => :irb_break,
165
+ :catch => :irb_catch,
166
+ :next => :irb_next,
167
+ }.freeze
168
+
169
+ private_constant :KEYWORD_ALIASES
170
+
171
+ def use_tracer=(val)
172
+ require_relative "ext/tracer" if val
173
+ IRB.conf[:USE_TRACER] = val
174
+ end
175
+
176
+ def eval_history=(val)
177
+ self.class.remove_method(__method__)
178
+ require_relative "ext/eval_history"
179
+ __send__(__method__, val)
180
+ end
181
+
182
+ def use_loader=(val)
183
+ self.class.remove_method(__method__)
184
+ require_relative "ext/use-loader"
185
+ __send__(__method__, val)
186
+ end
187
+
188
+ private def build_completor
189
+ completor_type = IRB.conf[:COMPLETOR]
190
+ case completor_type
191
+ when :regexp
192
+ return RegexpCompletor.new
193
+ when :type
194
+ completor = build_type_completor
195
+ return completor if completor
196
+ else
197
+ warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
198
+ end
199
+ # Fallback to RegexpCompletor
200
+ RegexpCompletor.new
201
+ end
202
+
203
+ private def build_type_completor
204
+ if RUBY_ENGINE == 'truffleruby'
205
+ # Avoid SyntaxError. truffleruby does not support endless method definition yet.
206
+ warn 'TypeCompletor is not supported on TruffleRuby yet'
207
+ return
208
+ end
209
+
210
+ begin
211
+ require 'repl_type_completor'
212
+ rescue LoadError => e
213
+ warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}"
214
+ return
215
+ end
216
+
217
+ ReplTypeCompletor.preload_rbs
218
+ TypeCompletor.new(self)
219
+ end
220
+
221
+ def save_history=(val)
222
+ IRB.conf[:SAVE_HISTORY] = val
223
+ end
224
+
225
+ def save_history
226
+ IRB.conf[:SAVE_HISTORY]
227
+ end
228
+
229
+ # A copy of the default <code>IRB.conf[:HISTORY_FILE]</code>
230
+ def history_file
231
+ IRB.conf[:HISTORY_FILE]
232
+ end
233
+
234
+ # Set <code>IRB.conf[:HISTORY_FILE]</code> to the given +hist+.
235
+ def history_file=(hist)
236
+ IRB.conf[:HISTORY_FILE] = hist
237
+ end
238
+
239
+ # Workspace in the current context.
240
+ def workspace
241
+ @workspace_stack.last
242
+ end
243
+
244
+ # Replace the current workspace with the given +workspace+.
245
+ def replace_workspace(workspace)
246
+ @workspace_stack.pop
247
+ @workspace_stack.push(workspace)
152
248
  end
153
249
 
154
250
  # The top-level workspace, see WorkSpace#main
155
251
  def main
156
- @workspace.main
252
+ workspace.main
157
253
  end
158
254
 
159
255
  # The toplevel workspace, see #home_workspace
160
256
  attr_reader :workspace_home
161
- # WorkSpace in the current context.
162
- attr_accessor :workspace
163
257
  # The current thread in this context.
164
258
  attr_reader :thread
165
259
  # The current input method.
@@ -180,9 +274,27 @@ module IRB
180
274
  # Can be either name from <code>IRB.conf[:IRB_NAME]</code>, or the number of
181
275
  # the current job set by JobManager, such as <code>irb#2</code>
182
276
  attr_accessor :irb_name
183
- # Can be either the #irb_name surrounded by parenthesis, or the
184
- # +input_method+ passed to Context.new
185
- attr_accessor :irb_path
277
+
278
+ # Can be one of the following:
279
+ # - the #irb_name surrounded by parenthesis
280
+ # - the +input_method+ passed to Context.new
281
+ # - the file path of the current IRB context in a binding.irb session
282
+ attr_reader :irb_path
283
+
284
+ # Sets @irb_path to the given +path+ as well as @eval_path
285
+ # @eval_path is used for evaluating code in the context of IRB session
286
+ # It's the same as irb_path, but with the IRB name postfix
287
+ # This makes sure users can distinguish the methods defined in the IRB session
288
+ # from the methods defined in the current file's context, especially with binding.irb
289
+ def irb_path=(path)
290
+ @irb_path = path
291
+
292
+ if File.exist?(path)
293
+ @eval_path = "#{path}(#{IRB.conf[:IRB_NAME]})"
294
+ else
295
+ @eval_path = path
296
+ end
297
+ end
186
298
 
187
299
  # Whether multiline editor mode is enabled or not.
188
300
  #
@@ -203,18 +315,29 @@ module IRB
203
315
  attr_reader :prompt_mode
204
316
  # Standard IRB prompt.
205
317
  #
206
- # See IRB@Customizing+the+IRB+Prompt for more information.
318
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
207
319
  attr_accessor :prompt_i
208
320
  # IRB prompt for continuated strings.
209
321
  #
210
- # See IRB@Customizing+the+IRB+Prompt for more information.
322
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
211
323
  attr_accessor :prompt_s
212
324
  # IRB prompt for continuated statement. (e.g. immediately after an +if+)
213
325
  #
214
- # See IRB@Customizing+the+IRB+Prompt for more information.
326
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
215
327
  attr_accessor :prompt_c
216
- # See IRB@Customizing+the+IRB+Prompt for more information.
217
- attr_accessor :prompt_n
328
+
329
+ # TODO: Remove this when developing v2.0
330
+ def prompt_n
331
+ warn "IRB::Context#prompt_n is deprecated and will be removed in the next major release."
332
+ ""
333
+ end
334
+
335
+ # TODO: Remove this when developing v2.0
336
+ def prompt_n=(_)
337
+ warn "IRB::Context#prompt_n= is deprecated and will be removed in the next major release."
338
+ ""
339
+ end
340
+
218
341
  # Can be either the default <code>IRB.conf[:AUTO_INDENT]</code>, or the
219
342
  # mode set by #prompt_mode=
220
343
  #
@@ -322,13 +445,13 @@ module IRB
322
445
  # The default value is 16.
323
446
  #
324
447
  # Can also be set using the +--back-trace-limit+ command line option.
325
- #
326
- # See IRB@Command+line+options for more command line options.
327
448
  attr_accessor :back_trace_limit
328
449
 
329
450
  # User-defined IRB command aliases
330
451
  attr_accessor :command_aliases
331
452
 
453
+ attr_accessor :with_debugger
454
+
332
455
  # Alias for #use_multiline
333
456
  alias use_multiline? use_multiline
334
457
  # Alias for #use_singleline
@@ -372,9 +495,7 @@ module IRB
372
495
  # StdioInputMethod or RelineInputMethod or ReadlineInputMethod, see #io
373
496
  # for more information.
374
497
  def prompting?
375
- verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
376
- @io.kind_of?(RelineInputMethod) ||
377
- (defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
498
+ verbose? || @io.prompting?
378
499
  end
379
500
 
380
501
  # The return value of the last statement evaluated.
@@ -384,19 +505,18 @@ module IRB
384
505
  # to #last_value.
385
506
  def set_last_value(value)
386
507
  @last_value = value
387
- @workspace.local_variable_set :_, value
508
+ workspace.local_variable_set :_, value
388
509
  end
389
510
 
390
511
  # Sets the +mode+ of the prompt in this context.
391
512
  #
392
- # See IRB@Customizing+the+IRB+Prompt for more information.
513
+ # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
393
514
  def prompt_mode=(mode)
394
515
  @prompt_mode = mode
395
516
  pconf = IRB.conf[:PROMPT][mode]
396
517
  @prompt_i = pconf[:PROMPT_I]
397
518
  @prompt_s = pconf[:PROMPT_S]
398
519
  @prompt_c = pconf[:PROMPT_C]
399
- @prompt_n = pconf[:PROMPT_N]
400
520
  @return_format = pconf[:RETURN]
401
521
  @return_format = "%s\n" if @return_format == nil
402
522
  if ai = pconf.include?(:AUTO_INDENT)
@@ -428,8 +548,6 @@ module IRB
428
548
  #
429
549
  # Can also be set using the +--inspect+ and +--noinspect+ command line
430
550
  # options.
431
- #
432
- # See IRB@Command+line+options for more command line options.
433
551
  def inspect_mode=(opt)
434
552
 
435
553
  if i = Inspector::INSPECTORS[opt]
@@ -473,45 +591,55 @@ module IRB
473
591
  @inspect_mode
474
592
  end
475
593
 
476
- def evaluate(line, line_no) # :nodoc:
594
+ def evaluate(statement, line_no) # :nodoc:
477
595
  @line_no = line_no
478
- result = nil
479
596
 
597
+ case statement
598
+ when Statement::EmptyInput
599
+ return
600
+ when Statement::Expression
601
+ result = evaluate_expression(statement.code, line_no)
602
+ set_last_value(result)
603
+ when Statement::Command
604
+ statement.command_class.execute(self, statement.arg)
605
+ set_last_value(nil)
606
+ end
607
+
608
+ nil
609
+ end
610
+
611
+ def from_binding?
612
+ @irb.from_binding
613
+ end
614
+
615
+ def evaluate_expression(code, line_no) # :nodoc:
616
+ result = nil
480
617
  if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
481
618
  IRB.set_measure_callback
482
619
  end
483
620
 
484
621
  if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
485
622
  last_proc = proc do
486
- result = @workspace.evaluate(line, irb_path, line_no)
623
+ result = workspace.evaluate(code, @eval_path, line_no)
487
624
  end
488
625
  IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item|
489
626
  _name, callback, arg = item
490
627
  proc do
491
- callback.(self, line, line_no, arg) do
628
+ callback.(self, code, line_no, arg) do
492
629
  chain.call
493
630
  end
494
631
  end
495
632
  end.call
496
633
  else
497
- result = @workspace.evaluate(line, irb_path, line_no)
634
+ result = workspace.evaluate(code, @eval_path, line_no)
498
635
  end
499
-
500
- set_last_value(result)
636
+ result
501
637
  end
502
638
 
503
639
  def inspect_last_value # :nodoc:
504
640
  @inspect_method.inspect_value(@last_value)
505
641
  end
506
642
 
507
- alias __exit__ exit
508
- # Exits the current session, see IRB.irb_exit
509
- def exit(ret = 0)
510
- IRB.irb_exit(@irb, ret)
511
- rescue UncaughtThrowError
512
- super
513
- end
514
-
515
643
  NOPRINTING_IVARS = ["@last_value"] # :nodoc:
516
644
  NO_INSPECTING_IVARS = ["@irb", "@io"] # :nodoc:
517
645
  IDNAME_IVARS = ["@prompt_mode"] # :nodoc:
@@ -542,17 +670,5 @@ module IRB
542
670
  def local_variables # :nodoc:
543
671
  workspace.binding.local_variables
544
672
  end
545
-
546
- # Return true if it's aliased from the argument and it's not an identifier.
547
- def symbol_alias?(command)
548
- return nil if command.match?(/\A\w+\z/)
549
- command_aliases.key?(command.to_sym)
550
- end
551
-
552
- # Return true if the command supports transforming args
553
- def transform_args?(command)
554
- command = command_aliases.fetch(command.to_sym, command)
555
- ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
556
- end
557
673
  end
558
674
  end
@@ -0,0 +1,103 @@
1
+ require 'io/console/size'
2
+ require 'debug/console'
3
+
4
+ module IRB
5
+ module Debug
6
+ class UI < DEBUGGER__::UI_Base
7
+ def initialize(irb)
8
+ @irb = irb
9
+ end
10
+
11
+ def remote?
12
+ false
13
+ end
14
+
15
+ def activate session, on_fork: false
16
+ end
17
+
18
+ def deactivate
19
+ end
20
+
21
+ def width
22
+ if (w = IO.console_size[1]) == 0 # for tests PTY
23
+ 80
24
+ else
25
+ w
26
+ end
27
+ end
28
+
29
+ def quit n
30
+ yield
31
+ exit n
32
+ end
33
+
34
+ def ask prompt
35
+ setup_interrupt do
36
+ print prompt
37
+ ($stdin.gets || '').strip
38
+ end
39
+ end
40
+
41
+ def puts str = nil
42
+ case str
43
+ when Array
44
+ str.each{|line|
45
+ $stdout.puts line.chomp
46
+ }
47
+ when String
48
+ str.each_line{|line|
49
+ $stdout.puts line.chomp
50
+ }
51
+ when nil
52
+ $stdout.puts
53
+ end
54
+ end
55
+
56
+ def readline _
57
+ setup_interrupt do
58
+ tc = DEBUGGER__::SESSION.instance_variable_get(:@tc)
59
+ cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
60
+
61
+ case cmd
62
+ when nil # when user types C-d
63
+ "continue"
64
+ else
65
+ cmd
66
+ end
67
+ end
68
+ end
69
+
70
+ def setup_interrupt
71
+ DEBUGGER__::SESSION.intercept_trap_sigint false do
72
+ current_thread = Thread.current # should be session_server thread
73
+
74
+ prev_handler = trap(:INT){
75
+ current_thread.raise Interrupt
76
+ }
77
+
78
+ yield
79
+ ensure
80
+ trap(:INT, prev_handler)
81
+ end
82
+ end
83
+
84
+ def after_fork_parent
85
+ parent_pid = Process.pid
86
+
87
+ at_exit{
88
+ DEBUGGER__::SESSION.intercept_trap_sigint_end
89
+ trap(:SIGINT, :IGNORE)
90
+
91
+ if Process.pid == parent_pid
92
+ # only check child process from its parent
93
+ begin
94
+ # wait for all child processes to keep terminal
95
+ Process.waitpid
96
+ rescue Errno::ESRCH, Errno::ECHILD
97
+ end
98
+ end
99
+ }
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,34 +1,13 @@
1
- require_relative "nop"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module IRB
4
- # :stopdoc:
5
-
6
- module ExtendCommand
7
- class Debug < Nop
8
- category "Debugging"
9
- description "Start the debugger of debug.gem."
10
-
11
- BINDING_IRB_FRAME_REGEXPS = [
12
- '<internal:prelude>',
13
- binding.method(:irb).source_location.first,
14
- ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }
15
- IRB_DIR = File.expand_path('..', __dir__)
16
-
17
- def execute(pre_cmds: nil, do_cmds: nil)
18
- unless binding_irb?
19
- puts "`debug` command is only available when IRB is started with binding.irb"
20
- return
21
- end
22
-
23
- unless setup_debugger
24
- puts <<~MSG
25
- You need to install the debug gem before using this command.
26
- If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
27
- MSG
28
- return
29
- end
4
+ module Debug
5
+ IRB_DIR = File.expand_path('..', __dir__)
30
6
 
7
+ class << self
8
+ def insert_debug_break(pre_cmds: nil, do_cmds: nil)
31
9
  options = { oneshot: true, hook_call: false }
10
+
32
11
  if pre_cmds || do_cmds
33
12
  options[:command] = ['irb', pre_cmds, do_cmds]
34
13
  end
@@ -41,37 +20,29 @@ module IRB
41
20
  # a TracePoint on #debug_break.
42
21
  file, lineno = IRB::Irb.instance_method(:debug_break).source_location
43
22
  DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
44
- # exit current Irb#run call
45
- throw :IRB_EXIT
46
- end
47
-
48
- private
49
-
50
- def binding_irb?
51
- caller.any? do |frame|
52
- BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
53
- frame.match?(regexp)
54
- end
55
- end
56
- end
57
-
58
- module SkipPathHelperForIRB
59
- def skip_internal_path?(path)
60
- # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
61
- super || path.match?(IRB_DIR) || path.match?('<internal:prelude>')
62
- end
63
23
  end
64
24
 
65
- def setup_debugger
25
+ def setup(irb)
26
+ # When debug session is not started at all
66
27
  unless defined?(DEBUGGER__::SESSION)
67
28
  begin
68
29
  require "debug/session"
69
30
  rescue LoadError # debug.gem is not written in Gemfile
70
31
  return false unless load_bundled_debug_gem
71
32
  end
72
- DEBUGGER__.start(nonstop: true)
33
+ DEBUGGER__::CONFIG.set_config
34
+ configure_irb_for_debugger(irb)
35
+
36
+ DEBUGGER__.initialize_session{ IRB::Debug::UI.new(irb) }
37
+ end
38
+
39
+ # When debug session was previously started but not by IRB
40
+ if defined?(DEBUGGER__::SESSION) && !irb.context.with_debugger
41
+ configure_irb_for_debugger(irb)
42
+ DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(irb))
73
43
  end
74
44
 
45
+ # Apply patches to debug gem so it skips IRB frames
75
46
  unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
76
47
  DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
77
48
 
@@ -86,9 +57,43 @@ module IRB
86
57
  DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
87
58
  end
88
59
 
60
+ if !@output_modifier_defined && !DEBUGGER__::CONFIG[:no_hint]
61
+ irb_output_modifier_proc = Reline.output_modifier_proc
62
+
63
+ Reline.output_modifier_proc = proc do |output, complete:|
64
+ unless output.strip.empty?
65
+ cmd = output.split(/\s/, 2).first
66
+
67
+ if !complete && DEBUGGER__.commands.key?(cmd)
68
+ output = output.sub(/\n$/, " # debug command\n")
69
+ end
70
+ end
71
+
72
+ irb_output_modifier_proc.call(output, complete: complete)
73
+ end
74
+
75
+ @output_modifier_defined = true
76
+ end
77
+
89
78
  true
90
79
  end
91
80
 
81
+ private
82
+
83
+ def configure_irb_for_debugger(irb)
84
+ require 'irb/debug/ui'
85
+ IRB.instance_variable_set(:@debugger_irb, irb)
86
+ irb.context.with_debugger = true
87
+ irb.context.irb_name += ":rdbg"
88
+ end
89
+
90
+ module SkipPathHelperForIRB
91
+ def skip_internal_path?(path)
92
+ # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
93
+ super || path.match?(IRB_DIR) || path.match?('<internal:prelude>')
94
+ end
95
+ end
96
+
92
97
  # This is used when debug.gem is not written in Gemfile. Even if it's not
93
98
  # installed by `bundle install`, debug.gem is installed by default because
94
99
  # it's a bundled gem. This method tries to activate and load that.
@@ -121,16 +126,5 @@ module IRB
121
126
  end
122
127
  end
123
128
  end
124
-
125
- class DebugCommand < Debug
126
- def self.category
127
- "Debugging"
128
- end
129
-
130
- def self.description
131
- command_name = self.name.split("::").last.downcase
132
- "Start the debugger of debug.gem and run its `#{command_name}` command."
133
- end
134
- end
135
129
  end
136
130
  end