ruby_jard 0.2.2 → 0.2.3

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.github/workflows/ruby.yml +85 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +70 -1
  7. data/CHANGELOG.md +31 -0
  8. data/Gemfile +6 -3
  9. data/README.md +122 -8
  10. data/bin/console +1 -2
  11. data/docs/color_schemes/256-light.png +0 -0
  12. data/docs/color_schemes/gruvbox.png +0 -0
  13. data/docs/color_schemes/one-half-dark.png +0 -0
  14. data/docs/color_schemes/one-half-light.png +0 -0
  15. data/lib/ruby_jard.rb +5 -5
  16. data/lib/ruby_jard/box_drawer.rb +4 -1
  17. data/lib/ruby_jard/color_schemes.rb +31 -15
  18. data/lib/ruby_jard/color_schemes/256_color_scheme.rb +37 -37
  19. data/lib/ruby_jard/color_schemes/256_light_color_scheme.rb +62 -0
  20. data/lib/ruby_jard/color_schemes/deep_space_color_scheme.rb +36 -36
  21. data/lib/ruby_jard/color_schemes/gruvbox_color_scheme.rb +62 -0
  22. data/lib/ruby_jard/color_schemes/one_half_dark_color_scheme.rb +61 -0
  23. data/lib/ruby_jard/color_schemes/one_half_light_color_scheme.rb +62 -0
  24. data/lib/ruby_jard/column.rb +3 -1
  25. data/lib/ruby_jard/commands/continue_command.rb +2 -3
  26. data/lib/ruby_jard/commands/down_command.rb +9 -5
  27. data/lib/ruby_jard/commands/exit_command.rb +27 -0
  28. data/lib/ruby_jard/commands/frame_command.rb +11 -10
  29. data/lib/ruby_jard/commands/jard/color_scheme_command.rb +52 -0
  30. data/lib/ruby_jard/commands/jard/hide_command.rb +40 -0
  31. data/lib/ruby_jard/commands/jard/output_command.rb +28 -0
  32. data/lib/ruby_jard/commands/jard/show_command.rb +41 -0
  33. data/lib/ruby_jard/commands/jard_command.rb +50 -0
  34. data/lib/ruby_jard/commands/list_command.rb +5 -4
  35. data/lib/ruby_jard/commands/next_command.rb +10 -5
  36. data/lib/ruby_jard/commands/step_command.rb +10 -5
  37. data/lib/ruby_jard/commands/step_out_command.rb +10 -5
  38. data/lib/ruby_jard/commands/up_command.rb +10 -5
  39. data/lib/ruby_jard/commands/validation_helpers.rb +50 -0
  40. data/lib/ruby_jard/config.rb +7 -3
  41. data/lib/ruby_jard/console.rb +10 -22
  42. data/lib/ruby_jard/control_flow.rb +3 -3
  43. data/lib/ruby_jard/decorators/color_decorator.rb +11 -5
  44. data/lib/ruby_jard/decorators/loc_decorator.rb +1 -1
  45. data/lib/ruby_jard/decorators/path_decorator.rb +20 -7
  46. data/lib/ruby_jard/decorators/source_decorator.rb +2 -0
  47. data/lib/ruby_jard/frame.rb +55 -0
  48. data/lib/ruby_jard/keys.rb +0 -3
  49. data/lib/ruby_jard/layout.rb +9 -2
  50. data/lib/ruby_jard/layout_calculator.rb +29 -12
  51. data/lib/ruby_jard/layout_picker.rb +34 -0
  52. data/lib/ruby_jard/layouts.rb +52 -0
  53. data/lib/ruby_jard/layouts/narrow_horizontal_layout.rb +28 -0
  54. data/lib/ruby_jard/layouts/narrow_vertical_layout.rb +32 -0
  55. data/lib/ruby_jard/layouts/tiny_layout.rb +25 -0
  56. data/lib/ruby_jard/layouts/wide_layout.rb +13 -15
  57. data/lib/ruby_jard/pager.rb +96 -0
  58. data/lib/ruby_jard/repl_processor.rb +61 -31
  59. data/lib/ruby_jard/repl_proxy.rb +193 -89
  60. data/lib/ruby_jard/row.rb +16 -1
  61. data/lib/ruby_jard/row_renderer.rb +51 -42
  62. data/lib/ruby_jard/screen.rb +2 -12
  63. data/lib/ruby_jard/screen_adjuster.rb +104 -0
  64. data/lib/ruby_jard/screen_drawer.rb +3 -0
  65. data/lib/ruby_jard/screen_manager.rb +32 -54
  66. data/lib/ruby_jard/screen_renderer.rb +30 -16
  67. data/lib/ruby_jard/screens.rb +31 -12
  68. data/lib/ruby_jard/screens/backtrace_screen.rb +23 -26
  69. data/lib/ruby_jard/screens/menu_screen.rb +53 -22
  70. data/lib/ruby_jard/screens/source_screen.rb +65 -37
  71. data/lib/ruby_jard/screens/threads_screen.rb +14 -14
  72. data/lib/ruby_jard/screens/variables_screen.rb +59 -34
  73. data/lib/ruby_jard/session.rb +19 -10
  74. data/lib/ruby_jard/span.rb +3 -0
  75. data/lib/ruby_jard/templates/layout_template.rb +1 -1
  76. data/lib/ruby_jard/templates/screen_template.rb +3 -4
  77. data/lib/ruby_jard/version.rb +1 -1
  78. data/ruby_jard.gemspec +1 -1
  79. metadata +38 -9
  80. data/lib/ruby_jard/commands/color_scheme_command.rb +0 -42
  81. data/lib/ruby_jard/layouts/narrow_layout.rb +0 -41
  82. data/lib/ruby_jard/screens/empty_screen.rb +0 -13
