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,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
##
|
5
|
+
# A helper to standardize control instruction passed around via
|
6
|
+
# throw and catch mechanism.
|
7
|
+
class ControlFlow
|
8
|
+
THROW_KEYWORD = :jard_control_flow
|
9
|
+
ALLOW_LIST = {
|
10
|
+
continue: [], # lib/ruby_jard/commands/continue_command.rb
|
11
|
+
exit: [], # lib/ruby_jard/commands/exit_command.rb
|
12
|
+
frame: [:frame], # lib/ruby_jard/commands/frame_command.rb
|
13
|
+
up: [:times], # lib/ruby_jard/commands/up_command.rb
|
14
|
+
down: [:times], # lib/ruby_jard/commands/down_command.rb
|
15
|
+
next: [:times], # lib/ruby_jard/commands/next_command.rb
|
16
|
+
step: [:times], # lib/ruby_jard/commands/step_command.rb
|
17
|
+
step_out: [:times], # lib/ruby_jard/commands/step_out_command.rb
|
18
|
+
key_binding: [:action], # lib/ruby_jard/commands/step_command.rb
|
19
|
+
list: [] # lib/ruby_jard/commands/list_command.rb
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
attr_reader :command, :arguments
|
23
|
+
|
24
|
+
def initialize(command, arguments)
|
25
|
+
@command = command
|
26
|
+
@arguments = arguments
|
27
|
+
|
28
|
+
validate!
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate!
|
32
|
+
if command.to_s.empty?
|
33
|
+
raise RubyJard::Error, 'Control command is empty'
|
34
|
+
end
|
35
|
+
|
36
|
+
unless ALLOW_LIST.key?(command)
|
37
|
+
raise RubyJard::Error,
|
38
|
+
"Control command `#{command}` is not registered in the allow list."
|
39
|
+
end
|
40
|
+
|
41
|
+
invalid_keys = arguments.keys - ALLOW_LIST[command]
|
42
|
+
unless invalid_keys.empty?
|
43
|
+
raise RubyJard::Error,
|
44
|
+
"Control command `#{command}` is attached with unregister arguments: #{invalid_keys}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def dispatch(command, arguments = {})
|
50
|
+
if command.is_a?(RubyJard::ControlFlow)
|
51
|
+
throw THROW_KEYWORD, command
|
52
|
+
else
|
53
|
+
throw THROW_KEYWORD, new(command, arguments)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def listen
|
58
|
+
raise RubyJard::Error, 'This method requires a block' unless block_given?
|
59
|
+
|
60
|
+
flow = catch(THROW_KEYWORD) do
|
61
|
+
yield
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
if flow.nil? || flow.is_a?(RubyJard::ControlFlow)
|
66
|
+
flow
|
67
|
+
else
|
68
|
+
raise RubyJard::Error, 'Control flow misused!'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
##
|
6
|
+
# Decorate Array data structure, supports singleline and multiline form.
|
7
|
+
class ArrayDecorator
|
8
|
+
def initialize(generic_decorator)
|
9
|
+
@generic_decorator = generic_decorator
|
10
|
+
@attributes_decorator = RubyJard::Decorators::AttributesDecorator.new(generic_decorator)
|
11
|
+
end
|
12
|
+
|
13
|
+
def match?(variable)
|
14
|
+
RubyJard::Reflection.call_is_a?(variable, Array)
|
15
|
+
end
|
16
|
+
|
17
|
+
def decorate_singleline(variable, line_limit:, depth: 0)
|
18
|
+
spans = []
|
19
|
+
spans << RubyJard::Span.new(content: '[', styles: :text_primary)
|
20
|
+
spans += @attributes_decorator.inline_values(
|
21
|
+
variable.each_with_index, total: variable.length, line_limit: line_limit - 2, depth: depth + 1
|
22
|
+
)
|
23
|
+
spans << RubyJard::Span.new(content: ']', styles: :text_primary)
|
24
|
+
|
25
|
+
spans
|
26
|
+
end
|
27
|
+
|
28
|
+
def decorate_multiline(variable, first_line_limit:, lines:, line_limit:, depth: 0)
|
29
|
+
if variable.length > lines * 2
|
30
|
+
return do_decorate_multiline(variable, lines: lines, line_limit: line_limit, depth: depth)
|
31
|
+
end
|
32
|
+
|
33
|
+
singleline = decorate_singleline(variable, line_limit: first_line_limit, depth: depth)
|
34
|
+
if (singleline.map(&:content_length).sum < line_limit && same_type?(variable, lines)) || variable.length <= 1
|
35
|
+
[singleline]
|
36
|
+
else
|
37
|
+
do_decorate_multiline(variable, lines: lines, line_limit: line_limit, depth: depth)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def same_type?(variable, sample)
|
44
|
+
variable.first(sample).map { |item| RubyJard::Reflection.call_class(item) }.uniq.length <= 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def do_decorate_multiline(variable, lines:, line_limit:, depth: 0)
|
48
|
+
spans = [[RubyJard::Span.new(content: '[', styles: :text_primary)]]
|
49
|
+
|
50
|
+
item_count = 0
|
51
|
+
variable.each_with_index do |value, index|
|
52
|
+
spans << @attributes_decorator.value(value, line_limit: line_limit, depth: depth + 1)
|
53
|
+
|
54
|
+
item_count += 1
|
55
|
+
break if index >= lines - 2
|
56
|
+
end
|
57
|
+
|
58
|
+
spans << last_line(variable.length, item_count)
|
59
|
+
end
|
60
|
+
|
61
|
+
def last_line(total, item_count)
|
62
|
+
if total > item_count
|
63
|
+
[
|
64
|
+
RubyJard::Span.new(
|
65
|
+
content: "▸ #{total - item_count} more...",
|
66
|
+
margin_left: 2, styles: :text_dim
|
67
|
+
),
|
68
|
+
RubyJard::Span.new(
|
69
|
+
content: ']',
|
70
|
+
styles: :text_primary
|
71
|
+
)
|
72
|
+
]
|
73
|
+
else
|
74
|
+
[RubyJard::Span.new(content: ']', styles: :text_primary)]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
TYPICAL_DEPTH = 3
|
6
|
+
MAX_DEPTH = 5
|
7
|
+
DO_NOT_WASTE_LENGTH = 40
|
8
|
+
##
|
9
|
+
# Decorate collection data structure. Support:
|
10
|
+
# - Collection of values
|
11
|
+
# - Collection of key-value pairs
|
12
|
+
# - Individual value
|
13
|
+
# - Individual pair
|
14
|
+
# This decorator should not be used directly.
|
15
|
+
class AttributesDecorator
|
16
|
+
def initialize(generic_decorator)
|
17
|
+
@generic_decorator = generic_decorator
|
18
|
+
end
|
19
|
+
|
20
|
+
def inline_pairs(enum, total:, line_limit:, process_key:, value_proc: nil, depth: 0)
|
21
|
+
return [ellipsis_span] if too_deep?(depth, line_limit)
|
22
|
+
|
23
|
+
spans = []
|
24
|
+
width = 1
|
25
|
+
item_limit = total == 0 ? 0 : [line_limit / total, pair_limit(depth)].max
|
26
|
+
|
27
|
+
enum.each do |(key, value), index|
|
28
|
+
key_inspection = inspect_key(key, item_limit, process_key: process_key, depth: depth)
|
29
|
+
key_inspection_length = key_inspection.map(&:content_length).sum
|
30
|
+
|
31
|
+
value_inspection = @generic_decorator.decorate_singleline(
|
32
|
+
value_proc.nil? ? value : value_proc.call(key),
|
33
|
+
line_limit: [item_limit - key_inspection_length, pair_limit(depth)].max, depth: depth
|
34
|
+
)
|
35
|
+
value_inspection_length = value_inspection.map(&:content_length).sum
|
36
|
+
|
37
|
+
if index > 0
|
38
|
+
spans << separator_span
|
39
|
+
width += 2
|
40
|
+
end
|
41
|
+
|
42
|
+
if width + key_inspection_length + value_inspection_length + 5 > line_limit
|
43
|
+
spans << ellipsis_span
|
44
|
+
break
|
45
|
+
end
|
46
|
+
|
47
|
+
spans += key_inspection
|
48
|
+
width += key_inspection_length
|
49
|
+
|
50
|
+
spans << arrow_span
|
51
|
+
width += 3
|
52
|
+
|
53
|
+
spans += value_inspection
|
54
|
+
width += value_inspection_length
|
55
|
+
end
|
56
|
+
|
57
|
+
spans
|
58
|
+
end
|
59
|
+
|
60
|
+
def pair(key, value, line_limit:, process_key:, depth: 0)
|
61
|
+
return [ellipsis_span] if too_deep?(depth, line_limit)
|
62
|
+
|
63
|
+
spans = []
|
64
|
+
spans << indent_span
|
65
|
+
width = indent_span.content_length
|
66
|
+
|
67
|
+
key_inspection = inspect_key(key, line_limit - width, process_key: process_key, depth: depth)
|
68
|
+
key_inspection_length = key_inspection.map(&:content_length).sum
|
69
|
+
|
70
|
+
spans += key_inspection
|
71
|
+
width += key_inspection_length
|
72
|
+
|
73
|
+
spans << arrow_span
|
74
|
+
width += 3
|
75
|
+
|
76
|
+
value_inspection = @generic_decorator.decorate_singleline(
|
77
|
+
value, line_limit: [line_limit - width, pair_limit(depth)].max, depth: depth
|
78
|
+
)
|
79
|
+
|
80
|
+
spans + value_inspection
|
81
|
+
end
|
82
|
+
|
83
|
+
def inline_values(enum, total:, line_limit:, depth: 0)
|
84
|
+
return [ellipsis_span] if too_deep?(depth, line_limit)
|
85
|
+
|
86
|
+
spans = []
|
87
|
+
width = 1
|
88
|
+
item_limit = total == 0 ? 0 : [line_limit / total, value_limit(depth)].max
|
89
|
+
|
90
|
+
enum.each do |value, index|
|
91
|
+
value_inspection = @generic_decorator.decorate_singleline(
|
92
|
+
value, line_limit: [item_limit, value_limit(depth)].max, depth: depth
|
93
|
+
)
|
94
|
+
value_inspection_length = value_inspection.map(&:content_length).sum
|
95
|
+
|
96
|
+
if index > 0
|
97
|
+
spans << separator_span
|
98
|
+
width += 2
|
99
|
+
end
|
100
|
+
|
101
|
+
if width + value_inspection_length + 2 > line_limit
|
102
|
+
spans << ellipsis_span
|
103
|
+
break
|
104
|
+
end
|
105
|
+
|
106
|
+
spans += value_inspection
|
107
|
+
width += value_inspection_length
|
108
|
+
end
|
109
|
+
|
110
|
+
spans
|
111
|
+
end
|
112
|
+
|
113
|
+
def value(value, line_limit:, depth: 0)
|
114
|
+
return [ellipsis_span] if too_deep?(depth, line_limit)
|
115
|
+
|
116
|
+
spans = []
|
117
|
+
spans << indent_span
|
118
|
+
width = indent_span.content_length
|
119
|
+
|
120
|
+
value_inspection = @generic_decorator.decorate_singleline(
|
121
|
+
value, line_limit: [line_limit - width, value_limit(depth)].max, depth: depth
|
122
|
+
)
|
123
|
+
|
124
|
+
spans + value_inspection
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def inspect_key(key, item_limit, process_key:, depth: 0)
|
130
|
+
if process_key
|
131
|
+
@generic_decorator.decorate_singleline(
|
132
|
+
key, line_limit: item_limit, depth: depth
|
133
|
+
)
|
134
|
+
else
|
135
|
+
[RubyJard::Span.new(content: key.to_s, styles: :text_primary)]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def arrow_span
|
140
|
+
RubyJard::Span.new(content: '→', margin_left: 1, margin_right: 1, styles: :text_highlighted)
|
141
|
+
end
|
142
|
+
|
143
|
+
def separator_span
|
144
|
+
RubyJard::Span.new(content: ',', margin_right: 1, styles: :text_primary)
|
145
|
+
end
|
146
|
+
|
147
|
+
def ellipsis_span
|
148
|
+
RubyJard::Span.new(content: '…', styles: :text_dim)
|
149
|
+
end
|
150
|
+
|
151
|
+
def indent_span
|
152
|
+
RubyJard::Span.new(content: '▸', margin_right: 1, margin_left: 2, styles: :text_dim)
|
153
|
+
end
|
154
|
+
|
155
|
+
def too_deep?(depth, line_limit)
|
156
|
+
return true if depth > MAX_DEPTH
|
157
|
+
return false if line_limit > DO_NOT_WASTE_LENGTH
|
158
|
+
|
159
|
+
depth > TYPICAL_DEPTH
|
160
|
+
end
|
161
|
+
|
162
|
+
def pair_limit(depth)
|
163
|
+
# The deeper structure, the less meaningful the actual data is
|
164
|
+
30 - depth * 5
|
165
|
+
end
|
166
|
+
|
167
|
+
def value_limit(_depth)
|
168
|
+
30
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
##
|
6
|
+
# Manipulate and decorate color for texts.
|
7
|
+
# This class translate colors to corresponding escape sequences.
|
8
|
+
# Support 24-bit color (#51617d format) or 256 colors (https://jonasjacek.github.io/colors/)
|
9
|
+
# Example:
|
10
|
+
# - #fafafa => \e[38;2;250;250;250m
|
11
|
+
# - #aaa => \e[38;2;170;170;170m
|
12
|
+
# - 77 => \e[38;2;170;170;170m
|
13
|
+
class ColorDecorator
|
14
|
+
HEX_PATTERN_6 = /^#([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$/i.freeze
|
15
|
+
HEX_PATTERN_3 = /^#([A-Fa-f0-9]{1})([A-Fa-f0-9]{1})([A-Fa-f0-9]{1})$/i.freeze
|
16
|
+
XTERM_NUMBER_PATTERN = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/i.freeze
|
17
|
+
|
18
|
+
CSI_RESET = "\e[0m"
|
19
|
+
CSI_FOREGROUND_24BIT = "\e[38;2;%d;%d;%dm"
|
20
|
+
CSI_BACKGROUND_24BIT = "\e[48;2;%d;%d;%dm"
|
21
|
+
CSI_FOREGROUND_256 = "\e[38;5;%dm"
|
22
|
+
CSI_BACKGROUND_256 = "\e[48;5;%dm"
|
23
|
+
|
24
|
+
CSI_ITALIC = "\e[3m"
|
25
|
+
CSI_UNDERLINE = "\e[4m"
|
26
|
+
CSI_BOLD = "\e[1m"
|
27
|
+
|
28
|
+
STYLES_CSI_MAP = {
|
29
|
+
underline: CSI_UNDERLINE,
|
30
|
+
italic: CSI_ITALIC,
|
31
|
+
bold: CSI_BOLD
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
def initialize(color_scheme)
|
35
|
+
@color_scheme = color_scheme
|
36
|
+
end
|
37
|
+
|
38
|
+
def decorate(style_names, content)
|
39
|
+
attributes = nil
|
40
|
+
if style_names.is_a?(Symbol)
|
41
|
+
styles = @color_scheme.styles_for(style_names)
|
42
|
+
else
|
43
|
+
attributes = translate_styles(style_names)
|
44
|
+
styles = @color_scheme.styles_for(style_names.shift)
|
45
|
+
end
|
46
|
+
foreground = translate_color(styles.shift, true)
|
47
|
+
background = translate_color(styles.shift, false)
|
48
|
+
"#{foreground}#{background}#{attributes}#{content}#{CSI_RESET}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def translate_color(color, foreground)
|
52
|
+
if (matches = HEX_PATTERN_6.match(color.to_s))
|
53
|
+
red = matches[1].to_i(16)
|
54
|
+
green = matches[2].to_i(16)
|
55
|
+
blue = matches[3].to_i(16)
|
56
|
+
sequence = foreground ? CSI_FOREGROUND_24BIT : CSI_BACKGROUND_24BIT
|
57
|
+
format sequence, red, green, blue
|
58
|
+
elsif (matches = HEX_PATTERN_3.match(color.to_s))
|
59
|
+
red = (matches[1] * 2).to_i(16)
|
60
|
+
green = (matches[2] * 2).to_i(16)
|
61
|
+
blue = (matches[3] * 2).to_i(16)
|
62
|
+
sequence = foreground ? CSI_FOREGROUND_24BIT : CSI_BACKGROUND_24BIT
|
63
|
+
format sequence, red, green, blue
|
64
|
+
elsif (matches = XTERM_NUMBER_PATTERN.match(color.to_s))
|
65
|
+
color = matches[1]
|
66
|
+
sequence = foreground ? CSI_FOREGROUND_256 : CSI_BACKGROUND_256
|
67
|
+
format sequence, color
|
68
|
+
else
|
69
|
+
''
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def translate_styles(styles = [])
|
76
|
+
styles.map { |key| STYLES_CSI_MAP[key] }.compact.join
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
##
|
6
|
+
# Decorate Hash data structure, supports singleline and multiline form.
|
7
|
+
class HashDecorator
|
8
|
+
def initialize(generic_decorator)
|
9
|
+
@generic_decorator = generic_decorator
|
10
|
+
@attributes_decorator = RubyJard::Decorators::AttributesDecorator.new(generic_decorator)
|
11
|
+
end
|
12
|
+
|
13
|
+
def decorate_singleline(variable, line_limit:, depth: 0)
|
14
|
+
spans = []
|
15
|
+
spans << RubyJard::Span.new(content: '{', styles: :text_primary)
|
16
|
+
spans += @attributes_decorator.inline_pairs(
|
17
|
+
variable.each_with_index,
|
18
|
+
total: variable.length, line_limit: line_limit - 2, process_key: true, depth: depth + 1
|
19
|
+
)
|
20
|
+
spans << RubyJard::Span.new(content: '}', styles: :text_primary)
|
21
|
+
end
|
22
|
+
|
23
|
+
def decorate_multiline(variable, first_line_limit:, lines:, line_limit:, depth: 0)
|
24
|
+
if variable.size > lines * 1.5
|
25
|
+
return do_decorate_multiline(variable, lines: lines, line_limit: line_limit, depth: depth)
|
26
|
+
end
|
27
|
+
|
28
|
+
singleline = decorate_singleline(variable, line_limit: first_line_limit)
|
29
|
+
if singleline.map(&:content_length).sum < line_limit || variable.length <= 1
|
30
|
+
[singleline]
|
31
|
+
else
|
32
|
+
do_decorate_multiline(variable, lines: lines, line_limit: line_limit, depth: depth)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def match?(variable)
|
37
|
+
RubyJard::Reflection.call_is_a?(variable, Hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def do_decorate_multiline(variable, lines:, line_limit:, depth: 0)
|
43
|
+
spans = [[RubyJard::Span.new(content: '{', styles: :text_primary)]]
|
44
|
+
|
45
|
+
item_count = 0
|
46
|
+
variable.each_with_index do |(key, value), index|
|
47
|
+
spans << @attributes_decorator.pair(
|
48
|
+
key, value, line_limit: line_limit, process_key: true, depth: depth + 1
|
49
|
+
)
|
50
|
+
item_count += 1
|
51
|
+
break if index >= lines - 2
|
52
|
+
end
|
53
|
+
spans << last_line(variable.length, item_count)
|
54
|
+
end
|
55
|
+
|
56
|
+
def last_line(total, item_count)
|
57
|
+
if total > item_count
|
58
|
+
[
|
59
|
+
RubyJard::Span.new(
|
60
|
+
content: "▸ #{total - item_count} more...",
|
61
|
+
margin_left: 2, styles: :text_dim
|
62
|
+
),
|
63
|
+
RubyJard::Span.new(
|
64
|
+
content: '}',
|
65
|
+
styles: :text_primary
|
66
|
+
)
|
67
|
+
]
|
68
|
+
else
|
69
|
+
[RubyJard::Span.new(content: '}', styles: :text_primary)]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|