ruby_jard 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/documentation.yml +65 -0
  4. data/.github/workflows/{ruby.yml → rspec.yml} +22 -11
  5. data/.rubocop.yml +6 -0
  6. data/CHANGELOG.md +15 -3
  7. data/Gemfile +9 -2
  8. data/README.md +52 -332
  9. data/benchmark/path_filter_bench.rb +58 -0
  10. data/lib/ruby_jard.rb +15 -5
  11. data/lib/ruby_jard/color_schemes.rb +9 -1
  12. data/lib/ruby_jard/color_schemes/256_color_scheme.rb +21 -35
  13. data/lib/ruby_jard/color_schemes/256_light_color_scheme.rb +23 -35
  14. data/lib/ruby_jard/color_schemes/deep_space_color_scheme.rb +20 -34
  15. data/lib/ruby_jard/color_schemes/gruvbox_color_scheme.rb +20 -34
  16. data/lib/ruby_jard/color_schemes/one_half_dark_color_scheme.rb +20 -34
  17. data/lib/ruby_jard/color_schemes/one_half_light_color_scheme.rb +21 -34
  18. data/lib/ruby_jard/commands/color_helpers.rb +32 -0
  19. data/lib/ruby_jard/commands/frame_command.rb +2 -2
  20. data/lib/ruby_jard/commands/jard/color_scheme_command.rb +25 -3
  21. data/lib/ruby_jard/commands/jard/filter_command.rb +136 -0
  22. data/lib/ruby_jard/commands/jard/output_command.rb +13 -5
  23. data/lib/ruby_jard/commands/jard_command.rb +3 -1
  24. data/lib/ruby_jard/config.rb +9 -2
  25. data/lib/ruby_jard/decorators/array_decorator.rb +79 -0
  26. data/lib/ruby_jard/decorators/attributes_decorator.rb +172 -0
  27. data/lib/ruby_jard/decorators/color_decorator.rb +12 -5
  28. data/lib/ruby_jard/decorators/hash_decorator.rb +74 -0
  29. data/lib/ruby_jard/decorators/inspection_decorator.rb +91 -58
  30. data/lib/ruby_jard/decorators/object_decorator.rb +122 -0
  31. data/lib/ruby_jard/decorators/path_decorator.rb +55 -72
  32. data/lib/ruby_jard/decorators/rails_decorator.rb +194 -0
  33. data/lib/ruby_jard/decorators/string_decorator.rb +41 -0
  34. data/lib/ruby_jard/decorators/struct_decorator.rb +79 -0
  35. data/lib/ruby_jard/frame.rb +23 -10
  36. data/lib/ruby_jard/keys.rb +1 -0
  37. data/lib/ruby_jard/layouts/narrow_horizontal_layout.rb +4 -0
  38. data/lib/ruby_jard/layouts/tiny_layout.rb +4 -0
  39. data/lib/ruby_jard/pager.rb +21 -5
  40. data/lib/ruby_jard/path_classifier.rb +133 -0
  41. data/lib/ruby_jard/path_filter.rb +125 -0
  42. data/lib/ruby_jard/reflection.rb +97 -0
  43. data/lib/ruby_jard/repl_processor.rb +71 -38
  44. data/lib/ruby_jard/row_renderer.rb +5 -3
  45. data/lib/ruby_jard/screen.rb +2 -2
  46. data/lib/ruby_jard/screen_manager.rb +13 -36
  47. data/lib/ruby_jard/screen_renderer.rb +1 -1
  48. data/lib/ruby_jard/screens/backtrace_screen.rb +55 -39
  49. data/lib/ruby_jard/screens/menu_screen.rb +30 -30
  50. data/lib/ruby_jard/screens/source_screen.rb +46 -62
  51. data/lib/ruby_jard/screens/threads_screen.rb +59 -72
  52. data/lib/ruby_jard/screens/variables_screen.rb +168 -124
  53. data/lib/ruby_jard/session.rb +120 -16
  54. data/lib/ruby_jard/thread_info.rb +69 -0
  55. data/lib/ruby_jard/version.rb +1 -1
  56. data/ruby_jard.gemspec +3 -1
  57. metadata +20 -39
  58. data/.travis.yml +0 -6
  59. data/CNAME +0 -1
  60. data/_config.yml +0 -1
  61. data/docs/_config.yml +0 -8
  62. data/docs/color_schemes/256-light.png +0 -0
  63. data/docs/color_schemes/256.png +0 -0
  64. data/docs/color_schemes/deep-space.png +0 -0
  65. data/docs/color_schemes/gruvbox.png +0 -0
  66. data/docs/color_schemes/one-half-dark.png +0 -0
  67. data/docs/color_schemes/one-half-light.png +0 -0
  68. data/docs/demo.png +0 -0
  69. data/docs/guide-ui.png +0 -0
  70. data/docs/index.md +0 -238
  71. data/docs/logo.jpg +0 -0
  72. data/docs/screen-backtrace.png +0 -0
  73. data/docs/screen-repl.png +0 -0
  74. data/docs/screen-source.png +0 -0
  75. data/docs/screen-threads.png +0 -0
  76. data/docs/screen-variables.png +0 -0
  77. data/images/bg_hr.png +0 -0
  78. data/images/blacktocat.png +0 -0
  79. data/images/body-bg.jpg +0 -0
  80. data/images/download-button.png +0 -0
  81. data/images/github-button.png +0 -0
  82. data/images/header-bg.jpg +0 -0
  83. data/images/highlight-bg.jpg +0 -0
  84. data/images/icon_download.png +0 -0
  85. data/images/sidebar-bg.jpg +0 -0
  86. data/images/sprite_download.png +0 -0
  87. data/javascripts/main.js +0 -1
  88. data/stylesheets/github-light.css +0 -130
  89. data/stylesheets/normalize.css +0 -424
  90. data/stylesheets/print.css +0 -228
  91. 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