@@ -6,17 +6,15 @@ module RubyJard
6
6
  # generated based on input layout specifiation, screen data, and top-left
7
7
  # corner cordination.
8
8
  class Screen
9
- attr_accessor :layout, :rows, :color_scheme, :window, :cursor, :selected
9
+ attr_accessor :layout, :rows, :window, :cursor, :selected
10
10
 
11
- def initialize(session: nil, layout:, color_scheme:)
11
+ def initialize(session: nil, layout:)
12
12
  @session = session || RubyJard.current_session
13
13
  @layout = layout
14
- @color_scheme = color_scheme
15
14
  @window = []
16
15
  @cursor = nil
17
16
  @selected = 0
18
17
  @rows = []
19
- @need_to_render = true
20
18
  end
21
19
 
22
20
  def move_down; end
@@ -32,13 +30,5 @@ module RubyJard
32
30
  def build
33
31
  raise NotImplementedError, "#{self.class} should implement this method."
34
32
  end
35
-
36
- def need_to_render?
37
- @need_to_render == true
38
- end
39
-
40
- def mark_rendered!
41
- @need_to_render = false
42
- end
43
33
  end
44
34
  end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyJard
4
+ ##
5
+ # Implement elastic screen height. If a screen is shrinkable (which means)
6
+ # its current window is less than its height, it will be forced to give
7
+ # away those spaces to other screens. This layout adjustment is small, and
8
+ # should not affect the original generated layout too much, nor work with
9
+ # nested layout.
10
+ class ScreenAdjuster
11
+ def initialize(screens)
12
+ @screens = screens
13
+ end
14
+
15
+ def adjust
16
+ groups = @screens.group_by { |screen| screen.layout.parent_template }
17
+ groups.each do |_, grouped_screens|
18
+ next if grouped_screens.length <= 1
19
+ next unless same_column?(grouped_screens)
20
+
21
+ grouped_screens.sort_by! { |screen| screen.layout.box_y }
22
+ shrinkable_screens = grouped_screens.select { |s| shrinkable?(s) }
23
+ expandable_screens = grouped_screens.select { |s| expandable?(s) }
24
+
25
+ next if shrinkable_screens.empty? || expandable_screens.empty?
26
+
27
+ budget = shrinkable_screens.map { |s| shrinkable_height(s) }.sum
28
+ expand_screens(expandable_screens, budget)
29
+ shrink_screens(shrinkable_screens)
30
+ compact_screens(grouped_screens)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def same_column?(screens)
37
+ column_x = screens.first.layout.box_x
38
+ column_width = screens.first.layout.box_width
39
+ screens.all? { |s| s.layout.box_x == column_x && s.layout.box_width == column_width }
40
+ end
41
+
42
+ def expand_screens(expandable_screens, budget)
43
+ budget_each = budget / expandable_screens.length
44
+
45
+ expandable_screens.each_with_index do |screen, index|
46
+ if index == expandable_screens.length - 1
47
+ screen.layout.height += budget
48
+ screen.layout.box_height += budget
49
+ else
50
+ screen.layout.height += budget_each
51
+ screen.layout.box_height += budget_each
52
+ budget_each -= budget_each
53
+ end
54
+ end
55
+ end
56
+
57
+ def shrink_screens(shrinkable_screens)
58
+ shrinkable_screens.each do |screen|
59
+ delta = shrinkable_height(screen)
60
+ screen.layout.height -= delta
61
+ screen.layout.box_height -= delta
62
+ end
63
+ end
64
+
65
+ def compact_screens(screens)
66
+ box_y = screens.first.layout.box_y
67
+ screens.each do |screen|
68
+ screen.layout.box_y = box_y
69
+ screen.layout.y = box_y + 1
70
+ box_y += screen.layout.box_height - 1
71
+ end
72
+ end
73
+
74
+ def shrinkable?(screen)
75
+ case screen.layout.template.adjust_mode
76
+ when :expand
77
+ false
78
+ else
79
+ screen.window.length < screen.layout.height
80
+ end
81
+ end
82
+
83
+ def expandable?(screen)
84
+ case screen.layout.template.adjust_mode
85
+ when :expand
86
+ true
87
+ else
88
+ screen.window.length >= screen.layout.height
89
+ end
90
+ end
91
+
92
+ def shrinkable_height(screen)
93
+ if screen.window.length < screen.layout.height
94
+ window_height = screen.window.length
95
+ if !screen.layout.template.min_height.nil? && screen.layout.template.min_height > window_height
96
+ window_height = screen.layout.template.min_height
97
+ end
98
+ screen.layout.height - window_height
99
+ else
100
+ 0
101
+ end
102
+ end
103
+ end
104
+ end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyJard
4
+ ##
5
+ # Draw rendered screen bitmap in current screen window onto the screen.
6
+ # Fulfill missing window if needed
4
7
  class ScreenDrawer
