debugger-xml 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|