rudebug 0.3.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/COPYING +56 -0
- data/NEWS +6 -0
- data/README +31 -0
- data/Rakefile +112 -0
- data/bin/rudebug +4 -0
- data/lib/rudebug.glade +894 -0
- data/lib/rudebug.rb +13 -0
- data/lib/rudebug/connect.rb +203 -0
- data/lib/rudebug/connection.rb +3 -0
- data/lib/rudebug/connection/base.rb +109 -0
- data/lib/rudebug/connection/breakpoint.rb +109 -0
- data/lib/rudebug/connection/ruby-debug.rb +133 -0
- data/lib/rudebug/gtk-patch.rb +24 -0
- data/lib/rudebug/highlight.rb +129 -0
- data/lib/rudebug/main.rb +62 -0
- data/lib/rudebug/page.rb +75 -0
- data/lib/rudebug/page/browser.rb +291 -0
- data/lib/rudebug/page/code_file.rb +89 -0
- data/lib/rudebug/page/shell.rb +91 -0
- data/lib/rudebug/page/source_code.rb +54 -0
- data/setup.rb +1360 -0
- metadata +88 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# This files contains fixes and enhancements to the GTK library
|
2
|
+
|
3
|
+
class GladeXML # :nodoc:
|
4
|
+
# There's a bug in GladeXML: It tries to guard all elements that were loaded
|
5
|
+
# from a glade file against the GC by holding a reference so they won't go
|
6
|
+
# away. However, it can try to reference objects which weren't loaded when we
|
7
|
+
# supply a root argument. This ought to protect against that case. -- flgr
|
8
|
+
alias :guard_source_from_gc_without_nil_check :guard_source_from_gc
|
9
|
+
def guard_source_from_gc(source)
|
10
|
+
guard_source_from_gc_without_nil_check(source) unless source.nil?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Gtk::Container # :nodoc:
|
15
|
+
include Enumerable
|
16
|
+
end
|
17
|
+
|
18
|
+
class Gtk::ListStore # :nodoc:
|
19
|
+
include Enumerable
|
20
|
+
|
21
|
+
def size()
|
22
|
+
self.to_a().size()
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
begin
|
2
|
+
require 'syntax/convertors/abstract'
|
3
|
+
|
4
|
+
module Syntax
|
5
|
+
module Convertors
|
6
|
+
class GTKTextBuffer < Abstract
|
7
|
+
def self.color(red, green, blue)
|
8
|
+
Gdk::Color.new(red * 256, green * 256, blue * 256)
|
9
|
+
end
|
10
|
+
|
11
|
+
constant =
|
12
|
+
Styles = {
|
13
|
+
"number" => {
|
14
|
+
:foreground_gdk => color(0, 99, 0),
|
15
|
+
:background_gdk => color(222, 229, 157)
|
16
|
+
},
|
17
|
+
"string" => {
|
18
|
+
:priority => 0,
|
19
|
+
:foreground_gdk => color(153, 99, 112),
|
20
|
+
:background_gdk => color(255, 239, 216)
|
21
|
+
},
|
22
|
+
"regex" => {
|
23
|
+
:foreground_gdk => color(99, 153, 112),
|
24
|
+
:background_gdk => color(239, 255, 216)
|
25
|
+
},
|
26
|
+
"constant" => {
|
27
|
+
:foreground_gdk => color(58, 82, 120)
|
28
|
+
},
|
29
|
+
"method" => {
|
30
|
+
:foreground_gdk => color(127, 0, 127),
|
31
|
+
},
|
32
|
+
"attribute" => {
|
33
|
+
:foreground_gdk => color(153, 29, 170)
|
34
|
+
},
|
35
|
+
"symbol" => {
|
36
|
+
:priority => 0,
|
37
|
+
:foreground_gdk => color(152, 159, 154),
|
38
|
+
:background_gdk => color(243, 245, 244)
|
39
|
+
},
|
40
|
+
"keyword" => {
|
41
|
+
:foreground_gdk => color(16, 125, 202)
|
42
|
+
},
|
43
|
+
"escape" => {
|
44
|
+
:foreground_gdk => color(255, 255, 255),
|
45
|
+
:background_gdk => color(172, 115, 77)
|
46
|
+
},
|
47
|
+
"expr" => {
|
48
|
+
:foreground_gdk => color(188, 16, 2),
|
49
|
+
:background_gdk => color(255, 239, 216)
|
50
|
+
},
|
51
|
+
"punct" => {
|
52
|
+
:foreground_gdk => color(202, 0, 34)
|
53
|
+
},
|
54
|
+
"comment" => {
|
55
|
+
:foreground_gdk => color(153, 155, 154)
|
56
|
+
},
|
57
|
+
"ident" => {},
|
58
|
+
"expr" => {}
|
59
|
+
}
|
60
|
+
Styles["module"] = Styles["class"] = Styles["constant"]
|
61
|
+
Styles["global"] = Styles["punct"]
|
62
|
+
Styles["char"] = Styles["number"]
|
63
|
+
|
64
|
+
def style_for(tag)
|
65
|
+
Styles[tag.to_s]
|
66
|
+
end
|
67
|
+
|
68
|
+
def convert(code, buffer)
|
69
|
+
regions = []
|
70
|
+
region_tags = ["default"]
|
71
|
+
|
72
|
+
@tokenizer.tokenize(code) do |tok|
|
73
|
+
value = tok.to_s
|
74
|
+
group = tok.group.to_s
|
75
|
+
|
76
|
+
case tok.instruction
|
77
|
+
when :region_close then
|
78
|
+
region_tags.pop
|
79
|
+
|
80
|
+
when :region_open then
|
81
|
+
region_tags << group
|
82
|
+
end
|
83
|
+
|
84
|
+
unless value.empty?
|
85
|
+
tags = (region_tags + [group]) - ["normal"]
|
86
|
+
tags.uniq!
|
87
|
+
|
88
|
+
tags.reject! do |tag_name|
|
89
|
+
tag = buffer.tag_table.lookup(tag_name)
|
90
|
+
style = style_for(tag_name)
|
91
|
+
is_unknown = tag.nil? and style.nil?
|
92
|
+
|
93
|
+
if tag.nil? and !style.nil? then
|
94
|
+
priority = style.delete(:priority)
|
95
|
+
buffer.create_tag(tag_name, style)
|
96
|
+
if priority then
|
97
|
+
buffer.tag_table.lookup(tag_name).priority = priority
|
98
|
+
end
|
99
|
+
is_unknown = false
|
100
|
+
end
|
101
|
+
|
102
|
+
puts "Unknown tag %p" % tag_name if is_unknown
|
103
|
+
is_unknown
|
104
|
+
end
|
105
|
+
|
106
|
+
buffer.insert(buffer.end_iter, value, *tags.reverse)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.gtk_highlight(code, buffer)
|
114
|
+
@convertor ||= Syntax::Convertors::GTKTextBuffer.for_syntax "ruby"
|
115
|
+
@convertor.tokenizer.set(:expressions => :highlight)
|
116
|
+
@convertor.convert(code, buffer)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
rescue LoadError => error
|
120
|
+
module Syntax
|
121
|
+
module Convertors
|
122
|
+
class GTKTextBuffer
|
123
|
+
def self.gtk_highlight(code, buffer)
|
124
|
+
buffer.insert(buffer.end_iter, code)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/rudebug/main.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rudebug/gtk-patch'
|
2
|
+
|
3
|
+
class RuDebug
|
4
|
+
attr_reader :glade_file, :window, :notebook
|
5
|
+
|
6
|
+
def initialize(glade_file)
|
7
|
+
load_config()
|
8
|
+
|
9
|
+
@glade_file = glade_file
|
10
|
+
@glade = GladeXML.new(glade_file) do |handler|
|
11
|
+
method(handler) rescue lambda { |*args| p [handler, *args] }
|
12
|
+
end
|
13
|
+
|
14
|
+
@window = @glade["main-window"]
|
15
|
+
@notebook = @glade["session-notebook"]
|
16
|
+
@notebook.homogeneous = false
|
17
|
+
@notebook.remove_page(0)
|
18
|
+
@notebook.show_tabs = false
|
19
|
+
@pages = {}
|
20
|
+
|
21
|
+
initialize_connect()
|
22
|
+
|
23
|
+
@window.show
|
24
|
+
end
|
25
|
+
|
26
|
+
def config_path()
|
27
|
+
base_path = ENV["APPDATA"] || File.expand_path("~")
|
28
|
+
File.join(base_path, ".rudebug_conf")
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_config()
|
32
|
+
@config = begin
|
33
|
+
YAML.load(File.read(config_path)).to_hash
|
34
|
+
rescue Exception
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def save_config()
|
40
|
+
save_connect_servers()
|
41
|
+
|
42
|
+
File.open(config_path, "w") do |file|
|
43
|
+
file.puts(@config.to_yaml)
|
44
|
+
end
|
45
|
+
rescue Exception
|
46
|
+
# Ignored
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_main_window_destroy()
|
50
|
+
save_config()
|
51
|
+
Gtk.main_quit()
|
52
|
+
end
|
53
|
+
alias :on_quit_activate :on_main_window_destroy
|
54
|
+
end
|
55
|
+
|
56
|
+
require 'rudebug/page'
|
57
|
+
require 'rudebug/connect'
|
58
|
+
require 'rudebug/highlight'
|
59
|
+
|
60
|
+
Gtk.init
|
61
|
+
rudebug = RuDebug.new(File.join(DATA_PATH, "rudebug.glade"))
|
62
|
+
Gtk.main
|
data/lib/rudebug/page.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
class RuDebug::Page
|
2
|
+
attr_reader :session, :glade_file
|
3
|
+
|
4
|
+
def initialize(parent, session)
|
5
|
+
@parent = parent
|
6
|
+
@session = session
|
7
|
+
@glade_file = parent.glade_file
|
8
|
+
|
9
|
+
@glade = GladeXML.new(@glade_file, "session-hpaned") do |handler|
|
10
|
+
method(handler) rescue lambda { |*args| p [handler, *args] }
|
11
|
+
end
|
12
|
+
|
13
|
+
@page = @glade["session-hpaned"]
|
14
|
+
|
15
|
+
initialize_browser()
|
16
|
+
initialize_source_code()
|
17
|
+
initialize_shell()
|
18
|
+
|
19
|
+
load_browser()
|
20
|
+
load_current_code()
|
21
|
+
|
22
|
+
label = Gtk::Label.new(session.title)
|
23
|
+
@parent.notebook.prepend_page(@page, label)
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_default_tag(buffer, size = nil, more = {})
|
27
|
+
size ||= 10
|
28
|
+
options = { "font" => "Andale Mono %d" % size }.merge(more)
|
29
|
+
buffer.create_tag("default", options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def generic_step(method)
|
33
|
+
result = @session.__send__(method)
|
34
|
+
|
35
|
+
if result.nil? then
|
36
|
+
close()
|
37
|
+
else
|
38
|
+
update_location(*result)
|
39
|
+
load_browser()
|
40
|
+
title = result.join(":") # TODO: Query from server?
|
41
|
+
update_title(title)
|
42
|
+
end
|
43
|
+
|
44
|
+
return result
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_title(title)
|
48
|
+
@parent.notebook.set_tab_label_text(@page, title)
|
49
|
+
@parent.notebook.set_menu_label_text(@page, title)
|
50
|
+
@parent.pages[title] = self
|
51
|
+
end
|
52
|
+
|
53
|
+
def continue()
|
54
|
+
generic_step(:continue)
|
55
|
+
end
|
56
|
+
|
57
|
+
def step_into()
|
58
|
+
generic_step(:step_into)
|
59
|
+
end
|
60
|
+
|
61
|
+
def step_over()
|
62
|
+
generic_step(:step_over)
|
63
|
+
end
|
64
|
+
|
65
|
+
def close()
|
66
|
+
page_num = @parent.notebook.page_num(@page)
|
67
|
+
if page_num != -1 then
|
68
|
+
@parent.notebook.remove_page(page_num)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
require 'rudebug/page/shell'
|
74
|
+
require 'rudebug/page/source_code'
|
75
|
+
require 'rudebug/page/browser'
|
@@ -0,0 +1,291 @@
|
|
1
|
+
# This is all very complex because we can only send and receive strings via the
|
2
|
+
# network. (ruby-breakpoint can return DRbObject proxies which we could in
|
3
|
+
# theory work on directly, but that is not true for ruby-debug.)
|
4
|
+
class RuDebug::Page
|
5
|
+
def initialize_browser()
|
6
|
+
@browser_treeview = @glade["browser-treeview"]
|
7
|
+
@object_view = @glade["object-view"]
|
8
|
+
@object_buf = @object_view.buffer
|
9
|
+
|
10
|
+
@view_to_shell = @glade["view-to-shell-btn"]
|
11
|
+
|
12
|
+
@browser_store = Gtk::TreeStore.new(String, String, Integer, String, TrueClass)
|
13
|
+
@browser_treeview.model = @browser_store
|
14
|
+
@browser_treeview.rules_hint = true
|
15
|
+
renderer = Gtk::CellRendererText.new
|
16
|
+
|
17
|
+
create_default_tag(@object_buf, 10)
|
18
|
+
|
19
|
+
col = Gtk::TreeViewColumn.new("Attribute", renderer, :text => 0)
|
20
|
+
col.sizing = Gtk::TreeViewColumn::FIXED
|
21
|
+
col.reorderable = false
|
22
|
+
col.expand = true
|
23
|
+
col.resizable = true
|
24
|
+
@browser_treeview.append_column(col)
|
25
|
+
renderer = Gtk::CellRendererText.new()
|
26
|
+
renderer.size_points = 8
|
27
|
+
renderer.ellipsize = Pango::ELLIPSIZE_MIDDLE
|
28
|
+
col = Gtk::TreeViewColumn.new("Value", renderer, :text => 1)
|
29
|
+
col.sizing = Gtk::TreeViewColumn::FIXED
|
30
|
+
col.reorderable = false
|
31
|
+
col.expand = true
|
32
|
+
col.resizable = true
|
33
|
+
@browser_treeview.append_column(col)
|
34
|
+
renderer = Gtk::CellRendererText.new
|
35
|
+
col = Gtk::TreeViewColumn.new("OID", nil, :text => 2)
|
36
|
+
col.visible = false
|
37
|
+
@browser_treeview.append_column(col)
|
38
|
+
renderer = Gtk::CellRendererText.new
|
39
|
+
col = Gtk::TreeViewColumn.new("Class", renderer, :text => 3)
|
40
|
+
col.visible = false
|
41
|
+
@browser_treeview.append_column(col)
|
42
|
+
renderer = Gtk::CellRendererText.new
|
43
|
+
col = Gtk::TreeViewColumn.new("Virtual", renderer, :text => 4)
|
44
|
+
col.visible = false
|
45
|
+
@browser_treeview.append_column(col)
|
46
|
+
|
47
|
+
@browser_treeview.headers_visible = true
|
48
|
+
@browser_treeview.headers_clickable = false
|
49
|
+
@browser_treeview.enable_search = false
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_browser()
|
53
|
+
load_data(@browser_treeview)
|
54
|
+
end
|
55
|
+
|
56
|
+
def attribute_for(iter) # Attribute description
|
57
|
+
iter[0]
|
58
|
+
end
|
59
|
+
|
60
|
+
def value_for(iter) # Value string
|
61
|
+
iter[1]
|
62
|
+
end
|
63
|
+
|
64
|
+
def oid_for(iter) # Object id
|
65
|
+
iter[2]
|
66
|
+
end
|
67
|
+
|
68
|
+
def class_for(iter) # Class string
|
69
|
+
iter[3]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Virtual attributes are attributes that don't really exist that way.
|
73
|
+
# They are just information we poll for the user and should not get
|
74
|
+
# information like classes and so on.
|
75
|
+
def is_virtual?(iter) # Virtual attribute?
|
76
|
+
iter[4]
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_browser_treeview_cursor_changed(treeview)
|
80
|
+
path, column = *treeview.cursor # We don't care about selected column
|
81
|
+
iter = treeview.model.get_iter(path)
|
82
|
+
text = [
|
83
|
+
value_for(iter),
|
84
|
+
#class_for(iter)
|
85
|
+
].join("\n\n")
|
86
|
+
|
87
|
+
@object_buf.text = ""
|
88
|
+
highlight_inspect(text, @object_buf)
|
89
|
+
|
90
|
+
@session.eval("$rudebug.selected = %s" % pid_to_obj(oid_for(iter)))
|
91
|
+
@view_to_shell.sensitive = true
|
92
|
+
end
|
93
|
+
|
94
|
+
def on_view_to_shell_btn_clicked(button)
|
95
|
+
@shell_in.delete_selection
|
96
|
+
@shell_in.insert_at_cursor("$rudebug.selected")
|
97
|
+
@shell_in.grab_focus
|
98
|
+
sel_start, sel_end = *@shell_in.selection_bounds
|
99
|
+
@shell_in.select_region(sel_end, sel_end)
|
100
|
+
end
|
101
|
+
|
102
|
+
def root_code()
|
103
|
+
"[['context', false, #{eval_data("binding")}]]"
|
104
|
+
end
|
105
|
+
|
106
|
+
def dummy_attribute()
|
107
|
+
"DUMMY"
|
108
|
+
end
|
109
|
+
|
110
|
+
def on_browser_treeview_row_expanded(treeview, iter, path)
|
111
|
+
load_data(treeview, iter)
|
112
|
+
end
|
113
|
+
|
114
|
+
def pid_to_obj(pid)
|
115
|
+
"ObjectSpace._id2ref(%s)" % pid
|
116
|
+
end
|
117
|
+
|
118
|
+
# TODO: Refresh functionality?
|
119
|
+
|
120
|
+
def load_data(treeview, parent_iter = nil)
|
121
|
+
store = treeview.model
|
122
|
+
code = nil
|
123
|
+
|
124
|
+
if parent_iter.nil? then
|
125
|
+
treeview.model.clear
|
126
|
+
code = root_code() % []
|
127
|
+
else
|
128
|
+
dummy = parent_iter.first_child
|
129
|
+
if attribute_for(dummy) == dummy_attribute() then
|
130
|
+
# Remove dummy
|
131
|
+
store.remove(parent_iter.first_child)
|
132
|
+
|
133
|
+
parent_class = class_for(parent_iter)
|
134
|
+
pid = oid_for(parent_iter)
|
135
|
+
|
136
|
+
code_template = attributes_for(parent_class, is_virtual?(parent_iter))
|
137
|
+
code = code_template % pid_to_obj(pid)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
if code then
|
142
|
+
attributes = eval(@session.eval(code))
|
143
|
+
attributes.each do |(attribute, virtual, value, klass, oid)|
|
144
|
+
iter = store.append(parent_iter)
|
145
|
+
iter[0], iter[1], iter[2], iter[3], iter[4] =
|
146
|
+
attribute, value, oid, klass, virtual
|
147
|
+
|
148
|
+
if can_expand?(iter, klass, oid) then
|
149
|
+
dummy_iter = store.append(iter)
|
150
|
+
dummy_iter[0] = dummy_attribute()
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
if parent_iter then
|
155
|
+
treeview.collapse_row(parent_iter.path)
|
156
|
+
treeview.expand_row(parent_iter.path, false)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def eval_data(code, needs_ref = false)
|
162
|
+
ref = needs_ref ? "$rudebug.add_ref(#{code})" : code
|
163
|
+
"$rudebug.inspect_obj((#{code})), (#{code}).class.to_s, (#{ref}).object_id"
|
164
|
+
end
|
165
|
+
|
166
|
+
def attributes_for(klass, virtual)
|
167
|
+
code_parts = []
|
168
|
+
|
169
|
+
hash_code =
|
170
|
+
|
171
|
+
code_parts << if !virtual then %<
|
172
|
+
(%1$s.methods(false).empty?() ? [] : [
|
173
|
+
[ "eigenclass", false,
|
174
|
+
#{eval_data("class << %1$s; self; end")} ]]
|
175
|
+
) +
|
176
|
+
|
177
|
+
(%1$s.is_a?(Module) ? [] : [
|
178
|
+
[ "class", false,
|
179
|
+
#{eval_data("%1$s.class")} ]]
|
180
|
+
) +
|
181
|
+
|
182
|
+
(%1$s.instance_variables.empty?() ? [] : [
|
183
|
+
[ "ivars", true,
|
184
|
+
#{eval_data(%<
|
185
|
+
$rudebug.to_pairs(%1$s.instance_variables.sort) do |rudebug_ivar|
|
186
|
+
%1$s.instance_variable_get(rudebug_ivar)
|
187
|
+
end>, true)
|
188
|
+
}]
|
189
|
+
]) +
|
190
|
+
|
191
|
+
(!%1$s.respond_to?(:size) ? [] : [
|
192
|
+
[ "size", true,
|
193
|
+
#{eval_data("%1$s.size rescue 'error'")} ]]
|
194
|
+
)>
|
195
|
+
end
|
196
|
+
|
197
|
+
code_parts << if klass == "Binding" or klass == "Proc" then %<[
|
198
|
+
[ "location", true,
|
199
|
+
#{eval_data("eval('[__FILE__, __LINE__]', %1$s).join(':')", true)} ],
|
200
|
+
[ "self", false,
|
201
|
+
#{eval_data("eval('self', %1$s)")} ],
|
202
|
+
[ "lvars", true,
|
203
|
+
#{eval_data(%<
|
204
|
+
$rudebug.to_pairs(
|
205
|
+
(eval('local_variables', %1$s) +
|
206
|
+
(eval('$~.nil?', %1$s) ? [] : ['$~']) +
|
207
|
+
(eval('$_.nil?', %1$s) ? [] : ['$_']) -
|
208
|
+
['_', 'bp', 'client']
|
209
|
+
).sort
|
210
|
+
) do |rudebug_lvar|
|
211
|
+
eval(rudebug_lvar, %1$s)
|
212
|
+
end>, true)
|
213
|
+
}],
|
214
|
+
[ "caller", true,
|
215
|
+
#{eval_data(%<$rudebug.to_list(eval('caller', %1$s))>, true)}
|
216
|
+
]]>
|
217
|
+
elsif klass == "Array" then %<
|
218
|
+
(0 .. %1$s.size - 1).map do |rudebug_idx|
|
219
|
+
[ rudebug_idx.inspect, false,
|
220
|
+
#{eval_data("%1$s[rudebug_idx]")} ]
|
221
|
+
end>
|
222
|
+
elsif klass == "Hash" then %<
|
223
|
+
%1$s.map do |rudebug_key, rudebug_val|
|
224
|
+
[ rudebug_key.inspect, false,
|
225
|
+
#{eval_data("rudebug_val")} ]
|
226
|
+
end>
|
227
|
+
elsif klass == "$rudebug::Pairs" then %<
|
228
|
+
%1$s.map do |rudebug_key, rudebug_val|
|
229
|
+
[ rudebug_key.to_s, true,
|
230
|
+
#{eval_data("rudebug_val")} ]
|
231
|
+
end>
|
232
|
+
elsif klass == "Class" then %<
|
233
|
+
(%1$s == Object ? [] : [
|
234
|
+
[ "superclass", true,
|
235
|
+
#{eval_data("%1$s.superclass")} ]]
|
236
|
+
)>
|
237
|
+
elsif klass == "Object" then %<
|
238
|
+
(%1$s != ENV ? [] :
|
239
|
+
%1$s.map do |rudebug_key, rudebug_val|
|
240
|
+
[ rudebug_key.inspect, true,
|
241
|
+
#{eval_data("rudebug_val", true)} ]
|
242
|
+
end
|
243
|
+
)>
|
244
|
+
end
|
245
|
+
|
246
|
+
code_parts << if klass == "Class" or klass == "Module" then %<[
|
247
|
+
[ "modules", true,
|
248
|
+
#{eval_data("%1$s.included_modules", true)} ],
|
249
|
+
[ "constants", true,
|
250
|
+
#{eval_data(%<
|
251
|
+
$rudebug.to_pairs(%1$s.constants.sort) do |rudebug_const|
|
252
|
+
%1$s.const_get(rudebug_const)
|
253
|
+
end>, true)
|
254
|
+
}],
|
255
|
+
[ "imethods", true,
|
256
|
+
#{eval_data(%<
|
257
|
+
$rudebug.to_pairs(%1$s.instance_methods(false).sort) do |rudebug_imethod|
|
258
|
+
%1$s.instance_method(rudebug_imethod)
|
259
|
+
end>, true)
|
260
|
+
}]]>
|
261
|
+
end
|
262
|
+
|
263
|
+
code_parts.compact.map { |part| part.strip }.join(" + ")
|
264
|
+
end
|
265
|
+
|
266
|
+
def can_expand?(iter, klass, pid)
|
267
|
+
# No cyclic nodes
|
268
|
+
return false if is_cyclic?(iter)
|
269
|
+
code_template = attributes_for(klass, is_virtual?(iter))
|
270
|
+
# Short-circuit empty attributes (optimization)
|
271
|
+
return false if code_template == ""
|
272
|
+
#puts "template: %s" % code_template
|
273
|
+
code = code_template % pid_to_obj(pid)
|
274
|
+
#puts "code: %s" % code
|
275
|
+
result = @session.eval(code)
|
276
|
+
#p result
|
277
|
+
#p [code, result]
|
278
|
+
attributes = eval(result)
|
279
|
+
!attributes.empty?
|
280
|
+
end
|
281
|
+
|
282
|
+
def is_cyclic?(iter)
|
283
|
+
value = value_for(iter)
|
284
|
+
|
285
|
+
while iter = iter.parent
|
286
|
+
return true if value_for(iter) == value
|
287
|
+
end
|
288
|
+
|
289
|
+
return false
|
290
|
+
end
|
291
|
+
end
|