ruby_jard 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -0
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile +1 -1
  5. data/README.md +65 -2
  6. data/docs/guide-ui.png +0 -0
  7. data/lib/ruby_jard.rb +49 -12
  8. data/lib/ruby_jard/box_drawer.rb +126 -0
  9. data/lib/ruby_jard/column.rb +18 -0
  10. data/lib/ruby_jard/commands/continue_command.rb +1 -6
  11. data/lib/ruby_jard/commands/down_command.rb +1 -4
  12. data/lib/ruby_jard/commands/frame_command.rb +12 -11
  13. data/lib/ruby_jard/commands/next_command.rb +1 -4
  14. data/lib/ruby_jard/commands/step_command.rb +1 -4
  15. data/lib/ruby_jard/commands/step_out_command.rb +28 -0
  16. data/lib/ruby_jard/commands/up_command.rb +1 -4
  17. data/lib/ruby_jard/console.rb +86 -0
  18. data/lib/ruby_jard/control_flow.rb +71 -0
  19. data/lib/ruby_jard/decorators/color_decorator.rb +78 -0
  20. data/lib/ruby_jard/decorators/loc_decorator.rb +41 -28
  21. data/lib/ruby_jard/decorators/source_decorator.rb +1 -1
  22. data/lib/ruby_jard/key_binding.rb +14 -0
  23. data/lib/ruby_jard/key_bindings.rb +96 -0
  24. data/lib/ruby_jard/keys.rb +49 -0
  25. data/lib/ruby_jard/layout.rb +67 -55
  26. data/lib/ruby_jard/layouts/wide_layout.rb +138 -0
  27. data/lib/ruby_jard/repl_processor.rb +80 -90
  28. data/lib/ruby_jard/repl_proxy.rb +232 -0
  29. data/lib/ruby_jard/row.rb +16 -0
  30. data/lib/ruby_jard/screen.rb +114 -36
  31. data/lib/ruby_jard/screen_drawer.rb +89 -0
  32. data/lib/ruby_jard/screen_manager.rb +157 -56
  33. data/lib/ruby_jard/screens/backtrace_screen.rb +88 -97
  34. data/lib/ruby_jard/screens/menu_screen.rb +23 -31
  35. data/lib/ruby_jard/screens/source_screen.rb +42 -90
  36. data/lib/ruby_jard/screens/threads_screen.rb +50 -64
  37. data/lib/ruby_jard/screens/variables_screen.rb +96 -99
  38. data/lib/ruby_jard/session.rb +13 -7
  39. data/lib/ruby_jard/span.rb +18 -0
  40. data/lib/ruby_jard/templates/column_template.rb +17 -0
  41. data/lib/ruby_jard/templates/layout_template.rb +35 -0
  42. data/lib/ruby_jard/templates/row_template.rb +22 -0
  43. data/lib/ruby_jard/templates/screen_template.rb +35 -0
  44. data/lib/ruby_jard/templates/space_template.rb +15 -0
  45. data/lib/ruby_jard/templates/span_template.rb +25 -0
  46. data/lib/ruby_jard/version.rb +1 -1
  47. data/ruby_jard.gemspec +1 -4
  48. metadata +29 -41
  49. data/lib/ruby_jard/commands/finish_command.rb +0 -31
  50. data/lib/ruby_jard/decorators/text_decorator.rb +0 -61
  51. data/lib/ruby_jard/layout_template.rb +0 -101
  52. data/lib/ruby_jard/screens/breakpoints_screen.rb +0 -23
  53. data/lib/ruby_jard/screens/expressions_sreen.rb +0 -22
@@ -2,133 +2,116 @@
2
2
 
3
3
  module RubyJard
4
4
  module Screens
5
+ ##
6
+ # Backtrace screen implements the content to display current thread's backtrace to the user.
5
7
  class BacktraceScreen < RubyJard::Screen
