ruby_jard 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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'