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
@@ -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,