6
- def draw
7
- @output.print TTY::Box.frame(**frame_styles)
8
-
9
- @output.print TTY::Cursor.move_to(@col + 2, @row)
10
- @output.print decorate_text
11
- .with_highlight(true)
12
- .text(" Backtrace (#{frames_count}) ", :bright_yellow)
13
- .content
14
-
15
- decorate_frames.each_with_index do |frame_texts, index|
16
- left, right = frame_texts
17
- @output.print TTY::Cursor.move_to(@col + 1, @row + index + 1)
18
- @output.print left.content
19
-
20
- next unless @col + left.length < @col + @layout.width - right.length - 1
21
-
22
- # TODO: handle reducable components in case of smaller screen
23
- @output.print TTY::Cursor.move_to(@col + @layout.width - right.length, @row + index + 1)
24
- @output.print right.content
25
- end
8
+ def title
9
+ "Backtrace (#{frames_count})"
26
10
  end
27
11
 
28
- private
29
-
30
12
  def data_size
31
- @layout.height - 1
32
- end
33
-
34
- def frame_styles
35
- default_frame_styles.merge(
36
- top: @row, left: @col, width: @layout.width, height: @layout.height
37
- )
13
+ [@height, frames_count].min
38
14
  end
39
15
 
40
- def decorate_frames
16
+ def data_window
41
17
  return [] if data_size.zero?
42
18
 
43
- window_start = frame_pos / data_size * data_size
44
- window_end = [frames_count, window_start + data_size - 1].min
45
-
46
- backtrace[window_start..window_end]
47
- .map
48
- .with_index do |frame, frame_index|
49
- decorate_frame(frame, window_start + frame_index, window_start, window_end)
50
- end
19
+ @data_window ||= backtrace[data_window_start..data_window_end]
51
20
  end
52
21
 
53
- def decorate_frame(line, frame_id, window_start, window_end)
54
- location = line[0]
55
- object = line[1]
56
- klass = line[2]
57
-
58
- left =
59
- decorate_frame_id(frame_id, window_start, window_end) +
60
- ' ' +
61
- decorate_location_label(frame_id, location, object, klass)
62
- right = decorate_location_path(frame_id, location)
22
+ def data_window_start
23
+ return 0 if data_size.zero?
63
24
 
64
- [left, right]
25
+ current_frame / data_size * data_size
65
26
  end
66
27
 
67
- def reset
68
- @color = Pastel.new
28
+ def data_window_end
29
+ [frames_count, data_window_start + data_size - 1].min
69
30
  end
70
31
 
71
- def decorate_frame_id(frame_id, _window_start, window_end)
72
- decorate_text
73
- .with_highlight(frame_pos == frame_id)
74
- .text(frame_pos == frame_id ? '→ ' : ' ', :white)
75
- .text(frame_id.to_s.ljust(window_end.to_s.length), frame_pos == frame_id ? :bright_yellow : :white)
32
+ def span_mark(_frame, index)
33
+ [
34
+ current_frame?(index) ? '→' : ' ',
35
+ [:bright_yellow, current_frame?(index) ? :bold : nil]
36
+ ]
76
37
  end
77
38
 
78
- def decorate_location_label(frame_id, location, object, klass)
79
- decorate_text
80
- .with_highlight(frame_pos == frame_id)
81
- .text(backtrace[frame_id].last.nil? ? '[c] ' : '', :green)
82
- .text(decorate_object_label(object, klass), :green)
83
- .text(' in ', :white)
84
- .text(decorate_method_label(location), :green)
39
+ def span_frame_id(_frame, index)
40
+ frame_id = index + data_window_start
41
+ [
42
+ frame_id.to_s,
43
+ [
44
+ current_frame?(index) ? :bright_yellow : :white,
45
+ current_frame?(index) ? :bold : nil
46
+ ]
47
+ ]
85
48
  end
86
49
 
87
- def decorate_object_label(object, klass)
88
- if klass.nil? || object.class == klass
89
- if object.is_a?(Class)
50
+ def span_klass_label(frame, index)
51
+ object = frame[1]
52
+ klass = frame[2]
53
+ klass_label =
54
+ if klass.nil? || object.class == klass
55
+ if object.is_a?(Class)
56
+ object.name
57
+ else
58
+ object.class.name
59
+ end
60
+ elsif klass.singleton_class?
61
+ # No easy way to get the original class of a singleton class
90
62
  object.name
91
63
  else
92
- object.class.name
64
+ klass.name
93
65
  end
