ruby_jard 0.1.0 → 0.3.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/.github/FUNDING.yml +3 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/documentation.yml +65 -0
- data/.github/workflows/rspec.yml +96 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +90 -2
- data/CHANGELOG.md +112 -0
- data/Gemfile +14 -4
- data/README.md +95 -3
- data/benchmark/path_filter_bench.rb +58 -0
- data/bin/console +1 -2
- data/lib/ruby_jard.rb +68 -32
- data/lib/ruby_jard/box_drawer.rb +175 -0
- data/lib/ruby_jard/color_scheme.rb +28 -0
- data/lib/ruby_jard/color_schemes.rb +54 -0
- data/lib/ruby_jard/color_schemes/256_color_scheme.rb +50 -0
- data/lib/ruby_jard/color_schemes/256_light_color_scheme.rb +50 -0
- data/lib/ruby_jard/color_schemes/deep_space_color_scheme.rb +49 -0
- data/lib/ruby_jard/color_schemes/gruvbox_color_scheme.rb +48 -0
- data/lib/ruby_jard/color_schemes/one_half_dark_color_scheme.rb +47 -0
- data/lib/ruby_jard/color_schemes/one_half_light_color_scheme.rb +49 -0
- data/lib/ruby_jard/column.rb +26 -0
- data/lib/ruby_jard/commands/color_helpers.rb +32 -0
- data/lib/ruby_jard/commands/continue_command.rb +4 -9
- data/lib/ruby_jard/commands/down_command.rb +9 -8
- data/lib/ruby_jard/commands/exit_command.rb +27 -0
- data/lib/ruby_jard/commands/frame_command.rb +13 -11
- data/lib/ruby_jard/commands/jard/color_scheme_command.rb +74 -0
- data/lib/ruby_jard/commands/jard/filter_command.rb +136 -0
- data/lib/ruby_jard/commands/jard/hide_command.rb +40 -0
- data/lib/ruby_jard/commands/jard/output_command.rb +36 -0
- data/lib/ruby_jard/commands/jard/show_command.rb +41 -0
- data/lib/ruby_jard/commands/jard_command.rb +52 -0
- data/lib/ruby_jard/commands/list_command.rb +31 -0
- data/lib/ruby_jard/commands/next_command.rb +11 -8
- data/lib/ruby_jard/commands/step_command.rb +11 -8
- data/lib/ruby_jard/commands/step_out_command.rb +34 -0
- data/lib/ruby_jard/commands/up_command.rb +10 -8
- data/lib/ruby_jard/commands/validation_helpers.rb +50 -0
- data/lib/ruby_jard/config.rb +61 -0
- data/lib/ruby_jard/console.rb +158 -0
- data/lib/ruby_jard/control_flow.rb +73 -0
- data/lib/ruby_jard/decorators/array_decorator.rb +79 -0
- data/lib/ruby_jard/decorators/attributes_decorator.rb +172 -0
- data/lib/ruby_jard/decorators/color_decorator.rb +80 -0
- data/lib/ruby_jard/decorators/hash_decorator.rb +74 -0
- data/lib/ruby_jard/decorators/inspection_decorator.rb +109 -0
- data/lib/ruby_jard/decorators/loc_decorator.rb +108 -119
- data/lib/ruby_jard/decorators/object_decorator.rb +122 -0
- data/lib/ruby_jard/decorators/path_decorator.rb +56 -60
- data/lib/ruby_jard/decorators/rails_decorator.rb +194 -0
- data/lib/ruby_jard/decorators/source_decorator.rb +3 -1
- data/lib/ruby_jard/decorators/string_decorator.rb +41 -0
- data/lib/ruby_jard/decorators/struct_decorator.rb +79 -0
- data/lib/ruby_jard/frame.rb +68 -0
- data/lib/ruby_jard/key_binding.rb +14 -0
- data/lib/ruby_jard/key_bindings.rb +96 -0
- data/lib/ruby_jard/keys.rb +48 -0
- data/lib/ruby_jard/layout.rb +17 -88
- data/lib/ruby_jard/layout_calculator.rb +168 -0
- data/lib/ruby_jard/layout_picker.rb +34 -0
- data/lib/ruby_jard/layouts.rb +52 -0
- data/lib/ruby_jard/layouts/narrow_horizontal_layout.rb +32 -0
- data/lib/ruby_jard/layouts/narrow_vertical_layout.rb +32 -0
- data/lib/ruby_jard/layouts/tiny_layout.rb +29 -0
- data/lib/ruby_jard/layouts/wide_layout.rb +50 -0
- data/lib/ruby_jard/pager.rb +112 -0
- data/lib/ruby_jard/path_classifier.rb +133 -0
- data/lib/ruby_jard/path_filter.rb +125 -0
- data/lib/ruby_jard/reflection.rb +97 -0
- data/lib/ruby_jard/repl_processor.rb +151 -89
- data/lib/ruby_jard/repl_proxy.rb +337 -0
- data/lib/ruby_jard/row.rb +31 -0
- data/lib/ruby_jard/row_renderer.rb +119 -0
- data/lib/ruby_jard/screen.rb +14 -41
- data/lib/ruby_jard/screen_adjuster.rb +104 -0
- data/lib/ruby_jard/screen_drawer.rb +25 -0
- data/lib/ruby_jard/screen_manager.rb +167 -82
- data/lib/ruby_jard/screen_renderer.rb +152 -0
- data/lib/ruby_jard/screens.rb +31 -12
- data/lib/ruby_jard/screens/backtrace_screen.rb +118 -116
- data/lib/ruby_jard/screens/menu_screen.rb +73 -45
- data/lib/ruby_jard/screens/source_screen.rb +86 -106
- data/lib/ruby_jard/screens/threads_screen.rb +103 -78
- data/lib/ruby_jard/screens/variables_screen.rb +224 -142
- data/lib/ruby_jard/session.rb +151 -16
- data/lib/ruby_jard/span.rb +23 -0
- data/lib/ruby_jard/templates/layout_template.rb +35 -0
- data/lib/ruby_jard/templates/screen_template.rb +34 -0
- data/lib/ruby_jard/thread_info.rb +69 -0
- data/lib/ruby_jard/version.rb +1 -1
- data/ruby_jard.gemspec +7 -8
- metadata +84 -50
- data/.travis.yml +0 -6
- 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/empty_screen.rb +0 -13
- data/lib/ruby_jard/screens/expressions_sreen.rb +0 -22
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
##
|
5
|
+
# Adjust the layout between screens, render row's bitmap by calling RowRenderer,
|
6
|
+
# calculate screen window to ready for putting on the screen.
|
7
|
+
class ScreenRenderer
|
8
|
+
def initialize(screen:, color_scheme:)
|
9
|
+
@screen = screen
|
10
|
+
@color_scheme = color_scheme
|
11
|
+
end
|
12
|
+
|
13
|
+
def render
|
14
|
+
# Move this logic into a class called SreenRenderer
|
15
|
+
calculate_content_lengths
|
16
|
+
column_widths = calculate_column_widths
|
17
|
+
adjust_column_widths(column_widths)
|
18
|
+
calculate_window
|
19
|
+
|
20
|
+
@screen
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def calculate_content_lengths
|
26
|
+
@screen.rows.each do |row|
|
27
|
+
row.columns.each do |column|
|
28
|
+
column.content_length = column.spans.map(&:content_length).inject(&:+) || 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def calculate_column_widths
|
34
|
+
column_widths = {}
|
35
|
+
total_columns = count_columns
|
36
|
+
|
37
|
+
return column_widths if total_columns == 0
|
38
|
+
|
39
|
+
ideal_column_width = @screen.layout.width / total_columns
|
40
|
+
total_columns.times do |column_index|
|
41
|
+
column_widths[column_index] ||= 0
|
42
|
+
@screen.rows.each do |row|
|
43
|
+
column = row.columns[column_index]
|
44
|
+
if column.content_length > ideal_column_width - 1
|
45
|
+
column_widths[column_index] = nil
|
46
|
+
break
|
47
|
+
elsif column.content_length + 1 > column_widths[column_index]
|
48
|
+
column_widths[column_index] = column.content_length + 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
column_widths
|
53
|
+
end
|
54
|
+
|
55
|
+
def adjust_column_widths(column_widths)
|
56
|
+
dynamic_count = count_dynamic_columns(column_widths)
|
57
|
+
fixed_width = sum_fixed_width(column_widths)
|
58
|
+
|
59
|
+
@screen.rows.each do |row|
|
60
|
+
total_width = 0
|
61
|
+
row.columns.each_with_index do |column, column_index|
|
62
|
+
column.width =
|
63
|
+
if column_index == row.columns.length - 1
|
64
|
+
@screen.layout.width - total_width
|
65
|
+
elsif column_widths[column_index].nil?
|
66
|
+
(@screen.layout.width - fixed_width) / dynamic_count
|
67
|
+
else
|
68
|
+
column_widths[column_index]
|
69
|
+
end
|
70
|
+
column.content_width = column.width
|
71
|
+
column.content_width -= 1 if column_index < row.columns.length - 1
|
72
|
+
|
73
|
+
total_width += column.width
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
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
|
+
|
88
|
+
def render_rows
|
89
|
+
@screen.rows.each do |row|
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def calculate_window
|
94
|
+
@screen.window = []
|
95
|
+
|
96
|
+
if @screen.cursor.nil?
|
97
|
+
find_seleted_window
|
98
|
+
else
|
99
|
+
find_cursor_window
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def find_seleted_window
|
104
|
+
@screen.rows.each_with_index do |row, row_index|
|
105
|
+
row_content(row).each_with_index do |line, line_index|
|
106
|
+
if @screen.window.length < @screen.layout.height
|
107
|
+
@screen.window << line
|
108
|
+
elsif row_index < @screen.selected
|
109
|
+
@screen.window = [line]
|
110
|
+
elsif row_index == @screen.selected
|
111
|
+
if line_index != 0
|
112
|
+
@screen.window.shift
|
113
|
+
@screen.window << line
|
114
|
+
else
|
115
|
+
@screen.window = [line]
|
116
|
+
end
|
117
|
+
else
|
118
|
+
return
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_cursor_window
|
125
|
+
cursor_line = -1
|
126
|
+
@screen.rows.each do |row|
|
127
|
+
row_content(row).each do |line|
|
128
|
+
cursor_line += 1
|
129
|
+
@screen.window << line if cursor_line >= @screen.cursor
|
130
|
+
return if @screen.window.length >= @screen.layout.height
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def count_columns
|
136
|
+
@screen.rows.map { |row| row.columns.count }.max.to_i
|
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
|
151
|
+
end
|
152
|
+
end
|
data/lib/ruby_jard/screens.rb
CHANGED
@@ -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
|
-
|
8
|
+
class Screens
|
7
9
|
class << self
|
8
|
-
|
9
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
18
|
+
def initialize
|
19
|
+
@screen_registry = {}
|
20
|
+
end
|
16
21
|
|
17
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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'
|
@@ -1,150 +1,152 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RubyJard
|
4
|
-
|
4
|
+
class 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
|
-
@
|
10
|
-
|
11
|
-
.
|
12
|
-
.
|
13
|
-
.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
def initialize(*args)
|
9
|
+
super(*args)
|
10
|
+
@current_frame = @session.current_frame
|
11
|
+
@frames =
|
12
|
+
@session
|
13
|
+
.current_backtrace
|
14
|
+
.select(&:visible?)
|
15
|
+
.sort { |a, b| a.virtual_pos.to_i <=> b.virtual_pos.to_i }
|
16
|
+
insert_current_frame
|
17
|
+
@selected =
|
18
|
+
if @current_frame.nil?
|
19
|
+
0
|
20
|
+
else
|
21
|
+
@frames.find_index { |f| f.real_pos == @current_frame.real_pos }
|
22
|
+
end
|
23
|
+
@frames_count = @frames.length
|
24
|
+
@hidden_frames_count = @session.current_backtrace.length - @frames.length
|
21
25
|
|
22
|
-
|
23
|
-
@output.print TTY::Cursor.move_to(@col + @layout.width - right.length, @row + index + 1)
|
24
|
-
@output.print right.content
|
25
|
-
end
|
26
|
+
@path_decorator = RubyJard::Decorators::PathDecorator.new
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def title
|
30
|
+
if @hidden_frames_count <= 0
|
31
|
+
['Backtrace', "#{@frames_count} frames"]
|
32
|
+
else
|
33
|
+
['Backtrace', "#{@frames_count} frames - #{@hidden_frames_count} hidden"]
|
34
|
+
end
|
32
35
|
end
|
33
36
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
def build
|
38
|
+
@rows = @frames.map do |frame|
|
39
|
+
RubyJard::Row.new(
|
40
|
+
line_limit: 2,
|
41
|
+
columns: [
|
42
|
+
RubyJard::Column.new(
|
43
|
+
spans: [
|
44
|
+
span_frame_pos(frame)
|
45
|
+
]
|
46
|
+
),
|
47
|
+
RubyJard::Column.new(
|
48
|
+
spans: [
|
49
|
+
span_class_label(frame),
|
50
|
+
span_label_preposition,
|
51
|
+
span_method_label(frame),
|
52
|
+
span_path(frame)
|
53
|
+
]
|
54
|
+
)
|
55
|
+
]
|
56
|
+
)
|
57
|
+
end
|
38
58
|
end
|
39
59
|
|
40
|
-
|
41
|
-
return [] if data_size.zero?
|
42
|
-
|
43
|
-
window_start = frame_pos / data_size * data_size
|
44
|
-
window_end = [frames_count, window_start + data_size - 1].min
|
60
|
+
private
|
45
61
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
62
|
+
def span_frame_pos(frame)
|
63
|
+
frame_pos_label =
|
64
|
+
if frame.hidden?
|
65
|
+
'*'.rjust(@frames_count.to_s.length)
|
66
|
+
else
|
67
|
+
frame.virtual_pos.to_s.rjust(@frames_count.to_s.length)
|
50
68
|
end
|
69
|
+
if frame.real_pos == @current_frame.real_pos
|
70
|
+
RubyJard::Span.new(
|
71
|
+
content: "⮕ #{frame_pos_label}",
|
72
|
+
styles: :text_selected
|
73
|
+
)
|
74
|
+
else
|
75
|
+
RubyJard::Span.new(
|
76
|
+
content: " #{frame_pos_label}",
|
77
|
+
styles: :text_dim
|
78
|
+
)
|
79
|
+
end
|
51
80
|
end
|
52
81
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@color = Pastel.new
|
69
|
-
end
|
82
|
+
def span_class_label(frame)
|
83
|
+
self_class = RubyJard::Reflection.call_class(frame.frame_self)
|
84
|
+
class_label =
|
85
|
+
if frame.frame_class.nil? || self_class == frame.frame_class
|
86
|
+
if ::RubyJard::Reflection.call_is_a?(frame.frame_self, Class)
|
87
|
+
frame.frame_self.name
|
88
|
+
else
|
89
|
+
self_class.name
|
90
|
+
end
|
91
|
+
elsif frame.frame_class.singleton_class?
|
92
|
+
# No easy way to get the original class of a singleton class
|
93
|
+
frame.frame_self.respond_to?(:name) ? frame.frame_self.name : frame.frame_self.to_s
|
94
|
+
else
|
95
|
+
frame.frame_class.name
|
96
|
+
end
|
70
97
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
98
|
+
c_frame = frame.c_frame? ? '[c] ' : ''
|
99
|
+
RubyJard::Span.new(
|
100
|
+
content: "#{c_frame}#{class_label}",
|
101
|
+
margin_right: 1,
|
102
|
+
styles: :constant
|
103
|
+
)
|
76
104
|
end
|
77
105
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
.text(decorate_method_label(location), :green)
|
106
|
+
def span_label_preposition
|
107
|
+
RubyJard::Span.new(
|
108
|
+
content: 'in',
|
109
|
+
margin_right: 1,
|
110
|
+
styles: :text_primary
|
111
|
+
)
|
85
112
|
end
|
86
113
|
|
87
|
-
def
|
88
|
-
|
89
|
-
if
|
90
|
-
|
114
|
+
def span_method_label(frame)
|
115
|
+
method_label =
|
116
|
+
if frame.frame_location.label != frame.frame_location.base_label
|
117
|
+
"#{frame.frame_location.base_label} (#{frame.frame_location.label.split(' ').first})"
|
91
118
|
else
|
92
|
-
|
119
|
+
frame.frame_location.base_label
|
93
120
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end
|
121
|
+
RubyJard::Span.new(
|
122
|
+
content: method_label,
|
123
|
+
margin_right: 1,
|
124
|
+
styles: :method
|
125
|
+
)
|
100
126
|
end
|
101
127
|
|
102
|
-
def
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
128
|
+
def span_path(frame)
|
129
|
+
path_label, = @path_decorator.decorate(
|
130
|
+
frame.frame_location.path, frame.frame_location.lineno
|
131
|
+
)
|
132
|
+
RubyJard::Span.new(
|
133
|
+
content: path_label,
|
134
|
+
styles: :text_primary
|
135
|
+
)
|
108
136
|
end
|
109
137
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
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
|
129
|
-
end
|
138
|
+
def insert_current_frame
|
139
|
+
return if @current_frame.visible?
|
130
140
|
|
131
|
-
|
132
|
-
if
|
133
|
-
|
141
|
+
index = @frames.find_index { |f| @current_frame.real_pos < f.real_pos }
|
142
|
+
if index.nil?
|
143
|
+
@frames << @current_frame
|
134
144
|
else
|
135
|
-
@
|
145
|
+
@frames.insert(index, @current_frame)
|
136
146
|
end
|
137
147
|
end
|
138
|
-
|
139
|
-
def frames_count
|
140
|
-
@session.backtrace.length
|
141
|
-
end
|
142
|
-
|
143
|
-
def backtrace
|
144
|
-
@session.backtrace
|
145
|
-
end
|
146
148
|
end
|
147
149
|
end
|
148
150
|
end
|
149
151
|
|
150
|
-
RubyJard::Screens.add_screen(
|
152
|
+
RubyJard::Screens.add_screen('backtrace', RubyJard::Screens::BacktraceScreen)
|