ruby_jard 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +40 -0
- data/Gemfile +1 -1
- data/README.md +65 -2
- data/docs/guide-ui.png +0 -0
- data/lib/ruby_jard.rb +49 -12
- data/lib/ruby_jard/box_drawer.rb +126 -0
- data/lib/ruby_jard/column.rb +18 -0
- data/lib/ruby_jard/commands/continue_command.rb +1 -6
- data/lib/ruby_jard/commands/down_command.rb +1 -4
- data/lib/ruby_jard/commands/frame_command.rb +12 -11
- data/lib/ruby_jard/commands/next_command.rb +1 -4
- data/lib/ruby_jard/commands/step_command.rb +1 -4
- data/lib/ruby_jard/commands/step_out_command.rb +28 -0
- data/lib/ruby_jard/commands/up_command.rb +1 -4
- data/lib/ruby_jard/console.rb +86 -0
- data/lib/ruby_jard/control_flow.rb +71 -0
- data/lib/ruby_jard/decorators/color_decorator.rb +78 -0
- data/lib/ruby_jard/decorators/loc_decorator.rb +41 -28
- data/lib/ruby_jard/decorators/source_decorator.rb +1 -1
- data/lib/ruby_jard/key_binding.rb +14 -0
- data/lib/ruby_jard/key_bindings.rb +96 -0
- data/lib/ruby_jard/keys.rb +49 -0
- data/lib/ruby_jard/layout.rb +67 -55
- data/lib/ruby_jard/layouts/wide_layout.rb +138 -0
- data/lib/ruby_jard/repl_processor.rb +80 -90
- data/lib/ruby_jard/repl_proxy.rb +232 -0
- data/lib/ruby_jard/row.rb +16 -0
- data/lib/ruby_jard/screen.rb +114 -36
- data/lib/ruby_jard/screen_drawer.rb +89 -0
- data/lib/ruby_jard/screen_manager.rb +157 -56
- data/lib/ruby_jard/screens/backtrace_screen.rb +88 -97
- data/lib/ruby_jard/screens/menu_screen.rb +23 -31
- data/lib/ruby_jard/screens/source_screen.rb +42 -90
- data/lib/ruby_jard/screens/threads_screen.rb +50 -64
- data/lib/ruby_jard/screens/variables_screen.rb +96 -99
- data/lib/ruby_jard/session.rb +13 -7
- data/lib/ruby_jard/span.rb +18 -0
- data/lib/ruby_jard/templates/column_template.rb +17 -0
- data/lib/ruby_jard/templates/layout_template.rb +35 -0
- data/lib/ruby_jard/templates/row_template.rb +22 -0
- data/lib/ruby_jard/templates/screen_template.rb +35 -0
- data/lib/ruby_jard/templates/space_template.rb +15 -0
- data/lib/ruby_jard/templates/span_template.rb +25 -0
- data/lib/ruby_jard/version.rb +1 -1
- data/ruby_jard.gemspec +1 -4
- metadata +29 -41
- data/lib/ruby_jard/commands/finish_command.rb +0 -31
- data/lib/ruby_jard/decorators/text_decorator.rb +0 -61
- data/lib/ruby_jard/layout_template.rb +0 -101
- data/lib/ruby_jard/screens/breakpoints_screen.rb +0 -23
- 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
|
7
|
-
|
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
|
-
@
|
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
|
16
|
+
def data_window
|
41
17
|
return [] if data_size.zero?
|
42
18
|
|
43
|
-
|
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
|
54
|
-
|
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
|
-
|
25
|
+
current_frame / data_size * data_size
|
65
26
|
end
|
66
27
|
|
67
|
-
def
|
68
|
-
|
28
|
+
def data_window_end
|
29
|
+
[frames_count, data_window_start + data_size - 1].min
|
69
30
|
end
|
70
31
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
88
|
-
|
89
|
-
|
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
|
-
|
64
|
+
klass.name
|
93
65
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
103
|
-
|
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
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
.
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
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
|
-
|
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 |
|
27
|
-
|
28
|
-
|
29
|
-
margin +=
|
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 |
|
35
|
-
|
36
|
-
|
37
|
-
margin +=
|
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
|
-
|
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
|
53
|
-
decorate_text
|
54
|
-
decorate_text
|
55
|
-
decorate_text
|
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
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
@
|
18
|
+
@height
|
32
19
|
end
|
33
20
|
|
34
|
-
def
|
21
|
+
def data_window
|
35
22
|
return [] if RubyJard.current_session.frame.nil?
|
36
23
|
|
37
|
-
|
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
|
65
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
92
|
-
|
93
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
125
|
-
|
74
|
+
def loc_decorator(loc)
|
75
|
+
RubyJard::Decorators::LocDecorator.new(current_file, loc)
|
76
|
+
end
|
126
77
|
|
127
|
-
|
78
|
+
def source_lineno(index)
|
79
|
+
source_decorator.window_start + index
|
128
80
|
end
|
129
81
|
end
|
130
82
|
end
|