94
- elsif klass.singleton_class?
95
- # No easy way to get the original class of a singleton class
96
- object.name
97
- else
98
- klass.name
99
- end
66
+ c_frame = frame_at(index).last.nil? ? '[c] ' : ''
67
+ [
68
+ "#{c_frame}#{klass_label}",
69
+ [:green, current_frame?(index) ? :bold : nil]
70
+ ]
100
71
  end
101
72
 
102
- def decorate_method_label(location)
103
- if location.label != location.base_label
104
- "#{location.base_label} (#{location.label.split(' ').first})"
105
- else
106
- location.base_label
107
- end
73
+ def span_label_preposition(_frame, index)
74
+ ['in', current_frame?(index) ? [:bright_white] : [:white]]
108
75
  end
109
76
 
110
- def decorate_location_path(frame_id, location)
77
+ def span_method_label(frame, index)
78
+ location = frame[0]
79
+ method_label =
80
+ if location.label != location.base_label
81
+ "#{location.base_label} (#{location.label.split(' ').first})"
82
+ else
83
+ location.base_label
84
+ end
85
+ [method_label, [:green, current_frame?(index) ? :bold : nil]]
86
+ end
87
+
88
+ def span_path_preposition(frame, index)
89
+ location = frame[0]
111
90
  decorated_path = decorate_path(location.absolute_path, location.lineno)
91
+ preposition = decorated_path.gem? ? 'in' : 'at'
92
+ [preposition, current_frame?(index) ? [:bright_white] : [:white]]
93
+ end
112
94
 
113
- if decorated_path.gem?
114
- decorate_text
115
- .with_highlight(frame_pos == frame_id)
116
- .text('in ', :bright_white)
117
- .text(decorated_path.gem, :bright_white)
118
- .text(' (', :bright_white)
119
- .text(decorated_path.gem_version, :bright_white)
120
- .text(')', :bright_white)
121
- else
122
- decorate_text
123
- .with_highlight(frame_pos == frame_id)
124
- .text('at ', :bright_white)
125
- .text(decorated_path.path, :bright_white)
126
- .text(':', :bright_white)
127
- .text(decorated_path.lineno, :bright_white)
128
- end
95
+ def span_path(frame, index)
96
+ location = frame[0]
97
+ decorated_path = decorate_path(location.absolute_path, location.lineno)
98
+
99
+ path_label =
100
+ if decorated_path.gem?
101
+ "#{decorated_path.gem} (#{decorated_path.gem_version})"
102
+ else
103
+ "#{decorated_path.path}:#{decorated_path.lineno}"
104
+ end
105
+ [path_label, current_frame?(index) ? [:bold, :bright_white] : [:white]]
129
106
  end
130
107
 
131
- def frame_pos
108
+ private
109
+
110
+ def current_frame?(index)
111
+ index + data_window_start == current_frame
112
+ end
113
+
114
+ def current_frame
132
115
  if @session.frame.nil?
133
116
  0
134
117
  else
@@ -136,6 +119,10 @@ module RubyJard
136
119
  end
137
120
  end
138
121
 
122
+ def frame_at(index)
123
+ backtrace[index + data_window_start]
124
+ end
125
+
139
126
  def frames_count
140
127
  @session.backtrace.length
141
128
  end
@@ -143,6 +130,10 @@ module RubyJard
143
130
  def backtrace
144
131
  @session.backtrace
145
132
  end
133
+
134
+ def decorate_path(path, lineno)
135
+ RubyJard::Decorators::PathDecorator.new(path, lineno)
136
+ end
146
137
  end
147
138
  end
148
139
  end
@@ -3,38 +3,23 @@
3
3
  module RubyJard
4
4
  module Screens
5
5
  class MenuScreen < RubyJard::Screen
6
- def draw
7
- @output.print TTY::Cursor.move_to(@col, @row)
8
- frame = TTY::Box.frame(
9
- **default_frame_styles.merge(
10
- top: @row, left: @col, width: @layout.width, height: @layout.height,
11
- border: {
12
- left: false,
13
- top: :line,
14
- right: false,
15
- bottom: false
16
- },
17
- style: {
18
- fg: :white
19
- }
20
- )
21
- )
22
- @output.print frame
6
+ def draw(output)
7
+ RubyJard::Console.move_to(output, @x, @y)
23
8
 