5
8
  def initialize(output:, screen:, color_scheme:)
6
9
  @output = output
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tempfile'
3
4
  require 'ruby_jard/console'
4
5
 
5
6
  require 'ruby_jard/decorators/color_decorator'
@@ -8,36 +9,19 @@ require 'ruby_jard/decorators/loc_decorator'
8
9
  require 'ruby_jard/decorators/source_decorator'
9
10
  require 'ruby_jard/decorators/inspection_decorator'
10
11
 
11
- require 'ruby_jard/screen'
12
12
  require 'ruby_jard/screens'
13
-
14
- require 'ruby_jard/color_scheme'
15
13
  require 'ruby_jard/color_schemes'
16
- require 'ruby_jard/color_schemes/deep_space_color_scheme'
17
- require 'ruby_jard/color_schemes/256_color_scheme'
14
+ require 'ruby_jard/layouts'
18
15
 
19
16
  require 'ruby_jard/row'
20
17
  require 'ruby_jard/column'
21
18
  require 'ruby_jard/span'
22
19
  require 'ruby_jard/row_renderer'
23
20
  require 'ruby_jard/screen_renderer'
21
+ require 'ruby_jard/screen_adjuster'
24
22
  require 'ruby_jard/box_drawer'
25
23
  require 'ruby_jard/screen_drawer'
