/
vdebug.rb
185 lines (154 loc) · 4.11 KB
/
vdebug.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
require 'securerandom'
class Vdebug
class BufferNotFound < StandardError; end;
attr_reader :vim, :watch_win_marker
def initialize(vim)
@lock_file = "../vdebug.lock"
@instance_id = SecureRandom::hex(3)
@vim = vim
@watch_win_marker = option_value("marker_default")
end
def start_listening
write_lock_file!
clear_buffer_cache!
if ENV["DEBUG"]
set_opt "debug_file_level", 2
set_opt "debug_file", "/tmp/vdebug.log"
end
set_opt "background_listener", 0
vim.server.remote_send ":python3 debugger.run()<CR>"
sleep 2
end
def set_opt(name, value)
vim.command "VdebugOpt #{name} #{value}"
end
def messages
vim.command("messages")
end
def last_error
vim.command("python3 debugger.get_last_error()")
end
def step_to_line(number)
vim.command "#{number}"
vim.command "python3 debugger.run_to_cursor()"
end
def step_over
vim.command 'python3 debugger.step_over()'
end
def step_in
vim.command 'python3 debugger.step_into()'
end
def trace(expression)
evaluate(expression, "VdebugTrace")
end
def evaluate(expression = "", command = "VdebugEval")
safe_expression = expression.gsub(/['"\\\x0]/,'\\\\\0')
vim.command "#{command} #{safe_expression}"
end
def evaluate!(expression)
evaluate(expression, "VdebugEval!")
end
# Retrieve a hash with the buffer names (values) and numbers (keys)
def buffers
@buffers ||= fetch_buffers
end
# Do this when you want to refresh the buffer list
def clear_buffer_cache!
@buffers = nil
end
# Has the vdebug GUI been opened?
def gui_open?
names = buffers.values
%w[DebuggerStack DebuggerStatus DebuggerWatch].all? { |b|
names.include? b
}
end
def running?
gui_open? && connected?
end
def connected?
status = vim.command(
"python3 print debugger.status()"
)
%w(break running).include? status
end
def watch_window_content
fetch_buffer_content 'DebuggerWatch'
end
def watch_vars
watch_lines = watch_window_content.split("\n")[4..-1]
Hash[watch_lines.join("").split('|').map { |v|
v.gsub(/^.*#{watch_win_marker}/, "").split("=", 2).map(&:strip)
}]
end
def stack_window_content
fetch_buffer_content 'DebuggerStack'
end
def trace_window_content
clear_buffer_cache!
fetch_buffer_content 'DebuggerTrace'
end
def stack
stack_window_content.split("\n").map { |l|
s = {}
matches = /^\[(\d+)\] (\S+) @ ([^:]+):(\d+)/.match(l)
if matches
s[:level] = matches[1]
s[:name] = matches[2]
s[:file] = matches[3]
s[:line] = matches[4]
s
else
raise "Invalid stack line: #{l}"
end
}
end
def status_window_content
fetch_buffer_content 'DebuggerStatus'
end
def status
/Status: (\S+)/.match(status_window_content)[1]
end
def remove_lock_file!
if File.exists?(@lock_file)
lock_file_id = File.read(@lock_file)
if lock_file_id == @instance_id
puts "Releasing lock (#{@instance_id})"
File.delete(@lock_file)
else
puts "Lock file #{@lock_file} is for instance #{lock_file_id}, whereas current instance is #{@instance_id}"
end
end
end
protected
def write_lock_file!
if File.exists?(@lock_file)
puts "Waiting for vdebug lock to be released"
i = 0
while File.exists?(@lock_file)
sleep 0.5
i += 1
raise "Failed to acquire vdebug lock" if i >= 20
end
end
puts "Acquiring vdebug lock (#{@instance_id})"
File.write(@lock_file, @instance_id)
end
def fetch_buffer_content(name)
bufnum = buffers.invert.fetch(name)
vim.echo(%Q{join(getbufline(#{bufnum}, 1, "$"), "\\n")})
rescue KeyError
raise BufferNotFound, "#{name} buffer not found"
end
def fetch_buffers
buffer_string = vim.command('buffers')
names = buffer_string.split("\n").collect do |bufline|
matches = /\A\s*(\d+).*"([^"]+)"/.match(bufline)
[matches[1].to_i, matches[2]] if matches
end
Hash[names.compact]
end
def option_value(name)
vim.echo("g:vdebug_options['#{name}']")
end
end