24
9
  margin = 0
25
10
  left_menu = generate_left_menu
26
- left_menu.each do |item|
27
- @output.print TTY::Cursor.move_to(@col + 1 + margin, @row + 1)
28
- @output.print item.content
29
- margin += item.length + 3
11
+ left_menu.each do |text, length|
12
+ RubyJard::Console.move_to(output, @x + 1 + margin, @y)
13
+ output.print text
14
+ margin += length + 3
30
15
  end
31
16
 
32
17
  margin = 0
33
18
  right_menu = generate_right_menu
34
- right_menu.reverse.each do |item|
35
- @output.print TTY::Cursor.move_to(@col + @layout.width - margin - item.length - 1, @row + 1)
36
- @output.print item.content
37
- margin += item.length + 3
19
+ right_menu.reverse.each do |text, length|
20
+ RubyJard::Console.move_to(output, @x + @width - margin - length - 1, @y)
21
+ output.print text
22
+ margin += length + 3
38
23
  end
39
24
  end
40
25
 
@@ -42,19 +27,26 @@ module RubyJard
42
27
 
43
28
  def generate_left_menu
44
29
  [
45
- decorate_text.with_highlight(true).text('Debug console (F5)', :bright_yellow),
46
- decorate_text.text('Program output (F6)', :white)
30
+ # Fill in later
47
31
  ]
48
32
  end
49
33
 
50
34
  def generate_right_menu
51
35
  [
52
- decorate_text.text('Step (F7)', :white),
53
- decorate_text.text('Next (F8)', :white),
54
- decorate_text.text('Step out (Shift+F8)', :white),
55
- decorate_text.text('Continue (F9)', :white)
36
+ decorate_text('Step (F7)', :white),
37
+ decorate_text('Step Out (Shift+F7)', :white),
38
+ decorate_text('Next (F8)', :white),
39
+ decorate_text('Continue (F9)', :white)
56
40
  ]
57
41
  end
42
+
43
+ def decorate_text(str, *styles)
44
+ [color_decorator.decorate(str, *styles), str.length]
45
+ end
46
+
47
+ def color_decorator
48
+ @color_decorator ||= RubyJard::Decorators::ColorDecorator.new
49
+ end
58
50
  end
59
51
  end
60
52
  end
@@ -3,75 +3,50 @@
3
3
  module RubyJard
4
4
  module Screens
5
5
  class SourceScreen < RubyJard::Screen
6
- def draw
7
- @output.print TTY::Box.frame(
8
- **default_frame_styles.merge(
9
- top: @row, left: @col, width: @layout.width, height: @layout.height
10
- )
11
- )
12
-
13
- @output.print TTY::Cursor.move_to(@col + 2, @row)
14
- @output.print decorate_text
15
- .with_highlight(true)
16
- .text(' Source', :bright_yellow)
17
- .text(' (', :bright_yellow)
18
- .text(file_path, :bright_yellow)
19
- .text(') ', :bright_yellow)
20
- .content
21
-
22
- decorate_codes.each_with_index do |decorated_loc, index|
23
- @output.print TTY::Cursor.move_to(@col + 1, @row + 1 + index)
24
- @output.print decorated_loc.content
6
+ def title
7
+ return 'Source' if RubyJard.current_session.frame.nil?
8
+
9
+ decorated_path = path_decorator(current_file, current_line)
10
+ if decorated_path.gem?
11
+ "Source (#{decorated_path.gem} - #{decorated_path.path}:#{decorated_path.lineno})"
12
+ else
13
+ "Source (#{decorated_path.path}:#{decorated_path.lineno})"
25
14
  end
26
15
  end
27
16
 
28
- private
29
-
30
17
  def data_size
31
- @layout.height - 1
18
+ @height
32
19
  end
33
20
 
34
- def decorate_codes
21
+ def data_window
35
22
  return [] if RubyJard.current_session.frame.nil?
36
23
 
