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
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyJard
4
+ ##
5
+ # User classes may override basic Kernel methods, such as #inspect
6
+ # or #to_s, or even #is_a?. It's not very wise to call those methods
7
+ # directly, as there maybe side effects. Therefore, this class is
8
+ # to extract unbound methods from Kernel module, and then call them
9
+ # in Object's context.
10
+ class Reflection
11
+ class << self
12
+ def call_class(object)
13
+ if call_is_a?(object, Module)
14
+ bind_call(Kernel, :class, object)
15
+ else
16
+ instance_bind_call(Kernel, :class, object)
17
+ end
18
+ end
19
+
20
+ def call_respond_to?(object, method_name)
21
+ if call_is_a?(object, Module)
22
+ bind_call(Kernel, :respond_to?, object, method_name)
23
+ else
24
+ instance_bind_call(Kernel, :respond_to?, object, method_name)
25
+ end
26
+ end
27
+
28
+ def call_instance_variables(object)
29
+ bind_call(Kernel, :instance_variables, object)
30
+ end
31
+
32
+ def call_instance_variable_get(object, variable)
33
+ bind_call(Kernel, :instance_variable_get, object, variable)
34
+ end
35
+
36
+ def call_instance_variable_set(object, variable, value)
37
+ bind_call(Kernel, :instance_variable_set, object, variable, value)
38
+ end
39
+
40
+ def call_inspect(object)
41
+ if call_is_a?(object, Module)
42
+ bind_call(Kernel, :inspect, object)
43
+ else
44
+ instance_bind_call(Kernel, :inspect, object)
45
+ end
46
+ end
47
+
48
+ def call_to_s(object)
49
+ if call_is_a?(object, Module)
50
+ bind_call(Kernel, :to_s, object)
51
+ else
52
+ instance_bind_call(Kernel, :to_s, object)
53
+ end
54
+ end
55
+
56
+ def call_is_a?(object, comparing_class)
57
+ bind_call(Kernel, :is_a?, object, comparing_class)
58
+ end
59
+
60
+ def call_const_get(object, const_name)
61
+ bind_call(Kernel, :const_get, object, const_name)
62
+ end
63
+
64
+ def call_const_defined?(object, const_name)
65
+ bind_call(Kernel, :const_defined?, object, const_name)
66
+ end
67
+
68
+ def bind_call(owner, method_name, object, *args)
69
+ @method_cache ||= {}
70
+ @method_cache[owner] ||= {}
71
+ @method_cache[owner][method_name] ||= fetch_method(owner, method_name)
72
+ @method_cache[owner][method_name].bind(object).call(*args)
73
+ end
74
+
75
+ def instance_bind_call(owner, method_name, object, *args)
76
+ @instance_method_cache ||= {}
77
+ @instance_method_cache[owner] ||= {}
78
+ @instance_method_cache[owner][method_name] ||= fetch_instance_method(owner, method_name)
79
+ @instance_method_cache[owner][method_name].bind(object).call(*args)
80
+ end
81
+
82
+ def fetch_method(object, method_name)
83
+ @method_cache ||= {}
84
+ @method_cache[::Kernel] ||= {}
85
+ @method_cache[::Kernel][:method] ||= ::Kernel.method(:method).unbind
86
+ @method_cache[::Kernel][:method].bind(object).call(method_name).unbind
87
+ end
88
+
89
+ def fetch_instance_method(object, method_name)
90
+ @method_cache ||= {}
91
+ @method_cache[::Kernel] ||= {}
92
+ @method_cache[::Kernel][:instance_method] ||= ::Kernel.method(:instance_method).unbind
93
+ @method_cache[::Kernel][:instance_method].bind(object).call(method_name)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'ruby_jard/commands/validation_helpers'
4
+ require 'ruby_jard/commands/color_helpers'
4
5
  require 'ruby_jard/commands/continue_command'
5
6
  require 'ruby_jard/commands/exit_command'
6
7
  require 'ruby_jard/commands/up_command'
@@ -24,9 +25,11 @@ module RubyJard
24
25
  class ReplProcessor < Byebug::CommandProcessor
25
26
  def initialize(context, *args)
26
27
  super(context, *args)
28
+ @config = RubyJard.config
27
29
  @repl_proxy = RubyJard::ReplProxy.new(
28
30
  key_bindings: RubyJard.global_key_bindings
29
31
  )
32
+ @previous_flow = nil
30
33
  end
31
34
 
32
35
  def at_line
@@ -45,27 +48,29 @@ module RubyJard
45
48
 
46
49
  def process_commands_with_lock
47
50
  allowing_other_threads do