- KIND_SELF = :self
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
- KIND_SELF => :constant
21
+ KIND_GLOB => :instance_variable
40
22
  }.freeze
41
23
 
42
24
  KIND_PRIORITIES = {
43
25
  KIND_SELF => 0,
44
- KIND_LOC => 1,
45
- KIND_INS => 2,
46
- KIND_CON => 3
26
+ KIND_LOC => 1,
27
+ KIND_INS => 2,
28
+ KIND_CON => 3,
29
+ KIND_GLOB => 4
47
30
  }.freeze
48
31
 
49
- INLINE_TOKEN_KIND_MAPS = {
50
- KIND_LOC => :ident,
51
- KIND_INS => :instance_variable,
52
- KIND_CON => :constant
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
- INLINE_TOKEN_KINDS = INLINE_TOKEN_KIND_MAPS.values
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
- variables = sort_variables(variables)
67
- @rows = variables.map do |variable|
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: 3,
110
+ line_limit: 1,
70
111
  columns: [
112
+ RubyJard::Column.new,
71
113
  RubyJard::Column.new(
72
- spans: [
73
- span_inline(variable)
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 span_inline(variable)
91
- if inline?(variable[0], variable[1])
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: :variable_mark_inline
125
+ content: ' ',
126
+ styles: :text_dim
95
127
  )
96
128
  else
97
129
  RubyJard::Span.new(
98
- content: ' ',
99
- styles: :variable_mark
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 value.is_a?(Array) && !value.empty?
146
+ if RubyJard::Reflection.call_is_a?(value, Array) && !value.empty?
116
147
  "(len:#{value.length})"
117
- elsif value.is_a?(String) && value.length > 20
148
+ elsif RubyJard::Reflection.call_is_a?(value, String) && value.length > 20
118
149
  "(len:#{value.length})"
119
- elsif value.is_a?(Hash) && !value.empty?
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
- margin_right: 1,
154
+ margin_left: 1,
124
155
  content: size_label,
125
- styles: :variable_size
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
- variables = @session.current_frame.frame_binding.local_variables
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
- @session.current_frame.frame_binding.local_variable_get(:pry_instance).sticky_locals.keys
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, @session.current_frame.frame_binding.local_variable_get(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
- @session.current_frame.frame_self.instance_variables.map do |variable|
175
- [KIND_INS, variable, @session.current_frame.frame_self.instance_variable_get(variable)]
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 @session.current_frame.frame_class&.singleton_class?
185
- @session.current_frame.frame_self
202
+ if @frame_class&.singleton_class?
203
+ @frame_self
186
204
  else
187
- @session.current_frame.frame_class
205
+ @frame_class
188
206
  end
189
207
 
190
- return [] unless constant_source.respond_to?(:constants)
191
- return [] if toplevel_binding?
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
- constants = constant_source.constants.select { |v| v.to_s.upcase == v.to_s }
194
- constants.map do |variable|
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
- [KIND_CON, variable, constant_source.const_get(variable)]
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, @session.current_frame.frame_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?(kind, name)
239
- tokens = inline_tokens[INLINE_TOKEN_KIND_MAPS[kind]] || []
240
- tokens.include?(name)
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 inline_tokens
244
- return @inline_tokens if defined?(@inline_tokens)
276
+ def generate_inline_tokens(file, line)
277
+ return [] if file == nil || line == nil
245
278
 
246
- current_file = @session.current_frame.frame_file
247
- current_line = @session.current_frame.frame_line
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[current_line - source_decorator.window_start],
251
- current_file
282
+ source_decorator.codes[line - source_decorator.window_start],
283
+ file
252
284
  )
253
285
 
254
- @inline_tokens = {}
286
+ inline_tokens = {}
255
287
  tokens.each_slice(2) do |token, kind|
256
- next unless INLINE_TOKEN_KINDS.include?(kind)
288
+ next if TOKEN_KIND_MAPS[kind] == nil
257
289
 
258
- @inline_tokens[kind] ||= []
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 loc_decorator
266
- @loc_decorator ||= RubyJard::Decorators::LocDecorator.new
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
@@ -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
- attr_accessor :threads, :current_frame, :current_backtrace
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 update
60
- current_context = Byebug.current_context
61
- @current_frame = RubyJard::Frame.new(current_context, current_context.frame.pos)
62
- @current_backtrace = current_context.backtrace.map.with_index do |_frame, index|
63
- RubyJard::Frame.new(current_context, index)
64
- end
65
- @threads =
66
- Byebug
67
- .contexts
68
- .reject(&:ignored?)
69
- .reject { |c| c.thread.name.to_s =~ /<<Jard:.*>>/ }
70
- .map do |context|
71
- RubyJard::Frame.new(context, 0)
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