37
- decorated_source = decorate_source(current_file, current_line, data_size)
38
-
39
- lineno_padding = decorated_source.window_end.to_s.length
40
-
41
- decorated_source.codes.map.with_index do |loc, index|
42
- lineno = decorated_source.window_start + index
43
- decorated_loc = decorate_loc(loc, current_line == lineno)
44
-
45
- if current_line == lineno
46
- decorate_text
47
- .with_highlight(true)
48
- .text('→ ')
49
- .text(lineno.to_s.ljust(lineno_padding), :bright_yellow)
50
- .text(' ')
51
- .text(decorated_loc.loc)
52
- .text(inline_variables(decorated_loc.tokens))
53
- else
54
- decorate_text
55
- .with_highlight(false)
56
- .text(' ')
57
- .text(lineno.to_s.ljust(lineno_padding), :white)
58
- .text(' ')
59
- .text(decorated_loc.loc)
60
- end
61
- end
24
+ @data_window ||= source_decorator.codes
62
25
  end
63
26
 
64
- def file_path
65
- return '' if RubyJard.current_session.frame.nil?
27
+ def span_mark(_loc, index)
28
+ lineno = source_lineno(index)
29
+ [
30
+ current_line == lineno ? '→' : ' ',
31
+ [:bright_yellow, current_line == lineno ? :bold : nil]
32
+ ]
33
+ end
66
34
 
67
- decorated_path = decorate_path(current_file, current_line)
68
- if decorated_path.gem?
69
- "#{decorated_path.gem}: #{decorated_path.path}:#{decorated_path.lineno}"
70
- else
71
- "#{decorated_path.path}:#{decorated_path.lineno}"
72
- end
35
+ def span_lineno(_loc, index)
36
+ lineno = source_lineno(index)
37
+ [
38
+ lineno.to_s,
39
+ current_line == lineno ? [:bold, :bright_yellow] : [:dim, :white]
40
+ ]
41
+ end
42
+
43
+ def span_code(loc, index)
44
+ lineno = source_lineno(index)
45
+ [loc_decorator(loc).spans, current_line == lineno ? [:brighter] : [:dim]]
73
46
  end
74
47
 
48
+ private
49
+
75
50
  def current_binding
76
51
  RubyJard.current_session.frame._binding
77
52
  end
@@ -88,43 +63,20 @@ module RubyJard
88
63
  RubyJard.current_session.frame.line
89
64
  end
90
65
 
91
- def inline_variables(tokens)
92
- variables = {}
93
- local_variables = current_binding.local_variables
94
- instance_variables = current_frame_scope.instance_variables
95
-
96
- tokens.each_slice(2).each do |token, kind|
97
- token = token.to_sym
98
-
99
- if kind == :ident && local_variables.include?(token)
100
- var = current_binding.local_variable_get(token)
101
- elsif kind == :instance_variable && instance_variables.include?(token)
102
- var = current_frame_scope.instance_variable_get(token)
103
- else
104
- next
105
- end
106
-
107
- next if variables.key?(token)
108
-
109
- var_inspect = var.inspect
110
- # TODO: dynamic fill the rest of the line instead
111
- variables[token] = var_inspect if var_inspect.length < 30
112
- end
113
-
114
- return '' if variables.empty?
66
+ def path_decorator(path, lineno)
67
+ @path_decorator ||= RubyJard::Decorators::PathDecorator.new(path, lineno)
68
+ end
115
69
 
116
- variables_text = decorate_text.with_highlight(false).text(' #→ ', :white)
117
- variables.to_a.each_with_index do |(var_name, var_inspect), index|
118
- variables_text
119
- .with_highlight(false)
120
- .text(var_name.to_s, :white)
121
- .text('=', :white)
122
- .text(var_inspect, :white)
70
+ def source_decorator
71
+ @source_decorator ||= RubyJard::Decorators::SourceDecorator.new(current_file, current_line, data_size)
72
+ end
123
73
 
124
- variables_text.with_highlight(false).text(', ', :white) if index != variables.length - 1
125
- end
74
+ def loc_decorator(loc)
75
+ RubyJard::Decorators::LocDecorator.new(current_file, loc)
76
+ end
126
77
 
127
- variables_text
78
+ def source_lineno(index)
79
+ source_decorator.window_start + index
128
80
  end
129
81
  end
130
82
  end