26
24
 
27
- require 'ruby_jard/screens/source_screen'
28
- require 'ruby_jard/screens/backtrace_screen'
29
- require 'ruby_jard/screens/threads_screen'
30
- require 'ruby_jard/screens/variables_screen'
31
- require 'ruby_jard/screens/menu_screen'
32
-
33
- require 'ruby_jard/templates/layout_template'
34
- require 'ruby_jard/templates/screen_template'
35
-
36
- require 'ruby_jard/layouts/wide_layout'
37
- require 'ruby_jard/layouts/narrow_layout'
38
- require 'ruby_jard/layout'
39
- require 'ruby_jard/layout_calculator'
40
-
41
25
  module RubyJard
42
26
  ##
43
27
  # This class acts as a coordinator, in which it combines the data and screen
@@ -46,7 +30,7 @@ module RubyJard
46
30
  class << self
47
31
  extend Forwardable
48
32
 
49
- def_delegators :instance, :update, :draw_error, :started?, :updating?
33
+ def_delegators :instance, :update, :puts, :draw_error, :started?, :updating?
50
34
 
51
35
  def instance
52
36
  @instance ||= new
@@ -60,7 +44,7 @@ module RubyJard
60
44
  @screens = {}
61
45
  @started = false
62
46
  @updating = false
63
- @output_storage = StringIO.new
47
+ @output_storage = Tempfile.new('jard')
64
48
  end
65
49
 
66
50
  def start
@@ -71,14 +55,16 @@ module RubyJard
71
55
  RubyJard::Console.start_alternative_terminal(@output)
72
56
  RubyJard::Console.clear_screen(@output)
73
57
 
74
- def $stdout.write(string)
58
+ # rubocop:disable Lint/NestedMethodDefinition
59
+ def $stdout.write(*string, from_jard: false)
75
60
  # NOTE: `RubyJard::ScreenManager.instance` is a must. Jard doesn't work well with delegator
76
61
  # TODO: Debug and fix the issues permanently
77
- if !RubyJard::ScreenManager.instance.updating? && RubyJard::ScreenManager.instance.started?
78
- RubyJard::ScreenManager.instance.output_storage.write(string)
62
+ if !RubyJard::ScreenManager.instance.updating? && RubyJard::ScreenManager.instance.started? && !from_jard
63
+ RubyJard::ScreenManager.instance.output_storage.write(*string)
79
64
  end
80
- super
65
+ super(*string)
81
66
  end
67
+ # rubocop:enable Lint/NestedMethodDefinition
82
68
 
83
69
  at_exit { stop }
84
70
  @started = true
@@ -102,12 +88,10 @@ module RubyJard
102
88
  RubyJard::Console.enable_echo!
103
89
  RubyJard::Console.enable_cursor!
104
90
 
105
- unless @output_storage.string.empty?
106
- @output.puts ''
107
- @output.write @output_storage.string
108
- @output.puts ''
109
- end
91
+ @output_storage.rewind
92
+ @output.write @output_storage.read until @output_storage.eof?
110
93
  @output_storage.close
94
+ @output_storage.unlink
111
95
  end
112
96
 
113
97
  def update
@@ -119,13 +103,14 @@ module RubyJard
119
103
 
120
104
  @layouts = calculate_layouts(width, height)
121
105
  @screens = build_screens(@layouts)
106
+
107
+ RubyJard::Console.move_to(@output, 0, 0)
108
+
122
109
  draw_box(@screens)
123
110
  draw_screens(@screens)
124
111
 
125
112
  RubyJard::Console.move_to(@output, 0, total_screen_height(@layouts) + 1)
