debug 1.4.0 → 1.5.0

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.
@@ -3,52 +3,51 @@
3
3
  require 'json'
4
4
  require 'irb/completion'
5
5
  require 'tmpdir'
6
- require 'json'
7
6
  require 'fileutils'
8
7
 
9
8
  module DEBUGGER__
10
9
  module UI_DAP
11
10
  SHOW_PROTOCOL = ENV['DEBUG_DAP_SHOW_PROTOCOL'] == '1' || ENV['RUBY_DEBUG_DAP_SHOW_PROTOCOL'] == '1'
12
11
 
13
- def self.setup sock_path
14
- dir = Dir.mktmpdir("ruby-debug-vscode-")
15
- at_exit{
12
+ def self.setup debug_port
13
+ if File.directory? '.vscode'
14
+ dir = Dir.pwd
15
+ else
16
+ dir = Dir.mktmpdir("ruby-debug-vscode-")
17
+ tempdir = true
18
+ end
19
+
20
+ at_exit do
16
21
  CONFIG[:skip_path] = [//] # skip all
17
- FileUtils.rm_rf dir
18
- }
22
+ FileUtils.rm_rf dir if tempdir
23
+ end
24
+
25
+ key = rand.to_s
26
+
19
27
  Dir.chdir(dir) do
20
- Dir.mkdir('.vscode')
21
- open('README.rb', 'w'){|f|
22
- f.puts <<~MSG
23
- # Wait for starting the attaching to the Ruby process
24
- # This file will be removed at the end of the debuggee process.
25
- #
26
- # Note that vscode-rdbg extension is needed. Please install if you don't have.
27
- MSG
28
- }
29
- open('.vscode/launch.json', 'w'){|f|
28
+ Dir.mkdir('.vscode') if tempdir
29
+
30
+ # vscode-rdbg 0.0.9 or later is needed
31
+ open('.vscode/rdbg_autoattach.json', 'w') do |f|
30
32
  f.puts JSON.pretty_generate({
31
- version: '0.2.0',
32
- configurations: [
33
- {
34
- type: "rdbg",
35
- name: "Attach with rdbg",
36
- request: "attach",
37
- rdbgPath: File.expand_path('../../exe/rdbg', __dir__),
38
- debugPort: sock_path,
39
- autoAttach: true,
40
- }
41
- ]
33
+ type: "rdbg",
34
+ name: "Attach with rdbg",
35
+ request: "attach",
36
+ rdbgPath: File.expand_path('../../exe/rdbg', __dir__),
37
+ debugPort: debug_port,
38
+ localfs: true,
39
+ autoAttach: key,
42
40
  })
43
- }
41
+ end
44
42
  end
45
43
 
46
- cmds = ['code', "#{dir}/", "#{dir}/README.rb"]
44
+ cmds = ['code', "#{dir}/"]
47
45
  cmdline = cmds.join(' ')
48
- ssh_cmdline = "code --remote ssh-remote+[SSH hostname] #{dir}/ #{dir}/README.rb"
46
+ ssh_cmdline = "code --remote ssh-remote+[SSH hostname] #{dir}/"
49
47
 
50
48
  STDERR.puts "Launching: #{cmdline}"
51
49
  env = ENV.delete_if{|k, h| /RUBY/ =~ k}.to_h
50
+ env['RUBY_DEBUG_AUTOATTACH'] = key
52
51
 
53
52
  unless system(env, *cmds)
54
53
  DEBUGGER__.warn <<~MESSAGE
@@ -71,9 +70,20 @@ module DEBUGGER__
71
70
  end
72
71
  end
73
72
 
73
+ @local_fs = false
74
+
75
+ def self.local_fs
76
+ @local_fs
77
+ end
78
+
79
+ def self.local_fs_set
80
+ @local_fs = true
81
+ end
82
+
74
83
  def dap_setup bytes
75
84
  CONFIG.set_config no_color: true
76
85
  @seq = 0
86
+ UI_DAP.local_fs_set if self.kind_of?(UI_UnixDomainServer)
77
87
 
78
88
  show_protocol :>, bytes
79
89
  req = JSON.load(bytes)
@@ -145,8 +155,8 @@ module DEBUGGER__
145
155
  def send **kw
146
156
  kw[:seq] = @seq += 1
147
157
  str = JSON.dump(kw)
148
- show_protocol '<', str
149
158
  @sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
159
+ show_protocol '<', str
150
160
  end
151
161
 
152
162
  def send_response req, success: true, message: nil, **kw
@@ -212,15 +222,18 @@ module DEBUGGER__
212
222
  when 'launch'
213
223
  send_response req
214
224
  @is_attach = false
225
+ UI_DAP.local_fs_set if req.dig('arguments', 'localfs')
215
226
  when 'attach'
216
227
  send_response req
217
- Process.kill(:SIGURG, Process.pid)
228
+ Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
218
229
  @is_attach = true
230
+ UI_DAP.local_fs_set if req.dig('arguments', 'localfs')
219
231
  when 'setBreakpoints'
220
232
  path = args.dig('source', 'path')
221
- bp_args = args['breakpoints']
233
+ SESSION.clear_line_breakpoints path
234
+
222
235
  bps = []
223
- bp_args.each{|bp|
236
+ args['breakpoints'].each{|bp|
224
237
  line = bp['line']
225
238
  if cond = bp['condition']
226
239
  bps << SESSION.add_line_breakpoint(path, line, cond: cond)
@@ -313,7 +326,7 @@ module DEBUGGER__
313
326
  exit
314
327
  when 'pause'
315
328
  send_response req
316
- Process.kill(:SIGURG, Process.pid)
329
+ Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
317
330
  when 'reverseContinue'
318
331
  send_response req,
319
332
  success: false, message: 'cancelled',
@@ -341,6 +354,8 @@ module DEBUGGER__
341
354
  raise "Unknown request: #{req.inspect}"
342
355
  end
343
356
  end
357
+ ensure
358
+ send_event :terminated unless @sock.closed?
344
359
  end
345
360
 
346
361
  ## called by the SESSION thread
@@ -496,7 +511,7 @@ module DEBUGGER__
496
511
  when 'source'
497
512
  ref = req.dig('arguments', 'sourceReference')
498
513
  if src = @src_map[ref]
499
- @ui.respond req, content: src.join
514
+ @ui.respond req, content: src.join("\n")
500
515
  else
501
516
  fail_response req, message: 'not found...'
502
517
  end
@@ -595,7 +610,11 @@ module DEBUGGER__
595
610
  event! :dap_result, :backtrace, req, {
596
611
  stackFrames: @target_frames.map{|frame|
597
612
  path = frame.realpath || frame.path
598
- ref = frame.file_lines unless path && File.exist?(path)
613
+ source_name = path ? File.basename(path) : frame.location.to_s
614
+
615
+ if !UI_DAP.local_fs || !(path && File.exist?(path))
616
+ ref = frame.file_lines
617
+ end
599
618
 
600
619
  {
601
620
  # id: ??? # filled by SESSION
@@ -603,7 +622,7 @@ module DEBUGGER__
603
622
  line: frame.location.lineno,
604
623
  column: 1,
605
624
  source: {
606
- name: File.basename(frame.path),
625
+ name: source_name,
607
626
  path: path,
608
627
  sourceReference: ref,
609
628
  },
@@ -612,7 +631,7 @@ module DEBUGGER__
612
631
  }
613
632
  when :scopes
614
633
  fid = args.shift
615
- frame = @target_frames[fid]
634
+ frame = get_frame(fid)
616
635
 
617
636
  lnum =
618
637
  if frame.binding
@@ -640,28 +659,12 @@ module DEBUGGER__
640
659
  }]
641
660
  when :scope
642
661
  fid = args.shift
643
- frame = @target_frames[fid]
644
- if b = frame.binding
645
- vars = b.local_variables.map{|name|
646
- v = b.local_variable_get(name)
647
- variable(name, v)
648
- }
649
- special_local_variables frame do |name, val|
650
- vars.unshift variable(name, val)
651
- end
652
- vars.unshift variable('%self', b.receiver)
653
- elsif lvars = frame.local_variables
654
- vars = lvars.map{|var, val|
655
- variable(var, val)
656
- }
657
- else
658
- vars = [variable('%self', frame.self)]
659
- special_local_variables frame do |name, val|
660
- vars.push variable(name, val)
661
- end
662
+ frame = get_frame(fid)
663
+ vars = collect_locals(frame).map do |var, val|
664
+ variable(var, val)
662
665
  end
663
- event! :dap_result, :scope, req, variables: vars, tid: self.id
664
666
 
667
+ event! :dap_result, :scope, req, variables: vars, tid: self.id
665
668
  when :variable
666
669
  vid = args.shift
667
670
  obj = @var_map[vid]
@@ -712,12 +715,11 @@ module DEBUGGER__
712
715
 
713
716
  when :evaluate
714
717
  fid, expr, context = args
715
- frame = @target_frames[fid]
718
+ frame = get_frame(fid)
716
719
  message = nil
717
720
 
718
- if frame && (b = frame.binding)
719
- b = b.dup
720
- special_local_variables current_frame do |name, var|
721
+ if frame && (b = frame.eval_binding)
722
+ special_local_variables frame do |name, var|
721
723
  b.local_variable_set(name, var) if /\%/ !~ name
722
724
  end
723
725
 
@@ -733,8 +735,7 @@ module DEBUGGER__
733
735
  case expr
734
736
  when /\A\@\S/
735
737
  begin
736
- (r = b.receiver).instance_variable_defined?(expr) or raise(NameError)
737
- result = r.instance_variable_get(expr)
738
+ result = b.receiver.instance_variable_get(expr)
738
739
  rescue NameError
739
740
  message = "Error: Not defined instance variable: #{expr.inspect}"
740
741
  end
@@ -745,14 +746,12 @@ module DEBUGGER__
745
746
  break false
746
747
  end
747
748
  } and (message = "Error: Not defined global variable: #{expr.inspect}")
748
- when /\A[A-Z]/
749
- unless result = search_const(b, expr)
749
+ when /(\A((::[A-Z]|[A-Z])\w*)+)/
750
+ unless result = search_const(b, $1)
750
751
  message = "Error: Not defined constants: #{expr.inspect}"
751
752
  end
752
753
  else
753
754
  begin
754
- # try to check local variables
755
- b.local_variable_defined?(expr) or raise NameError
756
755
  result = b.local_variable_get(expr)
757
756
  rescue NameError
758
757
  # try to check method
@@ -774,7 +773,7 @@ module DEBUGGER__
774
773
 
775
774
  when :completions
776
775
  fid, text = args
777
- frame = @target_frames[fid]
776
+ frame = get_frame(fid)
778
777
 
779
778
  if (b = frame&.binding) && word = text&.split(/[\s\{]/)&.last
780
779
  words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
@@ -788,10 +787,8 @@ module DEBUGGER__
788
787
  end
789
788
 
790
789
  begin
791
- if b&.local_variable_defined?(w)
792
- v = b.local_variable_get(w)
793
- phrase += " (variable:#{DEBUGGER__.safe_inspect(v)})"
794
- end
790
+ v = b.local_variable_get(w)
791
+ phrase += " (variable:#{DEBUGGER__.safe_inspect(v)})"
795
792
  rescue NameError
796
793
  end
797
794
 
@@ -807,7 +804,7 @@ module DEBUGGER__
807
804
  end
808
805
 
809
806
  def search_const b, expr
810
- cs = expr.split('::')
807
+ cs = expr.delete_prefix('::').split('::')
811
808
  [Object, *b.eval('Module.nesting')].reverse_each{|mod|
812
809
  if cs.all?{|c|
813
810
  if mod.const_defined?(c)