debugger-xml 0.0.4 → 0.1.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.
- data/CHANGELOG.md +7 -0
- data/Rakefile +1 -1
- data/bin/rdebug-vim +90 -0
- data/lib/debugger/xml/extensions/{debugger.rb → ide_server.rb} +0 -0
- data/lib/debugger/xml/extensions/vim_server.rb +47 -0
- data/lib/debugger/xml/ide/control_command_processor.rb +81 -0
- data/lib/debugger/xml/ide/interface.rb +67 -0
- data/lib/debugger/xml/ide/processor.rb +93 -0
- data/lib/debugger/xml/version.rb +1 -1
- data/lib/debugger/xml/vim/control_command_processor.rb +17 -0
- data/lib/debugger/xml/vim/interface.rb +39 -0
- data/lib/debugger/xml/vim/notification.rb +37 -0
- data/lib/debugger/xml/vim/processor.rb +21 -0
- data/test/{ide_control_command_processor_test.rb → ide/control_command_processor_test.rb} +5 -5
- data/test/{ide_processor_test.rb → ide/processor_test.rb} +4 -4
- data/test/printers/xml_test.rb +8 -1
- data/test/vim/control_command_processor_test.rb +28 -0
- data/test/vim/interface_test.rb +47 -0
- data/test/vim/notification_test.rb +34 -0
- data/test/vim/processor_test.rb +36 -0
- metadata +26 -9
- data/lib/debugger/xml/ide_processor.rb +0 -149
- data/lib/debugger/xml/interface.rb +0 -65
data/CHANGELOG.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
## 0.1.0
|
2
|
+
* Added rdebug-vim, used by vim-ruby-debugger. It is designed specifically for that vim plugin.
|
3
|
+
It creates new unix socket, and listen connections to it. After receiving commands/events, it
|
4
|
+
writes response to a file, and pokes Vim, so it can read it.
|
5
|
+
|
6
|
+
## 0.0.4
|
7
|
+
* First working version, copies ruby-debug-ide interface
|
data/Rakefile
CHANGED
data/bin/rdebug-vim
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
4
|
+
require 'optparse'
|
5
|
+
require 'ostruct'
|
6
|
+
require 'debugger'
|
7
|
+
require 'debugger/xml'
|
8
|
+
|
9
|
+
$stdout.sync = true
|
10
|
+
|
11
|
+
class RdebugVim
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
check_argv!
|
15
|
+
Debugger.const_set("RDEBUG_SCRIPT", File.expand_path($0))
|
16
|
+
install_interruption_hander
|
17
|
+
Debugger.tracing = false
|
18
|
+
Debugger.wait_for_start = true
|
19
|
+
Debugger.printer = Printers::Xml.new
|
20
|
+
Debugger.const_set("PROG_SCRIPT", ARGV.shift)
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
Debugger.start_for_vim(options)
|
25
|
+
bt = Debugger.debug_load(Debugger::PROG_SCRIPT, false, false)
|
26
|
+
if bt
|
27
|
+
print bt.backtrace.map{|l| "\t#{l}"}.join("\n"), "\n"
|
28
|
+
print "Uncaught exception: #{bt}\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def check_argv!
|
35
|
+
if ARGV.empty?
|
36
|
+
puts opts
|
37
|
+
puts
|
38
|
+
puts "Must specify a script to run"
|
39
|
+
exit(1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def install_interruption_hander
|
44
|
+
trap('INT') { Debugger.interrupt_last }
|
45
|
+
end
|
46
|
+
|
47
|
+
def options
|
48
|
+
opts
|
49
|
+
@options
|
50
|
+
end
|
51
|
+
|
52
|
+
def opts
|
53
|
+
@opts ||= begin
|
54
|
+
@options = OpenStruct.new
|
55
|
+
opts = OptionParser.new do |opts|
|
56
|
+
opts.banner = %{
|
57
|
+
Using rdebug-vim
|
58
|
+
Usage: rdebug-vim is supposed to be called from vim-ruby-debugger.
|
59
|
+
The command line interface to 'debugger' is rdebug.
|
60
|
+
}.gsub(/^\s*/, '')
|
61
|
+
opts.separator ""
|
62
|
+
opts.separator "Options:"
|
63
|
+
opts.on("-f", "--file FILE", String, "File for communication with Vim") { |file| @options.file = file }
|
64
|
+
opts.on("-o", "--output FILE", String, "File where standard/error output is sent to") do |file|
|
65
|
+
@options.output_file = file
|
66
|
+
end
|
67
|
+
opts.on("-s", "--socket FILE", String, "Socket file to communicate with debugger") do |file|
|
68
|
+
@options.socket = file
|
69
|
+
end
|
70
|
+
opts.on("-lf", "--logger_file FILE", String, "File for logging") { |file| @options.logger_file = file }
|
71
|
+
opts.on("-dm", "--debug_mode MODE", Integer, "Debug mode") { |mode| @options.debug_mode = mode == 1 }
|
72
|
+
opts.on("-ve", "--vim_executable FILE", String, "Vim executable file (e.g., 'mvim')") do |file|
|
73
|
+
@options.vim_executable = file
|
74
|
+
end
|
75
|
+
opts.on("-vs", "--vim_servername NAME", String, "Vim servername (e.g., 'VIM')") do |name|
|
76
|
+
@options.vim_servername = name
|
77
|
+
end
|
78
|
+
opts.on("-spr", "--separator NAME", String, "Output results separator") do |name|
|
79
|
+
@options.separator = name
|
80
|
+
end
|
81
|
+
opts.on("-I", "--include PATH", String, "Add PATH to $LOAD_PATH") { |path| $LOAD_PATH.unshift(path) }
|
82
|
+
end
|
83
|
+
opts.parse!
|
84
|
+
opts
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
RdebugVim.new.run
|
File without changes
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'ide_server'
|
2
|
+
|
3
|
+
module Debugger
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def start_for_vim(options)
|
7
|
+
return if @control_thread
|
8
|
+
daemonize!
|
9
|
+
$stdout.reopen(options.output_file, 'w')
|
10
|
+
$stderr.reopen(options.output_file, 'w')
|
11
|
+
@mutex = Mutex.new
|
12
|
+
@proceed = ConditionVariable.new
|
13
|
+
start
|
14
|
+
File.unlink(options.socket) if File.exist?(options.socket)
|
15
|
+
server = UNIXServer.new(options.socket)
|
16
|
+
Xml::Vim::Notification.new("establish_connection", options).send
|
17
|
+
@control_thread = DebugThread.new do
|
18
|
+
begin
|
19
|
+
while (session = server.accept)
|
20
|
+
interface = Xml::Vim::Interface.new(session, options)
|
21
|
+
processor = Xml::Vim::ControlCommandProcessor.new(interface)
|
22
|
+
self.handler = Xml::Vim::Processor.new(interface)
|
23
|
+
processor.process_commands
|
24
|
+
end
|
25
|
+
rescue Exception => e
|
26
|
+
puts "INTERNAL ERROR!!! #{$!}" rescue nil
|
27
|
+
puts $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
28
|
+
raise e
|
29
|
+
ensure
|
30
|
+
server.close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
@mutex.synchronize { @proceed.wait(@mutex) } if wait_for_start
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def daemonize!
|
39
|
+
pid = Process.fork
|
40
|
+
if pid
|
41
|
+
print pid
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Debugger
|
2
|
+
module Xml
|
3
|
+
module Ide
|
4
|
+
|
5
|
+
class ControlCommandProcessor < Debugger::Processor
|
6
|
+
|
7
|
+
def initialize(interface)
|
8
|
+
@interface = interface
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_commands
|
12
|
+
while input = @interface.read_command
|
13
|
+
process_input(input)
|
14
|
+
end
|
15
|
+
rescue IOError, Errno::EPIPE
|
16
|
+
rescue Exception
|
17
|
+
print "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
18
|
+
print $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
19
|
+
ensure
|
20
|
+
@interface.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_command(cmd)
|
24
|
+
catch(:debug_error) do
|
25
|
+
if matched_cmd = control_commands.find { |c| c.match(cmd) }
|
26
|
+
matched_cmd.execute
|
27
|
+
else
|
28
|
+
process_context_commands(cmd)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def control_commands
|
36
|
+
@control_commands = begin
|
37
|
+
control_command_classes = Command.commands.select(&:allow_in_control)
|
38
|
+
state = Debugger::ControlCommandProcessor::State.new(@interface, control_command_classes)
|
39
|
+
control_command_classes.map { |cmd| cmd.new(state) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_input(input)
|
44
|
+
split_commands(input).each do |cmd|
|
45
|
+
process_command(cmd)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_context_commands(input)
|
50
|
+
unless Debugger.handler.at_line?
|
51
|
+
errmsg pr("base.errors.no_suspended_thread", input: input)
|
52
|
+
return
|
53
|
+
end
|
54
|
+
event_command_classes = Command.commands.select(&:event)
|
55
|
+
state = Debugger::CommandProcessor::State.new do |s|
|
56
|
+
s.context = Debugger.handler.context
|
57
|
+
s.file = Debugger.handler.file
|
58
|
+
s.line = Debugger.handler.line
|
59
|
+
s.binding = Debugger.handler.context.frame_binding(0)
|
60
|
+
s.interface = @interface
|
61
|
+
s.commands = event_command_classes
|
62
|
+
end
|
63
|
+
event_commands = event_command_classes.map { |cls| cls.new(state) }
|
64
|
+
catch(:debug_error) do
|
65
|
+
if cmd = event_commands.find { |c| c.match(input) }
|
66
|
+
if state.context.dead? && cmd.class.need_context
|
67
|
+
print pr("base.errors.command_unavailable")
|
68
|
+
else
|
69
|
+
cmd.execute
|
70
|
+
end
|
71
|
+
else
|
72
|
+
print pr("base.errors.unknown_command", input: input)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
state.context.thread.run if state.proceed?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Debugger
|
2
|
+
module Xml
|
3
|
+
module Ide
|
4
|
+
class Interface < Debugger::Interface # :nodoc:
|
5
|
+
attr_accessor :command_queue
|
6
|
+
attr_accessor :histfile
|
7
|
+
attr_accessor :history_save
|
8
|
+
attr_accessor :history_length
|
9
|
+
attr_accessor :restart_file
|
10
|
+
|
11
|
+
def initialize(socket)
|
12
|
+
@command_queue = []
|
13
|
+
@socket = socket
|
14
|
+
@history_save = false
|
15
|
+
@history_length = 256
|
16
|
+
@histfile = ''
|
17
|
+
@restart_file = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def close
|
21
|
+
@socket.close
|
22
|
+
rescue Exception
|
23
|
+
end
|
24
|
+
|
25
|
+
def print_debug(msg)
|
26
|
+
STDOUT.puts(msg)
|
27
|
+
end
|
28
|
+
|
29
|
+
def errmsg(*args)
|
30
|
+
print(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def confirm(prompt)
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def finalize
|
38
|
+
close
|
39
|
+
end
|
40
|
+
|
41
|
+
# Workaround for JRuby issue http://jira.codehaus.org/browse/JRUBY-2063
|
42
|
+
def non_blocking_gets
|
43
|
+
loop do
|
44
|
+
result, _, _ = IO.select([@socket], nil, nil, 0.2)
|
45
|
+
next unless result
|
46
|
+
return result[0].gets
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def read_command(*args)
|
51
|
+
result = non_blocking_gets
|
52
|
+
raise IOError unless result
|
53
|
+
result.chomp
|
54
|
+
end
|
55
|
+
|
56
|
+
def readline_support?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def print(*args)
|
61
|
+
@socket.printf(*escape_input(args))
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'ruby-debug/processor'
|
2
|
+
|
3
|
+
module Debugger
|
4
|
+
module Xml
|
5
|
+
module Ide
|
6
|
+
|
7
|
+
class Processor < Debugger::Processor
|
8
|
+
attr_reader :context, :file, :line, :display
|
9
|
+
def initialize(interface)
|
10
|
+
@mutex = Mutex.new
|
11
|
+
@interface = interface
|
12
|
+
@display = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def at_breakpoint(context, breakpoint)
|
16
|
+
raise "@last_breakpoint supposed to be nil. is #{@last_breakpoint}" if @last_breakpoint
|
17
|
+
# at_breakpoint is immediately followed by #at_line event. So postpone breakpoint printing until #at_line.
|
18
|
+
@last_breakpoint = breakpoint
|
19
|
+
end
|
20
|
+
protect :at_breakpoint
|
21
|
+
|
22
|
+
# TODO: Catching exceptions doesn't work so far, need to fix
|
23
|
+
def at_catchpoint(context, excpt)
|
24
|
+
end
|
25
|
+
|
26
|
+
# We don't have tracing for IDE
|
27
|
+
def at_tracing(*args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def at_line(context, file, line)
|
31
|
+
if context.nil? || context.stop_reason == :step
|
32
|
+
print_file_line(context, file, line)
|
33
|
+
end
|
34
|
+
line_event(context, file, line)
|
35
|
+
end
|
36
|
+
protect :at_line
|
37
|
+
|
38
|
+
def at_return(context, file, line)
|
39
|
+
print_file_line(context, file, line)
|
40
|
+
context.stop_frame = -1
|
41
|
+
line_event(context, file, line)
|
42
|
+
end
|
43
|
+
|
44
|
+
def at_line?
|
45
|
+
!!@line
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def print_file_line(context, file, line)
|
51
|
+
print(
|
52
|
+
Debugger.printer.print(
|
53
|
+
"stop.suspend",
|
54
|
+
file: CommandProcessor.canonic_file(file), line_number: line, line: Debugger.line_at(file, line),
|
55
|
+
thnum: context && context.thnum, frames: context && context.stack_size
|
56
|
+
)
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def line_event(context, file, line)
|
61
|
+
@line = line
|
62
|
+
@file = file
|
63
|
+
@context = context
|
64
|
+
if @last_breakpoint
|
65
|
+
# followed after #at_breakpoint in the same thread. Print breakpoint
|
66
|
+
# now when @line, @file and @context are correctly set to prevent race
|
67
|
+
# condition with `control thread'.
|
68
|
+
n = Debugger.breakpoints.index(@last_breakpoint) + 1
|
69
|
+
print pr("breakpoints.stop_at_breakpoint",
|
70
|
+
id: n, file: @file, line: @line, thread_id: Debugger.current_context.thnum
|
71
|
+
)
|
72
|
+
end
|
73
|
+
if @context && @context.thread.is_a?(Debugger::DebugThread)
|
74
|
+
raise pr("thread.errors.debug_trace", thread: @context.thread)
|
75
|
+
end
|
76
|
+
# will be resumed by commands like `step', `next', `continue', `finish'
|
77
|
+
# from `control thread'
|
78
|
+
stop_thread
|
79
|
+
ensure
|
80
|
+
@line = nil
|
81
|
+
@file = nil
|
82
|
+
@context = nil
|
83
|
+
@last_breakpoint = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def stop_thread
|
87
|
+
Thread.stop
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/debugger/xml/version.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'debugger/xml/ide/interface'
|
2
|
+
|
3
|
+
module Debugger
|
4
|
+
module Xml
|
5
|
+
module Vim
|
6
|
+
class Interface < Ide::Interface
|
7
|
+
def initialize(socket, options)
|
8
|
+
super(socket)
|
9
|
+
@options = options
|
10
|
+
@output = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def print(*args)
|
14
|
+
@output << sprintf(*escape_input(args))
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_response
|
18
|
+
create_directory(@options.file)
|
19
|
+
message = @output.join(@options.separator)
|
20
|
+
@output.clear
|
21
|
+
unless message.empty?
|
22
|
+
File.open(@options.file, 'w') do |f|
|
23
|
+
f.puts(message)
|
24
|
+
end
|
25
|
+
Notification.new("receive_command", @options).send
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def create_directory(file)
|
32
|
+
dir = File.dirname(file)
|
33
|
+
Dir.mkdir(dir) unless File.exist?(dir) && File.directory?(dir)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Debugger
|
2
|
+
module Xml
|
3
|
+
module Vim
|
4
|
+
class Notification
|
5
|
+
|
6
|
+
def initialize(command, options)
|
7
|
+
@command = command
|
8
|
+
@executable = options.vim_executable
|
9
|
+
@servername = options.vim_servername
|
10
|
+
@debug_mode = options.debug_mode
|
11
|
+
@logger_file = options.logger_file
|
12
|
+
end
|
13
|
+
|
14
|
+
def send
|
15
|
+
command = ":call RubyDebugger.#{@command}()"
|
16
|
+
starter = "<C-\\\\>"
|
17
|
+
sys_cmd = "#{@executable} --servername #{@servername} -u NONE -U NONE " +
|
18
|
+
"--remote-send \"#{starter}<C-N>#{command}<CR>\""
|
19
|
+
log("Executing command: #{sys_cmd}")
|
20
|
+
system(sys_cmd);
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def log(string)
|
26
|
+
if @debug_mode
|
27
|
+
File.open(@logger_file, 'a') do |f|
|
28
|
+
# match vim redir style new lines, rather than trailing
|
29
|
+
f << "\ndebugger-xml, #{Time.now.strftime("%H:%M:%S")} : #{string.chomp}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'ruby-debug/processor'
|
2
|
+
|
3
|
+
module Debugger
|
4
|
+
module Xml
|
5
|
+
module Vim
|
6
|
+
|
7
|
+
class Processor < Ide::Processor
|
8
|
+
private
|
9
|
+
def stop_thread
|
10
|
+
processor = Vim::ControlCommandProcessor.new(@interface)
|
11
|
+
processor.process_command("where")
|
12
|
+
processor.process_command("var local")
|
13
|
+
@interface.send_response
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -1,15 +1,15 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
require 'debugger/xml/
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'debugger/xml/ide/control_command_processor'
|
3
3
|
|
4
|
-
describe Debugger::Xml::
|
4
|
+
describe Debugger::Xml::Ide::ControlCommandProcessor do
|
5
5
|
include TestDsl
|
6
6
|
|
7
|
-
let(:klass) { Debugger::Xml::
|
7
|
+
let(:klass) { Debugger::Xml::Ide::ControlCommandProcessor }
|
8
8
|
let(:interface) { Debugger.handler.interface }
|
9
9
|
let(:file) { fullpath('jump') }
|
10
10
|
let(:context) { stub(frame_binding: stub, stop_reason: nil, thread: stub, thnum: 1, stack_size: 2, dead?: false) }
|
11
11
|
subject { klass.new(interface) }
|
12
|
-
temporary_change_method_value(Debugger, :handler, Debugger::Xml::
|
12
|
+
temporary_change_method_value(Debugger, :handler, Debugger::Xml::Ide::Processor.new(TestInterface.new))
|
13
13
|
|
14
14
|
before do
|
15
15
|
Thread.stubs(:stop)
|
@@ -1,12 +1,12 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
require 'debugger/xml/
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'debugger/xml/ide/processor'
|
3
3
|
|
4
|
-
describe Debugger::Xml::
|
4
|
+
describe Debugger::Xml::Ide::Processor do
|
5
5
|
include TestDsl
|
6
6
|
|
7
7
|
before { Thread.stubs(:stop) }
|
8
8
|
|
9
|
-
let(:klass) { Debugger::Xml::
|
9
|
+
let(:klass) { Debugger::Xml::Ide::Processor }
|
10
10
|
let(:interface) { TestInterface.new }
|
11
11
|
let(:breakpoint) { stub }
|
12
12
|
let(:context) { stub(thread: nil, stop_reason: nil, thnum: 1, stack_size: 2) }
|
data/test/printers/xml_test.rb
CHANGED
@@ -41,9 +41,16 @@ describe "Printers::Xml" do
|
|
41
41
|
}
|
42
42
|
end
|
43
43
|
|
44
|
+
def yaml_file_path(filename)
|
45
|
+
File.expand_path(
|
46
|
+
File.join("..", "..", "..", "lib", "debugger", "printers", "texts", "#{filename}.yml"),
|
47
|
+
__FILE__
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
44
51
|
before do
|
45
52
|
YAML.stubs(:load_file).with(yaml_file_path('xml')).returns(yaml_xml)
|
46
|
-
YAML.stubs(:load_file).with(
|
53
|
+
YAML.stubs(:load_file).with(regexp_matches(/base/)).returns({})
|
47
54
|
end
|
48
55
|
|
49
56
|
describe "#print" do
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'debugger/xml/vim/control_command_processor'
|
3
|
+
|
4
|
+
describe Debugger::Xml::Vim::ControlCommandProcessor do
|
5
|
+
include TestDsl
|
6
|
+
|
7
|
+
let(:klass) { Debugger::Xml::Vim::ControlCommandProcessor }
|
8
|
+
let(:interface) { Debugger.handler.interface }
|
9
|
+
let(:file) { fullpath('jump') }
|
10
|
+
let(:context) { stub(frame_binding: stub, stop_reason: nil, thread: stub, thnum: 1, stack_size: 2, dead?: false) }
|
11
|
+
subject { klass.new(interface) }
|
12
|
+
temporary_change_method_value(Debugger, :handler, Debugger::Xml::Ide::Processor.new(TestInterface.new))
|
13
|
+
|
14
|
+
before do
|
15
|
+
Thread.stubs(:stop)
|
16
|
+
Debugger.handler.instance_variable_set("@context", context)
|
17
|
+
Debugger.handler.instance_variable_set("@file", file)
|
18
|
+
Debugger.handler.instance_variable_set("@line", 30)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "must send response after executing commands" do
|
22
|
+
Debugger::AddBreakpoint.any_instance.stubs(:execute).with()
|
23
|
+
Debugger::DeleteBreakpointCommand.any_instance.stubs(:execute).with()
|
24
|
+
interface.expects(:send_response)
|
25
|
+
enter 'break 5; delete 1'
|
26
|
+
subject.process_commands
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'debugger/xml/vim/interface'
|
3
|
+
|
4
|
+
describe Debugger::Xml::Vim::Interface do
|
5
|
+
include TestDsl
|
6
|
+
|
7
|
+
let(:klass) { Debugger::Xml::Vim::Interface }
|
8
|
+
let(:options) do
|
9
|
+
stub(debug_mode: false, file: filename, separator: "--sep--")
|
10
|
+
end
|
11
|
+
let(:socket) { stub }
|
12
|
+
let(:subject) { klass.new(socket, options) }
|
13
|
+
let(:filename) { File.expand_path("../tmp", __FILE__) }
|
14
|
+
let(:notification) { stub(send: nil) }
|
15
|
+
|
16
|
+
before do
|
17
|
+
File.open(filename, 'w') { |f| }
|
18
|
+
end
|
19
|
+
|
20
|
+
after do
|
21
|
+
File.unlink(filename)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "must send command to Vim" do
|
25
|
+
Debugger::Xml::Vim::Notification.expects(:new).with("receive_command", options).returns(notification)
|
26
|
+
subject.print("foo")
|
27
|
+
subject.print("bar")
|
28
|
+
subject.send_response
|
29
|
+
File.read(filename).strip.must_equal "foo--sep--bar"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "must clear the queue after sending response" do
|
33
|
+
Debugger::Xml::Vim::Notification.stubs(:new).with("receive_command", options).returns(notification)
|
34
|
+
subject.print("foo")
|
35
|
+
subject.print("bar")
|
36
|
+
subject.send_response
|
37
|
+
subject.print("bla")
|
38
|
+
subject.send_response
|
39
|
+
File.read(filename).strip.must_equal "bla"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "must not send any command if there is nothing to send" do
|
43
|
+
Debugger::Xml::Vim::Notification.expects(:new).never
|
44
|
+
subject.send_response
|
45
|
+
File.read(filename).strip.must_equal ""
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'debugger/xml/vim/notification'
|
3
|
+
|
4
|
+
describe Debugger::Xml::Vim::Notification do
|
5
|
+
include TestDsl
|
6
|
+
|
7
|
+
let(:klass) { Debugger::Xml::Vim::Notification }
|
8
|
+
let(:options) do
|
9
|
+
stub(vim_executable: "vim", vim_servername: "VIM", debug_mode: true, logger_file: filename)
|
10
|
+
end
|
11
|
+
let(:subject) { klass.new("foo", options) }
|
12
|
+
let(:filename) { File.expand_path("../tmp", __FILE__) }
|
13
|
+
let(:command) { %{vim --servername VIM -u NONE -U NONE --remote-send \"<C-\\\\><C-N>:call RubyDebugger.foo()<CR>\"} }
|
14
|
+
|
15
|
+
before do
|
16
|
+
File.open(filename, 'w') { |f| }
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
File.unlink(filename)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "must send command to Vim" do
|
24
|
+
subject.stubs(:log)
|
25
|
+
subject.expects(:system).with(command)
|
26
|
+
subject.send
|
27
|
+
end
|
28
|
+
|
29
|
+
it "must log to file" do
|
30
|
+
subject.stubs(:system).with(command)
|
31
|
+
subject.send
|
32
|
+
File.read(filename).must_match /Executing command: vim --servername/
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'debugger/xml/vim/processor'
|
3
|
+
|
4
|
+
describe Debugger::Xml::Vim::Processor do
|
5
|
+
include TestDsl
|
6
|
+
|
7
|
+
before { Thread.stubs(:stop) }
|
8
|
+
|
9
|
+
let(:klass) { Debugger::Xml::Vim::Processor }
|
10
|
+
let(:interface) { TestInterface.new }
|
11
|
+
let(:breakpoint) { stub }
|
12
|
+
let(:context) { stub(thread: nil, stop_reason: nil, thnum: 1, stack_size: 2) }
|
13
|
+
let(:file) { fullpath('jump') }
|
14
|
+
subject { klass.new(interface) }
|
15
|
+
|
16
|
+
describe "#at_line" do
|
17
|
+
it "must send response" do
|
18
|
+
processor = stub
|
19
|
+
processor.stubs(:process_command).with("where")
|
20
|
+
processor.stubs(:process_command).with("var local")
|
21
|
+
Debugger::Xml::Vim::ControlCommandProcessor.stubs(:new).with(interface).returns(processor)
|
22
|
+
interface.expects(:send_response)
|
23
|
+
subject.at_line(context, file, 30)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "must process additional commands" do
|
27
|
+
processor = stub
|
28
|
+
processor.expects(:process_command).with("where")
|
29
|
+
processor.expects(:process_command).with("var local")
|
30
|
+
Debugger::Xml::Vim::ControlCommandProcessor.expects(:new).with(interface).returns(processor)
|
31
|
+
interface.stubs(:send_response)
|
32
|
+
subject.at_line(context, file, 30)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: debugger-xml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debugger
|
@@ -96,15 +96,18 @@ email:
|
|
96
96
|
- anton.astashov@gmail.com
|
97
97
|
executables:
|
98
98
|
- rdebug-ide
|
99
|
+
- rdebug-vim
|
99
100
|
extensions: []
|
100
101
|
extra_rdoc_files: []
|
101
102
|
files:
|
102
103
|
- .gitignore
|
104
|
+
- CHANGELOG.md
|
103
105
|
- Gemfile
|
104
106
|
- LICENSE.txt
|
105
107
|
- README.md
|
106
108
|
- Rakefile
|
107
109
|
- bin/rdebug-ide
|
110
|
+
- bin/rdebug-vim
|
108
111
|
- debugger-xml.gemspec
|
109
112
|
- lib/debugger/printers/texts/xml.yml
|
110
113
|
- lib/debugger/printers/xml.rb
|
@@ -118,11 +121,17 @@ files:
|
|
118
121
|
- lib/debugger/xml/extensions/commands/tmate.rb
|
119
122
|
- lib/debugger/xml/extensions/commands/trace.rb
|
120
123
|
- lib/debugger/xml/extensions/commands/variables.rb
|
121
|
-
- lib/debugger/xml/extensions/
|
124
|
+
- lib/debugger/xml/extensions/ide_server.rb
|
122
125
|
- lib/debugger/xml/extensions/processor.rb
|
123
|
-
- lib/debugger/xml/
|
124
|
-
- lib/debugger/xml/
|
126
|
+
- lib/debugger/xml/extensions/vim_server.rb
|
127
|
+
- lib/debugger/xml/ide/control_command_processor.rb
|
128
|
+
- lib/debugger/xml/ide/interface.rb
|
129
|
+
- lib/debugger/xml/ide/processor.rb
|
125
130
|
- lib/debugger/xml/version.rb
|
131
|
+
- lib/debugger/xml/vim/control_command_processor.rb
|
132
|
+
- lib/debugger/xml/vim/interface.rb
|
133
|
+
- lib/debugger/xml/vim/notification.rb
|
134
|
+
- lib/debugger/xml/vim/processor.rb
|
126
135
|
- test/breakpoints_test.rb
|
127
136
|
- test/conditions_test.rb
|
128
137
|
- test/continue_test.rb
|
@@ -154,8 +163,8 @@ files:
|
|
154
163
|
- test/examples/variables_xml.rb
|
155
164
|
- test/frame_test.rb
|
156
165
|
- test/help_test.rb
|
157
|
-
- test/
|
158
|
-
- test/
|
166
|
+
- test/ide/control_command_processor_test.rb
|
167
|
+
- test/ide/processor_test.rb
|
159
168
|
- test/info_test.rb
|
160
169
|
- test/irb_test.rb
|
161
170
|
- test/jump_test.rb
|
@@ -171,6 +180,10 @@ files:
|
|
171
180
|
- test/tmate_test.rb
|
172
181
|
- test/trace_test.rb
|
173
182
|
- test/variables_test.rb
|
183
|
+
- test/vim/control_command_processor_test.rb
|
184
|
+
- test/vim/interface_test.rb
|
185
|
+
- test/vim/notification_test.rb
|
186
|
+
- test/vim/processor_test.rb
|
174
187
|
homepage: ''
|
175
188
|
licenses: []
|
176
189
|
post_install_message:
|
@@ -228,8 +241,8 @@ test_files:
|
|
228
241
|
- test/examples/variables_xml.rb
|
229
242
|
- test/frame_test.rb
|
230
243
|
- test/help_test.rb
|
231
|
-
- test/
|
232
|
-
- test/
|
244
|
+
- test/ide/control_command_processor_test.rb
|
245
|
+
- test/ide/processor_test.rb
|
233
246
|
- test/info_test.rb
|
234
247
|
- test/irb_test.rb
|
235
248
|
- test/jump_test.rb
|
@@ -245,3 +258,7 @@ test_files:
|
|
245
258
|
- test/tmate_test.rb
|
246
259
|
- test/trace_test.rb
|
247
260
|
- test/variables_test.rb
|
261
|
+
- test/vim/control_command_processor_test.rb
|
262
|
+
- test/vim/interface_test.rb
|
263
|
+
- test/vim/notification_test.rb
|
264
|
+
- test/vim/processor_test.rb
|
@@ -1,149 +0,0 @@
|
|
1
|
-
require 'ruby-debug/processor'
|
2
|
-
|
3
|
-
module Debugger
|
4
|
-
module Xml
|
5
|
-
|
6
|
-
class IdeProcessor < Processor
|
7
|
-
attr_reader :context, :file, :line, :display
|
8
|
-
def initialize(interface)
|
9
|
-
@mutex = Mutex.new
|
10
|
-
@interface = interface
|
11
|
-
@display = []
|
12
|
-
end
|
13
|
-
|
14
|
-
def at_breakpoint(context, breakpoint)
|
15
|
-
raise "@last_breakpoint supposed to be nil. is #{@last_breakpoint}" if @last_breakpoint
|
16
|
-
# at_breakpoint is immediately followed by #at_line event. So postpone breakpoint printing until #at_line.
|
17
|
-
@last_breakpoint = breakpoint
|
18
|
-
end
|
19
|
-
protect :at_breakpoint
|
20
|
-
|
21
|
-
# TODO: Catching exceptions doesn't work so far, need to fix
|
22
|
-
def at_catchpoint(context, excpt)
|
23
|
-
end
|
24
|
-
|
25
|
-
# We don't have tracing for IDE
|
26
|
-
def at_tracing(*args)
|
27
|
-
end
|
28
|
-
|
29
|
-
def at_line(context, file, line)
|
30
|
-
if context.nil? || context.stop_reason == :step
|
31
|
-
print_file_line(context, file, line)
|
32
|
-
end
|
33
|
-
line_event(context, file, line)
|
34
|
-
end
|
35
|
-
protect :at_line
|
36
|
-
|
37
|
-
def at_return(context, file, line)
|
38
|
-
print_file_line(context, file, line)
|
39
|
-
context.stop_frame = -1
|
40
|
-
line_event(context, file, line)
|
41
|
-
end
|
42
|
-
|
43
|
-
def at_line?
|
44
|
-
!!@line
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def print_file_line(context, file, line)
|
50
|
-
print(
|
51
|
-
Debugger.printer.print(
|
52
|
-
"stop.suspend",
|
53
|
-
file: CommandProcessor.canonic_file(file), line_number: line, line: Debugger.line_at(file, line),
|
54
|
-
thnum: context && context.thnum, frames: context && context.stack_size
|
55
|
-
)
|
56
|
-
)
|
57
|
-
end
|
58
|
-
|
59
|
-
def line_event(context, file, line)
|
60
|
-
@line = line
|
61
|
-
@file = file
|
62
|
-
@context = context
|
63
|
-
if @last_breakpoint
|
64
|
-
# followed after #at_breakpoint in the same thread. Print breakpoint
|
65
|
-
# now when @line, @file and @context are correctly set to prevent race
|
66
|
-
# condition with `control thread'.
|
67
|
-
n = Debugger.breakpoints.index(@last_breakpoint) + 1
|
68
|
-
print pr("breakpoints.stop_at_breakpoint",
|
69
|
-
id: n, file: @file, line: @line, thread_id: Debugger.current_context.thnum
|
70
|
-
)
|
71
|
-
end
|
72
|
-
if @context && @context.thread.is_a?(Debugger::DebugThread)
|
73
|
-
raise pr("thread.errors.debug_trace", thread: @context.thread)
|
74
|
-
end
|
75
|
-
# will be resumed by commands like `step', `next', `continue', `finish'
|
76
|
-
# from `control thread'
|
77
|
-
Thread.stop
|
78
|
-
ensure
|
79
|
-
@line = nil
|
80
|
-
@file = nil
|
81
|
-
@context = nil
|
82
|
-
@last_breakpoint = nil
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
class IdeControlCommandProcessor < Processor
|
87
|
-
|
88
|
-
def initialize(interface)
|
89
|
-
@interface = interface
|
90
|
-
end
|
91
|
-
|
92
|
-
def process_commands
|
93
|
-
control_command_classes = Command.commands.select(&:allow_in_control)
|
94
|
-
state = ControlCommandProcessor::State.new(@interface, control_command_classes)
|
95
|
-
control_commands = control_command_classes.map { |cmd| cmd.new(state) }
|
96
|
-
|
97
|
-
while input = @interface.read_command
|
98
|
-
split_commands(input).each do |cmd|
|
99
|
-
catch(:debug_error) do
|
100
|
-
if matched_cmd = control_commands.find { |c| c.match(cmd) }
|
101
|
-
matched_cmd.execute
|
102
|
-
else
|
103
|
-
process_context_commands(cmd)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
rescue IOError, Errno::EPIPE
|
109
|
-
rescue Exception
|
110
|
-
print "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
111
|
-
print $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
112
|
-
ensure
|
113
|
-
@interface.close
|
114
|
-
end
|
115
|
-
|
116
|
-
private
|
117
|
-
|
118
|
-
def process_context_commands(input)
|
119
|
-
unless Debugger.handler.at_line?
|
120
|
-
errmsg pr("base.errors.no_suspended_thread", input: input)
|
121
|
-
return
|
122
|
-
end
|
123
|
-
event_command_classes = Command.commands.select(&:event)
|
124
|
-
state = CommandProcessor::State.new do |s|
|
125
|
-
s.context = Debugger.handler.context
|
126
|
-
s.file = Debugger.handler.file
|
127
|
-
s.line = Debugger.handler.line
|
128
|
-
s.binding = Debugger.handler.context.frame_binding(0)
|
129
|
-
s.interface = @interface
|
130
|
-
s.commands = event_command_classes
|
131
|
-
end
|
132
|
-
event_commands = event_command_classes.map { |cls| cls.new(state) }
|
133
|
-
catch(:debug_error) do
|
134
|
-
if cmd = event_commands.find { |c| c.match(input) }
|
135
|
-
if state.context.dead? && cmd.class.need_context
|
136
|
-
print pr("base.errors.command_unavailable")
|
137
|
-
else
|
138
|
-
cmd.execute
|
139
|
-
end
|
140
|
-
else
|
141
|
-
print pr("base.errors.unknown_command", input: input)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
state.context.thread.run if state.proceed?
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
end
|
149
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
module Debugger
|
2
|
-
module Xml
|
3
|
-
class IdeInterface < Interface # :nodoc:
|
4
|
-
attr_accessor :command_queue
|
5
|
-
attr_accessor :histfile
|
6
|
-
attr_accessor :history_save
|
7
|
-
attr_accessor :history_length
|
8
|
-
attr_accessor :restart_file
|
9
|
-
|
10
|
-
def initialize(socket)
|
11
|
-
@command_queue = []
|
12
|
-
@socket = socket
|
13
|
-
@history_save = false
|
14
|
-
@history_length = 256
|
15
|
-
@histfile = ''
|
16
|
-
@restart_file = nil
|
17
|
-
end
|
18
|
-
|
19
|
-
def close
|
20
|
-
@socket.close
|
21
|
-
rescue Exception
|
22
|
-
end
|
23
|
-
|
24
|
-
def print_debug(msg)
|
25
|
-
STDOUT.puts(msg)
|
26
|
-
end
|
27
|
-
|
28
|
-
def errmsg(*args)
|
29
|
-
print(*args)
|
30
|
-
end
|
31
|
-
|
32
|
-
def confirm(prompt)
|
33
|
-
true
|
34
|
-
end
|
35
|
-
|
36
|
-
def finalize
|
37
|
-
close
|
38
|
-
end
|
39
|
-
|
40
|
-
# Workaround for JRuby issue http://jira.codehaus.org/browse/JRUBY-2063
|
41
|
-
def non_blocking_gets
|
42
|
-
loop do
|
43
|
-
result, _, _ = IO.select([@socket], nil, nil, 0.2)
|
44
|
-
next unless result
|
45
|
-
return result[0].gets
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def read_command(*args)
|
50
|
-
result = non_blocking_gets
|
51
|
-
raise IOError unless result
|
52
|
-
result.chomp
|
53
|
-
end
|
54
|
-
|
55
|
-
def readline_support?
|
56
|
-
false
|
57
|
-
end
|
58
|
-
|
59
|
-
def print(*args)
|
60
|
-
@socket.printf(*escape_input(args))
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|