irb 1.7.1 → 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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