48
- RubyJard.current_session.lock do
51
+ RubyJard::Session.lock do
52
+ RubyJard::Session.sync(@context)
53
+ unless RubyJard::Session.should_stop?
54
+ handle_flow(@previous_flow)
55
+ return
56
+ end
57
+
49
58
  process_commands
50
59
  end
51
60
  end
52
61
  end
53
62
 
54
- def process_commands(update = true)
55
- if update
56
- RubyJard.current_session.update
57
- RubyJard::ScreenManager.update
58
- end
63
+ def process_commands(redraw = true)
64
+ RubyJard::Session.sync(@context)
65
+ RubyJard::ScreenManager.draw_screens if redraw
66
+
59
67
  return_value = nil
60
68
 
61
69
  flow = RubyJard::ControlFlow.listen do
62
70
  return_value = @repl_proxy.repl(frame._binding)
63
71
  end
64
72
 
65
- unless flow.nil?
66
- command = flow.command
67
- send("handle_#{command}_command", flow.arguments)
68
- end
73
+ handle_flow(flow)
69
74
 
70
75
  return_value
71
76
  rescue StandardError => e
@@ -73,58 +78,65 @@ module RubyJard
73
78
  raise
74
79
  end
75
80
 
81
+ def handle_flow(flow)
82
+ return if flow.nil?
83
+
84
+ @previous_flow = flow
85
+ command = flow.command
86
+ send("handle_#{command}_command", flow.arguments)
87
+ end
88
+
76
89
  def handle_next_command(options = {})
77
90
  times = options[:times] || 1
78
- Byebug.current_context.step_over(times, Byebug.current_context.frame.pos)
79
- proceed!
91
+ RubyJard::Session.step_over(times)
80
92
  end
81
93
 
82
94
  def handle_step_command(options = {})
83
95
  times = options[:times] || 1
84
- Byebug.current_context.step_into(times, Byebug.current_context.frame.pos)
85
- proceed!
96
+ RubyJard::Session.step_into(times)
86
97
  end
87
98
 
88
99
  def handle_step_out_command(options = {})
89
100
  times = options[:times] || 1
90
101
 
91
- next_frame = up_n_frames(Byebug.current_context.frame.pos, times)
92
- Byebug.current_context.frame = next_frame
93
- Byebug.current_context.step_over(1, Byebug.current_context.frame.pos)
94
- proceed!
102
+ next_frame = up_n_frames(RubyJard::Session.current_frame.real_pos, times)
103
+ RubyJard::Session.frame = next_frame
104
+ RubyJard::Session.step_over(1)
95
105
  end
96
106
 
97
107
  def handle_up_command(options = {})
98
108
  times = options[:times] || 1
99
109
 
100
- next_frame = up_n_frames(Byebug.current_context.frame.pos, times)
101
- Byebug.current_context.frame = next_frame
102
- proceed!
110
+ next_frame = up_n_frames(RubyJard::Session.current_frame.real_pos, times)
111
+ RubyJard::Session.frame = next_frame
103
112
  process_commands
104
113
  end
105
114
 
106
115
  def handle_down_command(options = {})
107
116
  times = options[:times] || 1
108
- next_frame = down_n_frames(Byebug.current_context.frame.pos, times)
109
- Byebug.current_context.frame = next_frame
110
- proceed!
117
+ next_frame = down_n_frames(RubyJard::Session.current_frame.real_pos, times)
118
+ RubyJard::Session.frame = next_frame
111
119
  process_commands
112
120
  end
113
121
 
114
122
  def handle_frame_command(options)
115
- next_frame = options[:frame].to_i
116
- if Byebug::Frame.new(Byebug.current_context, next_frame).c_frame?
123
+ next_frame = find_frame(options[:frame].to_i)
124
+ if next_frame.nil?
125
+ # There must be an error in outer validators
126
+ RubyJard::ScreenManager.puts 'Error: Frame not found. There should be an error with Jard.'
127
+ process_commands(false)
128
+ elsif next_frame.c_frame?
117
129
  RubyJard::ScreenManager.puts "Error: Frame #{next_frame} is a c-frame. Not able to inspect c layer!"
118
130
  process_commands(false)
119
131
  else
120
- Byebug.current_context.frame = next_frame
121
- proceed!
132
+ RubyJard::Session.frame = next_frame.real_pos
122
133
  process_commands(true)
123
134
  end
124
135
  end
125
136
 
126
137
  def handle_continue_command(_options = {})
