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.
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