ruby_jard 0.2.3 → 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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/documentation.yml +65 -0
- data/.github/workflows/{ruby.yml → rspec.yml} +22 -11
- data/.rubocop.yml +6 -0
- data/CHANGELOG.md +15 -3
- data/Gemfile +9 -2
- data/README.md +52 -332
- data/benchmark/path_filter_bench.rb +58 -0
- data/lib/ruby_jard.rb +15 -5
- data/lib/ruby_jard/color_schemes.rb +9 -1
- data/lib/ruby_jard/color_schemes/256_color_scheme.rb +21 -35
- data/lib/ruby_jard/color_schemes/256_light_color_scheme.rb +23 -35
- data/lib/ruby_jard/color_schemes/deep_space_color_scheme.rb +20 -34
- data/lib/ruby_jard/color_schemes/gruvbox_color_scheme.rb +20 -34
- data/lib/ruby_jard/color_schemes/one_half_dark_color_scheme.rb +20 -34
- data/lib/ruby_jard/color_schemes/one_half_light_color_scheme.rb +21 -34
- data/lib/ruby_jard/commands/color_helpers.rb +32 -0
- data/lib/ruby_jard/commands/frame_command.rb +2 -2
- data/lib/ruby_jard/commands/jard/color_scheme_command.rb +25 -3
- data/lib/ruby_jard/commands/jard/filter_command.rb +136 -0
- data/lib/ruby_jard/commands/jard/output_command.rb +13 -5
- data/lib/ruby_jard/commands/jard_command.rb +3 -1
- data/lib/ruby_jard/config.rb +9 -2
- data/lib/ruby_jard/decorators/array_decorator.rb +79 -0
- data/lib/ruby_jard/decorators/attributes_decorator.rb +172 -0
- data/lib/ruby_jard/decorators/color_decorator.rb +12 -5
- data/lib/ruby_jard/decorators/hash_decorator.rb +74 -0
- data/lib/ruby_jard/decorators/inspection_decorator.rb +91 -58
- data/lib/ruby_jard/decorators/object_decorator.rb +122 -0
- data/lib/ruby_jard/decorators/path_decorator.rb +55 -72
- data/lib/ruby_jard/decorators/rails_decorator.rb +194 -0
- data/lib/ruby_jard/decorators/string_decorator.rb +41 -0
- data/lib/ruby_jard/decorators/struct_decorator.rb +79 -0
- data/lib/ruby_jard/frame.rb +23 -10
- data/lib/ruby_jard/keys.rb +1 -0
- data/lib/ruby_jard/layouts/narrow_horizontal_layout.rb +4 -0
- data/lib/ruby_jard/layouts/tiny_layout.rb +4 -0
- data/lib/ruby_jard/pager.rb +21 -5
- data/lib/ruby_jard/path_classifier.rb +133 -0
- data/lib/ruby_jard/path_filter.rb +125 -0
- data/lib/ruby_jard/reflection.rb +97 -0
- data/lib/ruby_jard/repl_processor.rb +71 -38
- data/lib/ruby_jard/row_renderer.rb +5 -3
- data/lib/ruby_jard/screen.rb +2 -2
- data/lib/ruby_jard/screen_manager.rb +13 -36
- data/lib/ruby_jard/screen_renderer.rb +1 -1
- data/lib/ruby_jard/screens/backtrace_screen.rb +55 -39
- data/lib/ruby_jard/screens/menu_screen.rb +30 -30
- data/lib/ruby_jard/screens/source_screen.rb +46 -62
- data/lib/ruby_jard/screens/threads_screen.rb +59 -72
- data/lib/ruby_jard/screens/variables_screen.rb +168 -124
- data/lib/ruby_jard/session.rb +120 -16
- data/lib/ruby_jard/thread_info.rb +69 -0
- data/lib/ruby_jard/version.rb +1 -1
- data/ruby_jard.gemspec +3 -1
- metadata +20 -39
- data/.travis.yml +0 -6
- data/CNAME +0 -1
- data/_config.yml +0 -1
- data/docs/_config.yml +0 -8
- data/docs/color_schemes/256-light.png +0 -0
- data/docs/color_schemes/256.png +0 -0
- data/docs/color_schemes/deep-space.png +0 -0
- data/docs/color_schemes/gruvbox.png +0 -0
- data/docs/color_schemes/one-half-dark.png +0 -0
- data/docs/color_schemes/one-half-light.png +0 -0
- data/docs/demo.png +0 -0
- data/docs/guide-ui.png +0 -0
- data/docs/index.md +0 -238
- data/docs/logo.jpg +0 -0
- data/docs/screen-backtrace.png +0 -0
- data/docs/screen-repl.png +0 -0
- data/docs/screen-source.png +0 -0
- data/docs/screen-threads.png +0 -0
- data/docs/screen-variables.png +0 -0
- data/images/bg_hr.png +0 -0
- data/images/blacktocat.png +0 -0
- data/images/body-bg.jpg +0 -0
- data/images/download-button.png +0 -0
- data/images/github-button.png +0 -0
- data/images/header-bg.jpg +0 -0
- data/images/highlight-bg.jpg +0 -0
- data/images/icon_download.png +0 -0
- data/images/sidebar-bg.jpg +0 -0
- data/images/sprite_download.png +0 -0
- data/javascripts/main.js +0 -1
- data/stylesheets/github-light.css +0 -130
- data/stylesheets/normalize.css +0 -424
- data/stylesheets/print.css +0 -228
- data/stylesheets/stylesheet.css +0 -245
@@ -5,209 +5,239 @@ module RubyJard
|
|
5
5
|
##
|
6
6
|
# Display the relevant variables and constants of current context, scopes
|
7
7
|
class VariablesScreen < RubyJard::Screen
|
8
|
-
TYPE_SYMBOLS = {
|
9
|
-
# Intertal classes for those values may differ between Ruby versions
|
10
|
-
# For example: Bignum is renamed to Integer
|
11
|
-
# So, it's safer to use discrete value's class as the key for this mapping.
|
12
|
-
true.class => :bool,
|
13
|
-
false.class => :bool,
|
14
|
-
1.class => :int,
|
15
|
-
1.1.class => :flt,
|
16
|
-
1.to_r.class => :rat, # Rational: (1/1)
|
17
|
-
1.to_c.class => :com, # Complex: (1+0i)
|
18
|
-
''.class => :str,
|
19
|
-
:sym.class => :sym,
|
20
|
-
[].class => :arr,
|
21
|
-
{}.class => :hash,
|
22
|
-
//.class => :reg,
|
23
|
-
(0..0).class => :rng,
|
24
|
-
Class => :cls # Sorry, I lied, Class will never change
|
25
|
-
}.freeze
|
26
|
-
DEFAULT_TYPE_SYMBOL = :var
|
27
|
-
|
28
8
|
KINDS = [
|
9
|
+
KIND_SELF = :self,
|
29
10
|
KIND_LOC = :local_variable,
|
30
11
|
KIND_INS = :instance_variable,
|
31
12
|
KIND_CON = :constant,
|
32
|
-
|
13
|
+
KIND_GLOB = :global_variable
|
33
14
|
].freeze
|
34
15
|
|
35
16
|
KIND_STYLES = {
|
17
|
+
KIND_SELF => :constant,
|
36
18
|
KIND_LOC => :local_variable,
|
37
19
|
KIND_INS => :instance_variable,
|
38
20
|
KIND_CON => :constant,
|
39
|
-
|
21
|
+
KIND_GLOB => :instance_variable
|
40
22
|
}.freeze
|
41
23
|
|
42
24
|
KIND_PRIORITIES = {
|
43
25
|
KIND_SELF => 0,
|
44
|
-
KIND_LOC
|
45
|
-
KIND_INS
|
46
|
-
KIND_CON
|
26
|
+
KIND_LOC => 1,
|
27
|
+
KIND_INS => 2,
|
28
|
+
KIND_CON => 3,
|
29
|
+
KIND_GLOB => 4
|
47
30
|
}.freeze
|
48
31
|
|
49
|
-
|
50
|
-
KIND_LOC
|
51
|
-
KIND_INS
|
52
|
-
KIND_CON
|
32
|
+
TOKEN_KIND_MAPS = {
|
33
|
+
ident: KIND_LOC,
|
34
|
+
instance_variable: KIND_INS,
|
35
|
+
constant: KIND_CON,
|
36
|
+
predefined_constant: KIND_CON,
|
37
|
+
global_variable: KIND_GLOB
|
53
38
|
}.freeze
|
54
|
-
|
39
|
+
TOKEN_KINDS = TOKEN_KIND_MAPS.keys.flatten
|
40
|
+
|
41
|
+
def initialize(*args)
|
42
|
+
super
|
43
|
+
|
44
|
+
@frame_file = @session.current_frame&.frame_file
|
45
|
+
@frame_line = @session.current_frame&.frame_line
|
46
|
+
@frame_self = @session.current_frame&.frame_self
|
47
|
+
@frame_class = @session.current_frame&.frame_class
|
48
|
+
@frame_binding = @session.current_frame&.frame_binding
|
49
|
+
|
50
|
+
@inline_tokens = generate_inline_tokens(@frame_file, @frame_line)
|
51
|
+
@file_tokens = generate_file_tokens(@frame_file)
|
52
|
+
|
53
|
+
@inspection_decorator = RubyJard::Decorators::InspectionDecorator.new
|
54
|
+
|
55
|
+
@selected = 0
|
56
|
+
end
|
55
57
|
|
56
58
|
def title
|
57
59
|
'Variables'
|
58
60
|
end
|
59
61
|
|
60
62
|
def build
|
61
|
-
variables =
|
63
|
+
variables = fetch_relevant_variables
|
64
|
+
@rows = variables.map do |variable|
|
65
|
+
name = span_name(variable)
|
66
|
+
size = span_size(variable)
|
67
|
+
assignment = RubyJard::Span.new(margin_right: 1, margin_left: 1, content: '=', styles: :text_primary)
|
68
|
+
inline_limit =
|
69
|
+
(@layout.width - 3) * 3 - name.content_length - size.content_length - assignment.content_length
|
70
|
+
inspections = @inspection_decorator.decorate_multiline(
|
71
|
+
variable[2], first_line_limit: inline_limit, line_limit: @layout.width - 3, lines: 7
|
72
|
+
)
|
73
|
+
base_inspection = inspections.shift
|
74
|
+
mark = span_mark(variable, inspections)
|
75
|
+
[
|
76
|
+
base_row(name, size, assignment, mark, base_inspection),
|
77
|
+
nested_rows(variable, inspections)
|
78
|
+
]
|
79
|
+
end.flatten.compact
|
80
|
+
end
|
81
|
+
|
82
|
+
def fetch_relevant_variables
|
83
|
+
sort_variables(
|
62
84
|
self_variable +
|
63
85
|
fetch_local_variables +
|
64
86
|
fetch_instance_variables +
|
65
|
-
fetch_constants
|
66
|
-
|
67
|
-
|
87
|
+
fetch_constants +
|
88
|
+
fetch_global_variables
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
def base_row(name, size, assignment, mark, base_inspection)
|
93
|
+
RubyJard::Row.new(
|
94
|
+
line_limit: 3,
|
95
|
+
columns: [
|
96
|
+
RubyJard::Column.new(spans: [mark]),
|
97
|
+
RubyJard::Column.new(
|
98
|
+
word_wrap: RubyJard::Column::WORD_WRAP_BREAK_WORD,
|
99
|
+
spans: [name, size, assignment, base_inspection].flatten.compact
|
100
|
+
)
|
101
|
+
]
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def nested_rows(variable, nested_inspections)
|
106
|
+
return nil if nested_inspections.empty? || variable[0] == KIND_SELF
|
107
|
+
|
108
|
+
nested_inspections.map do |spans|
|
68
109
|
RubyJard::Row.new(
|
69
|
-
line_limit:
|
110
|
+
line_limit: 1,
|
70
111
|
columns: [
|
112
|
+
RubyJard::Column.new,
|
71
113
|
RubyJard::Column.new(
|
72
|
-
|
73
|
-
|
74
|
-
]
|
75
|
-
),
|
76
|
-
RubyJard::Column.new(
|
77
|
-
spans: [
|
78
|
-
span_name(variable),
|
79
|
-
span_size(variable),
|
80
|
-
RubyJard::Span.new(margin_right: 1, content: '=', styles: :variable_assignment),
|
81
|
-
span_inspection(variable)
|
82
|
-
]
|
114
|
+
word_wrap: RubyJard::Column::WORD_WRAP_BREAK_WORD,
|
115
|
+
spans: spans
|
83
116
|
)
|
84
117
|
]
|
85
118
|
)
|
86
119
|
end
|
87
|
-
@selected = 0
|
88
120
|
end
|
89
121
|
|
90
|
-
def
|
91
|
-
if
|
122
|
+
def span_mark(variable, nested_inspections)
|
123
|
+
if variable[0] == KIND_SELF || nested_inspections.empty?
|
92
124
|
RubyJard::Span.new(
|
93
|
-
content: '
|
94
|
-
styles: :
|
125
|
+
content: ' ',
|
126
|
+
styles: :text_dim
|
95
127
|
)
|
96
128
|
else
|
97
129
|
RubyJard::Span.new(
|
98
|
-
content: '
|
99
|
-
styles: :
|
130
|
+
content: '▾',
|
131
|
+
styles: :text_dim
|
100
132
|
)
|
101
133
|
end
|
102
134
|
end
|
103
135
|
|
104
136
|
def span_name(variable)
|
105
137
|
RubyJard::Span.new(
|
106
|
-
margin_right: 1,
|
107
138
|
content: variable[1].to_s,
|
108
|
-
styles: KIND_STYLES[variable[0].to_sym]
|
139
|
+
styles: [KIND_STYLES[variable[0].to_sym], inline?(variable[1]) ? :underline : nil]
|
109
140
|
)
|
110
141
|
end
|
111
142
|
|
112
143
|
def span_size(variable)
|
113
144
|
value = variable[2]
|
114
145
|
size_label =
|
115
|
-
if
|
146
|
+
if RubyJard::Reflection.call_is_a?(value, Array) && !value.empty?
|
116
147
|
"(len:#{value.length})"
|
117
|
-
elsif
|
148
|
+
elsif RubyJard::Reflection.call_is_a?(value, String) && value.length > 20
|
118
149
|
"(len:#{value.length})"
|
119
|
-
elsif
|
150
|
+
elsif RubyJard::Reflection.call_is_a?(value, Hash) && !value.empty?
|
120
151
|
"(size:#{value.length})"
|
121
152
|
end
|
122
153
|
RubyJard::Span.new(
|
123
|
-
|
154
|
+
margin_left: 1,
|
124
155
|
content: size_label,
|
125
|
-
styles: :
|
126
|
-
)
|
127
|
-
end
|
128
|
-
|
129
|
-
def span_inspection(variable)
|
130
|
-
inspection =
|
131
|
-
case variable[0]
|
132
|
-
when KIND_SELF
|
133
|
-
variable[2].to_s
|
134
|
-
else
|
135
|
-
variable[2].inspect
|
136
|
-
end
|
137
|
-
# Hard limit: screen area
|
138
|
-
inspection = inspection[0..@layout.height * @layout.width]
|
139
|
-
# TODO: This is just a workable. Should write a decorator to inspect objects accurately
|
140
|
-
["\n", "\r", "\r\n"].each do |esc|
|
141
|
-
inspection.gsub!(esc, esc.inspect)
|
142
|
-
end
|
143
|
-
RubyJard::Span.new(
|
144
|
-
content: inspection,
|
145
|
-
styles: :variable_inspection
|
146
|
-
)
|
147
|
-
rescue StandardError
|
148
|
-
RubyJard::Span.new(
|
149
|
-
content: '<Fail to inspect>',
|
150
|
-
styles: :variable_inspection
|
156
|
+
styles: :text_primary
|
151
157
|
)
|
152
158
|
end
|
153
159
|
|
154
160
|
private
|
155
161
|
|
156
162
|
def fetch_local_variables
|
157
|
-
|
163
|
+
return [] if @frame_binding == nil
|
164
|
+
return [] unless RubyJard::Reflection.call_is_a?(@frame_binding, ::Binding)
|
165
|
+
|
166
|
+
variables = @frame_binding.local_variables
|
158
167
|
# Exclude Pry's sticky locals
|
159
168
|
pry_sticky_locals =
|
160
169
|
if variables.include?(:pry_instance)
|
161
|
-
@
|
170
|
+
@frame_binding.local_variable_get(:pry_instance)&.sticky_locals&.keys || []
|
162
171
|
else
|
163
172
|
[]
|
164
173
|
end
|
165
174
|
variables -= pry_sticky_locals
|
166
175
|
variables.map do |variable|
|
167
|
-
[KIND_LOC, variable, @
|
176
|
+
[KIND_LOC, variable, @frame_binding.local_variable_get(variable)]
|
168
177
|
rescue NameError
|
169
178
|
nil
|
170
179
|
end.compact
|
171
180
|
end
|
172
181
|
|
173
182
|
def fetch_instance_variables
|
174
|
-
@
|
175
|
-
|
183
|
+
return [] if @frame_self == nil
|
184
|
+
|
185
|
+
instance_variables =
|
186
|
+
RubyJard::Reflection
|
187
|
+
.call_instance_variables(@frame_self)
|
188
|
+
.select { |v| relevant?(KIND_INS, v) }
|
189
|
+
|
190
|
+
instance_variables.map do |variable|
|
191
|
+
[KIND_INS, variable, RubyJard::Reflection.call_instance_variable_get(@frame_self, variable)]
|
176
192
|
rescue NameError
|
177
193
|
nil
|
178
194
|
end.compact
|
179
195
|
end
|
180
196
|
|
181
197
|
def fetch_constants
|
198
|
+
return [] if @frame_class == nil
|
199
|
+
|
182
200
|
# Filter out truly constants (CONSTANT convention) only
|
183
201
|
constant_source =
|
184
|
-
if @
|
185
|
-
@
|
202
|
+
if @frame_class&.singleton_class?
|
203
|
+
@frame_self
|
186
204
|
else
|
187
|
-
@
|
205
|
+
@frame_class
|
188
206
|
end
|
189
207
|
|
190
|
-
|
191
|
-
|
208
|
+
(@file_tokens[KIND_CON] || {})
|
209
|
+
.keys
|
210
|
+
.select { |c| c.upcase == c }
|
211
|
+
.uniq
|
212
|
+
.map { |const| fetch_constant(constant_source, const) }
|
213
|
+
.compact
|
214
|
+
end
|
215
|
+
|
216
|
+
def fetch_constant(constant_source, const)
|
217
|
+
return nil if %w[NIL TRUE FALSE].include?(const.to_s)
|
218
|
+
return nil unless RubyJard::Reflection.call_const_defined?(constant_source, const)
|
219
|
+
|
220
|
+
[KIND_CON, const, RubyJard::Reflection.call_const_get(constant_source, const)]
|
221
|
+
rescue NameError
|
222
|
+
nil
|
223
|
+
end
|
192
224
|
|
193
|
-
|
194
|
-
|
195
|
-
next if %w[NIL TRUE FALSE].include?(variable.to_s)
|
225
|
+
def fetch_global_variables
|
226
|
+
return [] if @frame_self == nil
|
196
227
|
|
197
|
-
|
228
|
+
variables =
|
229
|
+
::Kernel
|
230
|
+
.global_variables
|
231
|
+
.select { |v| relevant?(KIND_GLOB, v) }
|
232
|
+
variables.map do |variable|
|
233
|
+
[KIND_GLOB, variable, ::Kernel.instance_eval(variable.to_s)]
|
198
234
|
rescue NameError
|
199
235
|
nil
|
200
236
|
end.compact
|
201
237
|
end
|
202
238
|
|
203
|
-
def toplevel_binding?
|
204
|
-
@session.current_frame.frame_self == TOPLEVEL_BINDING.receiver
|
205
|
-
rescue StandardError
|
206
|
-
false
|
207
|
-
end
|
208
|
-
|
209
239
|
def self_variable
|
210
|
-
[[KIND_SELF, :self, @
|
240
|
+
[[KIND_SELF, :self, @frame_self]]
|
211
241
|
rescue StandardError
|
212
242
|
[]
|
213
243
|
end
|
@@ -235,35 +265,49 @@ module RubyJard
|
|
235
265
|
end
|
236
266
|
end
|
237
267
|
|
238
|
-
def inline?(
|
239
|
-
|
240
|
-
|
268
|
+
def inline?(name)
|
269
|
+
@inline_tokens[name]
|
270
|
+
end
|
271
|
+
|
272
|
+
def relevant?(kind, name)
|
273
|
+
@file_tokens[kind] != nil && @file_tokens[kind][name]
|
241
274
|
end
|
242
275
|
|
243
|
-
def
|
244
|
-
return
|
276
|
+
def generate_inline_tokens(file, line)
|
277
|
+
return [] if file == nil || line == nil
|
245
278
|
|
246
|
-
|
247
|
-
|
248
|
-
source_decorator = RubyJard::Decorators::SourceDecorator.new(current_file, current_line, 1)
|
279
|
+
loc_decorator = RubyJard::Decorators::LocDecorator.new
|
280
|
+
source_decorator = RubyJard::Decorators::SourceDecorator.new(file, line, 1)
|
249
281
|
_spans, tokens = loc_decorator.decorate(
|
250
|
-
source_decorator.codes[
|
251
|
-
|
282
|
+
source_decorator.codes[line - source_decorator.window_start],
|
283
|
+
file
|
252
284
|
)
|
253
285
|
|
254
|
-
|
286
|
+
inline_tokens = {}
|
255
287
|
tokens.each_slice(2) do |token, kind|
|
256
|
-
next
|
288
|
+
next if TOKEN_KIND_MAPS[kind] == nil
|
257
289
|
|
258
|
-
|
259
|
-
@inline_tokens[kind] << token.to_s.to_sym
|
290
|
+
inline_tokens[token.to_s.to_sym] = true
|
260
291
|
end
|
261
|
-
|
262
|
-
@inline_tokens
|
292
|
+
inline_tokens
|
263
293
|
end
|
264
294
|
|
265
|
-
def
|
266
|
-
|
295
|
+
def generate_file_tokens(file)
|
296
|
+
return [] if file == nil
|
297
|
+
|
298
|
+
loc_decorator = RubyJard::Decorators::LocDecorator.new
|
299
|
+
# TODO: This is a mess
|
300
|
+
source_decorator = RubyJard::Decorators::SourceDecorator.new(file, 1, 10_000)
|
301
|
+
_spans, tokens = loc_decorator.decorate(source_decorator.codes.join("\n"), file)
|
302
|
+
|
303
|
+
file_tokens = {}
|
304
|
+
tokens.each_slice(2) do |token, kind|
|
305
|
+
next if TOKEN_KIND_MAPS[kind] == nil
|
306
|
+
|
307
|
+
file_tokens[TOKEN_KIND_MAPS[kind]] ||= {}
|
308
|
+
file_tokens[TOKEN_KIND_MAPS[kind]][token.to_s.to_sym] = true
|
309
|
+
end
|
310
|
+
file_tokens
|
267
311
|
end
|
268
312
|
end
|
269
313
|
end
|
data/lib/ruby_jard/session.rb
CHANGED
@@ -4,23 +4,42 @@ module RubyJard
|
|
4
4
|
##
|
5
5
|
# Centralized flow control and data storage to feed into screens. Each
|
6
6
|
# process supposes to have only one instance of this class.
|
7
|
-
# TODO: If attachment event happens after any threads are created, race
|
8
|
-
# condition may happen. Should use a mutex to wrap around.
|
9
7
|
# TODO: This class is created to store data, but byebug data structures are
|
10
8
|
# leaked, and accessible from outside and this doesn't work if screens stay in
|
11
9
|
# other processes. Therefore, an internal, jard-specific data mapping should
|
12
10
|
# be built.
|
13
11
|
class Session
|
14
|
-
|
12
|
+
class << self
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
def_delegators :instance,
|
16
|
+
:attach, :lock,
|
17
|
+
:sync, :should_stop?,
|
18
|
+
:step_over, :step_into, :frame=,
|
19
|
+
:threads, :current_frame, :current_thread, :current_backtrace,
|
20
|
+
:output_buffer, :append_output_buffer
|
21
|
+
|
22
|
+
def instance
|
23
|
+
@instance ||= new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
OUTPUT_BUFFER_LENGTH = 10_000 # 10k lines
|
28
|
+
|
29
|
+
attr_accessor :output_buffer
|
15
30
|
|
16
31
|
def initialize(options = {})
|
17
32
|
@options = options
|
18
33
|
@started = false
|
19
34
|
@session_lock = Mutex.new
|
35
|
+
@output_buffer = []
|
20
36
|
|
21
37
|
@current_frame = nil
|
22
38
|
@current_backtrace = []
|
23
39
|
@threads = []
|
40
|
+
@current_thread = nil
|
41
|
+
|
42
|
+
@path_filter = RubyJard::PathFilter.new
|
24
43
|
end
|
25
44
|
|
26
45
|
def start
|
@@ -42,9 +61,39 @@ module RubyJard
|
|
42
61
|
'*.rb'
|
43
62
|
)
|
44
63
|
)
|
64
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
65
|
+
def $stdout.write(*string, from_jard: false)
|
66
|
+
# NOTE: `RubyJard::ScreenManager.instance` is a must. Jard doesn't work well with delegator
|
67
|
+
# TODO: Debug and fix the issues permanently
|
68
|
+
if from_jard
|
69
|
+
super(*string)
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
unless RubyJard::ScreenManager.instance.updating?
|
74
|
+
RubyJard::Session.instance.append_output_buffer(string)
|
75
|
+
end
|
76
|
+
|
77
|
+
super(*string)
|
78
|
+
end
|
79
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
80
|
+
|
81
|
+
at_exit { stop }
|
82
|
+
|
45
83
|
@started = true
|
46
84
|
end
|
47
85
|
|
86
|
+
def append_output_buffer(string)
|
87
|
+
@output_buffer.shift if @output_buffer.length > OUTPUT_BUFFER_LENGTH
|
88
|
+
@output_buffer << string
|
89
|
+
end
|
90
|
+
|
91
|
+
def stop
|
92
|
+
return unless started?
|
93
|
+
|
94
|
+
RubyJard::ScreenManager.stop
|
95
|
+
end
|
96
|
+
|
48
97
|
def started?
|
49
98
|
@started == true
|
50
99
|
end
|
@@ -56,22 +105,58 @@ module RubyJard
|
|
56
105
|
Byebug.current_context.step_out(3, true)
|
57
106
|
end
|
58
107
|
|
59
|
-
def
|
60
|
-
current_context
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
108
|
+
def should_stop?
|
109
|
+
@path_filter.match?(@current_context.frame_file)
|
110
|
+
end
|
111
|
+
|
112
|
+
def sync(context)
|
113
|
+
@current_context = context
|
114
|
+
# Remove cache
|
115
|
+
@current_frame = nil
|
116
|
+
@current_thread = nil
|
117
|
+
@current_backtrace = nil
|
118
|
+
@threads = nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def current_frame
|
122
|
+
@current_frame ||=
|
123
|
+
begin
|
124
|
+
frame = RubyJard::Frame.new(@current_context, @current_context.frame.pos)
|
125
|
+
frame.visible = @path_filter.match?(frame.frame_file)
|
126
|
+
frame
|
72
127
|
end
|
73
128
|
end
|
74
129
|
|
130
|
+
def current_thread
|
131
|
+
@current_thread ||= RubyJard::ThreadInfo.new(@current_context.thread)
|
132
|
+
end
|
133
|
+
|
134
|
+
def current_backtrace
|
135
|
+
@current_backtrace ||= generate_backtrace
|
136
|
+
end
|
137
|
+
|
138
|
+
def threads
|
139
|
+
@threads ||=
|
140
|
+
Thread
|
141
|
+
.list
|
142
|
+
.select(&:alive?)
|
143
|
+
.reject { |t| t.name.to_s =~ /<<Jard:.*>>/ }
|
144
|
+
.map { |t| RubyJard::ThreadInfo.new(t) }
|
145
|
+
end
|
146
|
+
|
147
|
+
def frame=(real_pos)
|
148
|
+
@current_context.frame = @current_backtrace[real_pos].real_pos
|
149
|
+
@current_frame = @current_backtrace[real_pos]
|
150
|
+
end
|
151
|
+
|
152
|
+
def step_into(times)
|
153
|
+
@current_context.step_into(times, current_frame.real_pos)
|
154
|
+
end
|
155
|
+
|
156
|
+
def step_over(times)
|
157
|
+
@current_context.step_over(times, current_frame.real_pos)
|
158
|
+
end
|
159
|
+
|
75
160
|
def lock
|
76
161
|
raise RubyJard::Error, 'This method requires a block' unless block_given?
|
77
162
|
|
@@ -81,5 +166,24 @@ module RubyJard
|
|
81
166
|
yield
|
82
167
|
end
|
83
168
|
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def generate_backtrace
|
173
|
+
virtual_pos = 0
|
174
|
+
backtrace = @current_context.backtrace.map.with_index do |_frame, index|
|
175
|
+
frame = RubyJard::Frame.new(@current_context, index)
|
176
|
+
if @path_filter.match?(frame.frame_file)
|
177
|
+
frame.visible = true
|
178
|
+
frame.virtual_pos = virtual_pos
|
179
|
+
virtual_pos += 1
|
180
|
+
else
|
181
|
+
frame.visible = false
|
182
|
+
end
|
183
|
+
frame
|
184
|
+
end
|
185
|
+
current_frame.virtual_pos = backtrace[current_frame.real_pos].virtual_pos
|
186
|
+
backtrace
|
187
|
+
end
|
84
188
|
end
|
85
189
|
end
|