126
113
  RubyJard::Console.clear_screen_to_end(@output)
127
-
128
- draw_debug(width, height)
129
114
  rescue StandardError => e
130
115
  RubyJard::Console.clear_screen(@output)
131
116
  draw_error(e, height)
@@ -156,10 +141,14 @@ module RubyJard
156
141
  @output.puts '-------------'
157
142
  end
158
143
 
144
+ def puts(content)
145
+ @output.write "#{content}\n", from_jard: true
146
+ end
147
+
159
148
  private
160
149
 
161
150
  def calculate_layouts(width, height)
162
- layout = pick_layout(width, height)
151
+ layout = RubyJard::LayoutPicker.new(width, height).pick
163
152
  RubyJard::LayoutCalculator.calculate(
164
153
  layout_template: layout,
165
154
  width: width, height: height,
@@ -168,12 +157,17 @@ module RubyJard
168
157
  end
169
158
 
170
159
  def build_screens(layouts)
160
+ screens = layouts.map do |layout|
161
+ screen_class = fetch_screen(layout.template.screen)
162
+ screen = screen_class.new(layout: layout)
163
+ screen.build
164
+ render_screen(screen)
165
+ screen
166
+ end
167
+ RubyJard::ScreenAdjuster.new(screens).adjust
171
168
  layouts.map do |layout|
172
169
  screen_class = fetch_screen(layout.template.screen)
173
- screen = screen_class.new(
174
- layout: layout,
175
- color_scheme: pick_color_scheme
176
- )
170
+ screen = screen_class.new(layout: layout)
177
171
  screen.build
178
172
  render_screen(screen)
179
173
  screen
@@ -222,23 +216,7 @@ module RubyJard
222
216
  end
223
217
 
224
218
  def total_screen_height(layouts)
225
- layouts.map { |layout| layout.y + layout.height }.max
226
- end
227
-
228
- def pick_layout(width, height)
229
- RubyJard::DEFAULT_LAYOUT_TEMPLATES.each do |template|
230
- matched = true
231
- matched &&= (
232
- template.min_width.nil? ||
233
- width > template.min_width
234
- )
235
- matched &&= (
236
- template.min_height.nil? ||
237
- height > template.min_height
238
- )
239
- return template if matched
240
- end
241
- RubyJard::DEFAULT_LAYOUT_TEMPLATES.first
219
+ layouts.map { |layout| layout.y + layout.height }.max || 0
242
220
  end
243
221
 
244
222
  def pick_color_scheme
@@ -11,15 +11,11 @@ module RubyJard
11
11
  end
12
12
 
13
13
  def render
14
- return unless @screen.need_to_render?
15
-
16
14
  # Move this logic into a class called SreenRenderer
17
15
  calculate_content_lengths
18
16
  column_widths = calculate_column_widths
19
17
  adjust_column_widths(column_widths)
20
- render_rows
21
18
  calculate_window
22
- @screen.mark_rendered!
23
19
 
24
20
  @screen
25
21
  end
@@ -57,10 +53,8 @@ module RubyJard
57
53
  end
58
54
 
59
55
  def adjust_column_widths(column_widths)
60
- dynamic_count = column_widths.values.select(&:nil?).length
61
- fixed_width = column_widths.values.inject(0) do |sum, col|
62
- col.nil? ? sum : sum + col
63
- end
56
+ dynamic_count = count_dynamic_columns(column_widths)
57
+ fixed_width = sum_fixed_width(column_widths)
64
58
 
65
59
  @screen.rows.each do |row|
66
60
  total_width = 0
@@ -73,19 +67,26 @@ module RubyJard
73
67
  else
74
68
  column_widths[column_index]
75
69
  end
70
+ column.content_width = column.width
71
+ column.content_width -= 1 if column_index < row.columns.length - 1
72
+
76
73
  total_width += column.width
77
74
  end
78
75
  end
79
76
  end
80
77
 
78
+ def count_dynamic_columns(column_widths)
79
+ column_widths.values.select(&:nil?).length
80
+ end
81
+
82
+ def sum_fixed_width(column_widths)
83
+ column_widths.values.inject(0) do |sum, col|
84
+ col.nil? ? sum : sum + col
85
+ end
86
+ end
87
+
81
88
  def render_rows
82
89
  @screen.rows.each do |row|
83
- RubyJard::RowRenderer.new(
84
- row: row,
85
- width: @screen.layout.width,
86
- height: @screen.layout.height,
87
- color_scheme: @color_scheme
88
- ).render
89
90
  end
90
91
  end
91
92
 
@@ -101,7 +102,7 @@ module RubyJard
101
102
 
102
103
  def find_seleted_window
103
104
  @screen.rows.each_with_index do |row, row_index|
104
- row.content.each_with_index do |line, line_index|
105
+ row_content(row).each_with_index do |line, line_index|
105
106
  if @screen.window.length < @screen.layout.height
106
107
  @screen.window << line
107
108
  elsif row_index < @screen.selected
@@ -123,7 +124,7 @@ module RubyJard
123
124
  def find_cursor_window
124
125
  cursor_line = -1
125
126
  @screen.rows.each do |row|
126
- row.content.each do |line|
127
+ row_content(row).each do |line|
127
128
  cursor_line += 1
128
129
  @screen.window << line if cursor_line >= @screen.cursor
129
130
  return if @screen.window.length >= @screen.layout.height
@@ -134,5 +135,18 @@ module RubyJard
134
135
  def count_columns
135
136
  @screen.rows.map { |row| row.columns.count }.max.to_i
136
137
  end
138
+
139
+ def row_content(row)
140
+ unless row.rendered?
141
+ RubyJard::RowRenderer.new(
142
+ row: row,
143
+ width: @screen.layout.width,
144
+ height: @screen.layout.height,
145
+ color_scheme: @color_scheme
146
+ ).render
147
+ end
148
+
149
+ row.content
150
+ end
137
151
  end
138
152
  end
@@ -1,26 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'ruby_jard/screen'
4
+
3
5
  module RubyJard
4
6
  ##
5
7
  # Screen registry. The screens call add_screen right after they are declared.
6
- module Screens
8
+ class Screens
7
9
  class << self
8
- def screen_registry
9
- @screen_registry ||= {}
10
+ extend Forwardable
11
+ def_delegators :instance, :add_screen, :[], :get, :names
12
+
13
+ def instance
14
+ @instance ||= new
10
15
  end
16
+ end
11
17
 
12
- def add_screen(name, screen_class)
13
- unless screen_class < RubyJard::Screen
14
- raise RubyJard::Error, "#{screen_class} must implement, and inherit from #{RubyJard::Screen}"
15
- end
18
+ def initialize
19
+ @screen_registry = {}
20
+ end
16
21
 
17
- screen_registry[name] = screen_class
22
+ def add_screen(name, screen_class)
23
+ unless screen_class < RubyJard::Screen
24
+ raise RubyJard::Error, "#{screen_class} must implement, and inherit from #{RubyJard::Screen}"
18
25
  end
19
26
 
20
- def [](name)
21
- screen_registry[name]
22
- end
23
- alias get []
27
+ @screen_registry[name.to_s] = screen_class
28
+ end
29
+
30
+ def [](name)
31
+ @screen_registry[name.to_s]
32
+ end
33
+ alias_method :get, :[]
34
+
35
+ def names
36
+ @screen_registry.keys.sort.dup
24
37
  end
25
38
  end
26
39
  end
40
+
41
+ require 'ruby_jard/screens/source_screen'
42
+ require 'ruby_jard/screens/backtrace_screen'
43
+ require 'ruby_jard/screens/threads_screen'
44
+ require 'ruby_jard/screens/variables_screen'
45
+ require 'ruby_jard/screens/menu_screen'