127
- RubyJard::ScreenManager.puts ' Program resumed ► ►'
138
+ RubyJard::ScreenManager.puts '▸▸ Program resumed ▸▸'
139
+ Byebug.stop if Byebug.stoppable?
128
140
  end
129
141
 
130
142
  def handle_exit_command(_options = {})
@@ -145,28 +157,49 @@ module RubyJard
145
157
  process_commands
146
158
  end
147
159
 
148
- def up_n_frames(current_frame, times)
149
- next_frame = current_frame
160
+ def handle_switch_filter_command(_options = {})
161
+ index = RubyJard::PathFilter::FILTERS.index(@config.filter) || -1
162
+ index = (index + 1) % RubyJard::PathFilter::FILTERS.length
163
+ @config.filter = RubyJard::PathFilter::FILTERS[index]
164
+
165
+ process_commands
166
+ end
167
+
168
+ def up_n_frames(real_pos, times)
169
+ next_frame = real_pos
150
170
  times.times do
151
- next_frame = [next_frame + 1, Byebug.current_context.backtrace.length - 1].min
152
- while Byebug::Frame.new(Byebug.current_context, next_frame).c_frame? &&
153
- next_frame < Byebug.current_context.backtrace.length - 1
171
+ next_frame = [next_frame + 1, RubyJard::Session.current_backtrace.length - 1].min
172
+ while next_frame < RubyJard::Session.current_backtrace.length &&
173
+ (
174
+ RubyJard::Session.current_backtrace[next_frame].c_frame? ||
175
+ RubyJard::Session.current_backtrace[next_frame].hidden?
176
+ )
154
177
  next_frame += 1
155
178
  end
179
+ return real_pos if next_frame >= RubyJard::Session.current_backtrace.length
156
180
  end
157
181
  next_frame
158
182
  end
159
183
 
160
- def down_n_frames(current_frame, times)
161
- next_frame = current_frame
184
+ def down_n_frames(real_pos, times)
185
+ next_frame = real_pos
162
186
  times.times do
163
187
  next_frame = [next_frame - 1, 0].max
164
- while Byebug::Frame.new(Byebug.current_context, next_frame).c_frame? &&
165
- next_frame > 0
188
+ while next_frame >= 0 &&
189
+ (
190
+ RubyJard::Session.current_backtrace[next_frame].c_frame? ||
191
+ RubyJard::Session.current_backtrace[next_frame].hidden?
192
+ )
193
+
166
194
  next_frame -= 1
167
195
  end
196
+ return real_pos if next_frame < 0
168
197
  end
169
198
  next_frame
170
199
  end
200
+
201
+ def find_frame(virtual_pos)
202
+ RubyJard::Session.current_backtrace.find { |frame| frame.virtual_pos == virtual_pos }
203
+ end
171
204
  end
172
205
  end
@@ -30,7 +30,8 @@ module RubyJard
30
30
  @drawing_lines = 1
31
31
 
32
32
  column.spans.each do |span|
33
- render_span(column, span)
33
+ to_continue = render_span(column, span)
34
+ break unless to_continue
34
35
  end
35
36
 
36
37
  @original_x += column.width
@@ -61,7 +62,7 @@ module RubyJard
61
62
  @x = @original_x
62
63
  end
63
64
  elsif column.content_width - @drawing_width <= 0
64
- return
65
+ return false
65
66
  end
66
67
  drawing_content = line_content[0..column.content_width - @drawing_width - 1]
67
68
  line_content = line_content[column.content_width - @drawing_width..-1]
@@ -73,11 +74,12 @@ module RubyJard
73
74
  !line_content.empty?
74
75
  drawing_content[drawing_content.length - ELLIPSIS.length..-1] = ELLIPSIS
75
76
  draw_content(drawing_content, span.styles)
76
- return
77
+ return false
77
78
  else
78
79
  draw_content(drawing_content, span.styles)
79
80
  end
80
81
  end
82
+ true
81
83
  end
82
84
  # rubocop:enable Metrics/MethodLength
83
85
 
@@ -8,8 +8,8 @@ module RubyJard
8
8
  class Screen
9
9
  attr_accessor :layout, :rows, :window, :cursor, :selected
10
10
 
11
- def initialize(session: nil, layout:)
12
- @session = session || RubyJard.current_session
11
+ def initialize(layout, session: nil)
12
+ @session = session || RubyJard::Session
13
13
  @layout = layout
14
14
  @window = []
15
15
  @cursor = nil
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tempfile'
4
3
  require 'ruby_jard/console'
5
4
 
6
5
  require 'ruby_jard/decorators/color_decorator'
@@ -30,7 +29,7 @@ module RubyJard
30
29
  class << self
31
30
  extend Forwardable
32
31
 
33
- def_delegators :instance, :update, :puts, :draw_error, :started?, :updating?
32
+ def_delegators :instance, :draw_screens, :puts, :draw_error, :start, :stop, :started?, :updating?
34
33
 
35
34
  def instance
36
35
  @instance ||= new
@@ -44,7 +43,6 @@ module RubyJard
44
43
  @screens = {}
45
44
  @started = false
46
45
  @updating = false
47
- @output_storage = Tempfile.new('jard')
48
46
  end
49
47
 
50
48
  def start
@@ -52,21 +50,8 @@ module RubyJard
52
50
 
53
51
  # Load configurations
54
52
  RubyJard.config
55
- RubyJard::Console.start_alternative_terminal(@output)
56
53
  RubyJard::Console.clear_screen(@output)
57
54
 
58
- # rubocop:disable Lint/NestedMethodDefinition
59
- def $stdout.write(*string, from_jard: false)
60
- # NOTE: `RubyJard::ScreenManager.instance` is a must. Jard doesn't work well with delegator
61
- # TODO: Debug and fix the issues permanently
62
- if !RubyJard::ScreenManager.instance.updating? && RubyJard::ScreenManager.instance.started? && !from_jard
63
- RubyJard::ScreenManager.instance.output_storage.write(*string)
64
- end
65
- super(*string)
66
- end
67
- # rubocop:enable Lint/NestedMethodDefinition
68
-
69
- at_exit { stop }
70
55
  @started = true
71
56
  end
72
57
 
@@ -83,21 +68,16 @@ module RubyJard
83
68
 
84
69
  @started = false
85
70
 
86
- RubyJard::Console.stop_alternative_terminal(@output)
87
71
  RubyJard::Console.cooked!
88
72
  RubyJard::Console.enable_echo!
89
73
  RubyJard::Console.enable_cursor!
90
-
91
- @output_storage.rewind
92
- @output.write @output_storage.read until @output_storage.eof?
93
- @output_storage.close
94
- @output_storage.unlink
95
74
  end
96
75
 
97
- def update
76
+ def draw_screens
98
77
  start unless started?
99
78
  @updating = true
100
79
 
80
+ RubyJard::Console.clear_screen(@output)
101
81
  RubyJard::Console.disable_cursor!
102
82
  width, height = RubyJard::Console.screen_size(@output)
103
83
 
@@ -107,7 +87,13 @@ module RubyJard
107
87
  RubyJard::Console.move_to(@output, 0, 0)
108
88
 
109
89
  draw_box(@screens)
110
- draw_screens(@screens)
90
+ @screens.each do |screen|
91
+ RubyJard::ScreenDrawer.new(
92
+ output: @output,
93
+ screen: screen,
94
+ color_scheme: pick_color_scheme
95
+ ).draw
96
+ end
111
97
 
112
98
  RubyJard::Console.move_to(@output, 0, total_screen_height(@layouts) + 1)
113
99
  RubyJard::Console.clear_screen_to_end(@output)
@@ -139,6 +125,7 @@ module RubyJard
139
125
  @output.puts exception.backtrace.first(10)
140
126
  end
141
127
  @output.puts '-------------'
128
+ RubyJard.error(exception)
142
129
  end
143
130
 
144
131
  def puts(content)
@@ -159,7 +146,7 @@ module RubyJard
159
146
  def build_screens(layouts)
160
147
  screens = layouts.map do |layout|
161
148
  screen_class = fetch_screen(layout.template.screen)
162
- screen = screen_class.new(layout: layout)
149
+ screen = screen_class.new(layout)
163
150
  screen.build
164
151
  render_screen(screen)
165
152
  screen
@@ -167,7 +154,7 @@ module RubyJard
167
154
  RubyJard::ScreenAdjuster.new(screens).adjust
168
155
  layouts.map do |layout|
169
156
  screen_class = fetch_screen(layout.template.screen)
170
- screen = screen_class.new(layout: layout)
157
+ screen = screen_class.new(layout)
171
158
  screen.build
172
159
  render_screen(screen)
173
160
  screen
@@ -182,16 +169,6 @@ module RubyJard
182
169
  ).draw
183
170
  end
184
171
 
185
- def draw_screens(screens)
186
- screens.each do |screen|
187
- RubyJard::ScreenDrawer.new(
188
- output: @output,
189
- screen: screen,
190
- color_scheme: pick_color_scheme
191
- ).draw
192
- end
193
- end
194
-
195
172
  def render_screen(screen)
196
173
  RubyJard::ScreenRenderer.new(
197
174
  screen: screen,