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.
- 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
@@ -3,9 +3,6 @@
|
|
3
3
|
module RubyJard
|
4
4
|
module Commands
|
5
5
|
# Command used to continue program execution to the next line.
|
6
|
-
# Data attached in the throw:
|
7
|
-
# * command: constant symbol (:next)
|
8
|
-
# * pry: current context pry instance
|
9
6
|
class NextCommand < Pry::ClassCommand
|
10
7
|
group 'RubyJard'
|
11
8
|
description 'Next into the execution of the current line'
|
@@ -22,7 +19,7 @@ module RubyJard
|
|
22
19
|
BANNER
|
23
20
|
|
24
21
|
def process
|
25
|
-
|
22
|
+
RubyJard::ControlFlow.dispatch(:next)
|
26
23
|
end
|
27
24
|
end
|
28
25
|
end
|
@@ -3,9 +3,6 @@
|
|
3
3
|
module RubyJard
|
4
4
|
module Commands
|
5
5
|
# Command used to Step into the execution of the current line.
|
6
|
-
# Data attached in the throw:
|
7
|
-
# * command: constant symbol (:step)
|
8
|
-
# * pry: current context pry instance
|
9
6
|
class StepCommand < Pry::ClassCommand
|
10
7
|
group 'RubyJard'
|
11
8
|
description 'Step into the execution of the current line'
|
@@ -22,7 +19,7 @@ module RubyJard
|
|
22
19
|
BANNER
|
23
20
|
|
24
21
|
def process
|
25
|
-
|
22
|
+
RubyJard::ControlFlow.dispatch(:step)
|
26
23
|
end
|
27
24
|
end
|
28
25
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Commands
|
5
|
+
# Command used to Step into the execution of the current line.
|
6
|
+
class StepOutCommand < Pry::ClassCommand
|
7
|
+
group 'RubyJard'
|
8
|
+
description 'Step out of current frame and move to the execution of the upper frame'
|
9
|
+
|
10
|
+
match 'step-out'
|
11
|
+
|
12
|
+
banner <<-BANNER
|
13
|
+
Usage: step-out
|
14
|
+
|
15
|
+
Step out of current frame and move to the execution of the upper frame
|
16
|
+
|
17
|
+
Examples:
|
18
|
+
step-out
|
19
|
+
BANNER
|
20
|
+
|
21
|
+
def process
|
22
|
+
RubyJard::ControlFlow.dispatch(:step_out)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Pry::Commands.add_command(RubyJard::Commands::StepOutCommand)
|
@@ -3,9 +3,6 @@
|
|
3
3
|
module RubyJard
|
4
4
|
module Commands
|
5
5
|
# Command used to explore stacktrace.
|
6
|
-
# Data attached in the throw:
|
7
|
-
# * command: constant symbol (:up)
|
8
|
-
# * pry: current context pry instance
|
9
6
|
class UpCommand < Pry::ClassCommand
|
10
7
|
group 'RubyJard'
|
11
8
|
description 'Explore the frames above the current stopped line in the backtrace'
|
@@ -22,7 +19,7 @@ module RubyJard
|
|
22
19
|
BANNER
|
23
20
|
|
24
21
|
def process
|
25
|
-
|
22
|
+
RubyJard::ControlFlow.dispatch(:up)
|
26
23
|
end
|
27
24
|
end
|
28
25
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'io/console'
|
4
|
+
require 'English'
|
5
|
+
|
6
|
+
module RubyJard
|
7
|
+
# Wrapper for utilities to control screen
|
8
|
+
class Console
|
9
|
+
class << self
|
10
|
+
def start_alternative_terminal(output)
|
11
|
+
return unless output.tty?
|
12
|
+
|
13
|
+
output.print tput('smcup')
|
14
|
+
end
|
15
|
+
|
16
|
+
def stop_alternative_terminal(output)
|
17
|
+
return unless output.tty?
|
18
|
+
|
19
|
+
output.print tput('rmcup')
|
20
|
+
end
|
21
|
+
|
22
|
+
def move_to(output, x, y)
|
23
|
+
return unless output.tty?
|
24
|
+
|
25
|
+
output.goto(y, x)
|
26
|
+
end
|
27
|
+
|
28
|
+
def screen_size(output)
|
29
|
+
return [0, 0] unless output.tty?
|
30
|
+
|
31
|
+
height, width = output.winsize
|
32
|
+
[width, height]
|
33
|
+
end
|
34
|
+
|
35
|
+
def hard_clear_screen(output)
|
36
|
+
return unless output.tty?
|
37
|
+
|
38
|
+
output.print tput('clear')
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear_screen(output)
|
42
|
+
return unless output.tty?
|
43
|
+
|
44
|
+
output.clear_screen
|
45
|
+
end
|
46
|
+
|
47
|
+
def hide_cursor(output)
|
48
|
+
return unless output.tty?
|
49
|
+
|
50
|
+
output.print tput('civis')
|
51
|
+
end
|
52
|
+
|
53
|
+
def show_cursor(output)
|
54
|
+
return unless output.tty?
|
55
|
+
|
56
|
+
output.print tput('cvvis')
|
57
|
+
end
|
58
|
+
|
59
|
+
def cooked!(output)
|
60
|
+
return unless output.tty?
|
61
|
+
|
62
|
+
output.cooked!
|
63
|
+
end
|
64
|
+
|
65
|
+
def echo!(output)
|
66
|
+
return unless output.tty?
|
67
|
+
|
68
|
+
output.echo = true
|
69
|
+
end
|
70
|
+
|
71
|
+
def tput(*args)
|
72
|
+
# TODO: Should implement multiple fallbacks here to support different platforms
|
73
|
+
|
74
|
+
command = "tput #{args.join(' ')}"
|
75
|
+
output = `#{command}`
|
76
|
+
if $CHILD_STATUS.success?
|
77
|
+
output
|
78
|
+
else
|
79
|
+
raise Ruby::Error, "Fail to call `#{command}`: #{$CHILD_STATUS}"
|
80
|
+
end
|
81
|
+
rescue StandardError => e
|
82
|
+
raise Ruby::Error, "Fail to call `#{command}`. Error: #{e}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,71 @@
|
|
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: [:times], # lib/ruby_jard/commands/continue_command.rb
|
11
|
+
frame: [:frame], # lib/ruby_jard/commands/frame_command.rb
|
12
|
+
up: [:times], # lib/ruby_jard/commands/up_command.rb
|
13
|
+
down: [:times], # lib/ruby_jard/commands/down_command.rb
|
14
|
+
next: [:times], # lib/ruby_jard/commands/next_command.rb
|
15
|
+
step: [:times], # lib/ruby_jard/commands/step_command.rb
|
16
|
+
step_out: [:times], # lib/ruby_jard/commands/step_out_command.rb
|
17
|
+
key_binding: [:action] # lib/ruby_jard/commands/step_command.rb
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
attr_reader :command, :arguments
|
21
|
+
|
22
|
+
def initialize(command, arguments)
|
23
|
+
@command = command
|
24
|
+
@arguments = arguments
|
25
|
+
|
26
|
+
validate!
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
if command.to_s.empty?
|
31
|
+
raise RubyJard::Error, 'Control command is empty'
|
32
|
+
end
|
33
|
+
|
34
|
+
unless ALLOW_LIST.key?(command)
|
35
|
+
raise RubyJard::Error,
|
36
|
+
"Control command `#{command}` is not registered in the allow list."
|
37
|
+
end
|
38
|
+
|
39
|
+
invalid_keys = arguments.keys - ALLOW_LIST[command]
|
40
|
+
unless invalid_keys.empty?
|
41
|
+
raise RubyJard::Error,
|
42
|
+
"Control command `#{command}` is attached with unregister arguments: #{invalid_keys}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def dispatch(command, arguments = {})
|
48
|
+
if command.is_a?(RubyJard::ControlFlow)
|
49
|
+
throw THROW_KEYWORD, command
|
50
|
+
else
|
51
|
+
throw THROW_KEYWORD, new(command, arguments)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def listen
|
56
|
+
raise RubyJard::Error, 'This method requires a block' unless block_given?
|
57
|
+
|
58
|
+
flow = catch(THROW_KEYWORD) do
|
59
|
+
yield
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
if flow.nil? || flow.is_a?(RubyJard::ControlFlow)
|
64
|
+
flow
|
65
|
+
else
|
66
|
+
raise RubyJard::Error, 'Control flow misused!'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pastel'
|
4
|
+
|
5
|
+
module RubyJard
|
6
|
+
module Decorators
|
7
|
+
##
|
8
|
+
# Manipulate and decorate color for texts. The core is Pastel, which is a library to
|
9
|
+
# inject escape sequences to let the terminal emulator aware of target color. This
|
10
|
+
# class wraps around the core, validates, and standardizes the styles before feeding
|
11
|
+
# styling information to Pastel.
|
12
|
+
class ColorDecorator
|
13
|
+
COLORS = [
|
14
|
+
:black,
|
15
|
+
:red,
|
16
|
+
:green,
|
17
|
+
:yellow,
|
18
|
+
:blue,
|
19
|
+
:magenta,
|
20
|
+
:cyan,
|
21
|
+
:white
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@pastel = Pastel.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def decorate(text, *styles)
|
29
|
+
styles = standardize_styles(styles)
|
30
|
+
@pastel.decorate(text, *styles)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def standardize_styles(styles)
|
36
|
+
return [] if styles.include?(:clear)
|
37
|
+
|
38
|
+
if styles.include?(:darker)
|
39
|
+
# Convert all bright_color -> color
|
40
|
+
styles = darker(styles)
|
41
|
+
elsif styles.include?(:brighter)
|
42
|
+
# Convert all color -> bright_color
|
43
|
+
styles = brighter(styles)
|
44
|
+
end
|
45
|
+
|
46
|
+
styles.uniq.compact
|
47
|
+
end
|
48
|
+
|
49
|
+
def darker(styles)
|
50
|
+
styles.map do |color|
|
51
|
+
next if [:darker, :brighter].include?(color)
|
52
|
+
|
53
|
+
color_str = color.to_s
|
54
|
+
if color_str.start_with?('bright_')
|
55
|
+
color_str.gsub(/^bright_/i, '').to_sym
|
56
|
+
else
|
57
|
+
color
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def brighter(styles)
|
63
|
+
styles.map do |color|
|
64
|
+
next if [:darker, :brighter].include?(color)
|
65
|
+
|
66
|
+
color_str = color.to_s
|
67
|
+
if color_str.start_with?('bright_')
|
68
|
+
color
|
69
|
+
elsif COLORS.include?(color)
|
70
|
+
"bright_#{color}".to_sym
|
71
|
+
else
|
72
|
+
color
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -1,32 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'coderay'
|
4
|
+
|
3
5
|
module RubyJard
|
4
6
|
module Decorators
|
5
7
|
##
|
6
8
|
# Decorate a line of code fetched from the source file.
|
7
9
|
# The line is tokenized, and feed into JardEncoder to append color (with
|
8
|
-
#
|
10
|
+
# Span).
|
9
11
|
class LocDecorator
|
10
|
-
attr_reader :
|
12
|
+
attr_reader :spans, :tokens
|
11
13
|
|
12
|
-
def initialize(
|
14
|
+
def initialize(file, loc)
|
15
|
+
@file = file
|
13
16
|
@loc = loc
|
14
|
-
@
|
15
|
-
@encoder = JardLocEncoder.new(
|
16
|
-
color_decorator: color_decorator,
|
17
|
-
highlighted: highlighted
|
18
|
-
)
|
17
|
+
@encoder = JardLocEncoder.new
|
19
18
|
|
20
19
|
decorate
|
21
20
|
end
|
22
21
|
|
23
22
|
def decorate
|
24
|
-
@tokens = CodeRay.scan(@loc,
|
25
|
-
@
|
23
|
+
@tokens = CodeRay.scan(@loc, extension)
|
24
|
+
@spans = @encoder.encode_tokens(tokens)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def extension
|
30
|
+
# TODO: A map constant is better
|
31
|
+
if @file =~ /.*\.erb$/
|
32
|
+
:erb
|
33
|
+
elsif @file =~ /.*\.haml$/
|
34
|
+
:haml
|
35
|
+
else
|
36
|
+
:ruby
|
37
|
+
end
|
26
38
|
end
|
27
39
|
|
28
40
|
# A shameless copy from https://github.com/rubychan/coderay/blob/master/lib/coderay/encoders/terminal.rb
|
29
41
|
class JardLocEncoder < CodeRay::Encoders::Encoder
|
42
|
+
DEFAULT_COLOR = [:white].freeze
|
30
43
|
TOKEN_COLORS = {
|
31
44
|
debug: [:white, :on_blue],
|
32
45
|
annotation: [:blue],
|
@@ -49,7 +62,7 @@ module RubyJard
|
|
49
62
|
char: [:white],
|
50
63
|
delimiter: [:white]
|
51
64
|
},
|
52
|
-
constant: [:
|
65
|
+
constant: [:green],
|
53
66
|
decorator: [:blue],
|
54
67
|
definition: [:blue],
|
55
68
|
directive: [:blue],
|
@@ -129,7 +142,8 @@ module RubyJard
|
|
129
142
|
head: {
|
130
143
|
self: [:on_red],
|
131
144
|
filename: [:white, :on_red]
|
132
|
-
}
|
145
|
+
},
|
146
|
+
instance_variable: [:blue]
|
133
147
|
}.freeze
|
134
148
|
|
135
149
|
protected
|
@@ -138,8 +152,7 @@ module RubyJard
|
|
138
152
|
super
|
139
153
|
@opened = []
|
140
154
|
@color_scopes = [TOKEN_COLORS]
|
141
|
-
@
|
142
|
-
@highlighted = options[:highlighted]
|
155
|
+
@out = []
|
143
156
|
end
|
144
157
|
|
145
158
|
public
|
@@ -147,12 +160,20 @@ module RubyJard
|
|
147
160
|
def text_token(text, kind)
|
148
161
|
color = @color_scopes.last[kind]
|
149
162
|
text.gsub!("\n", '')
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
163
|
+
styles =
|
164
|
+
if !color
|
165
|
+
DEFAULT_COLOR
|
166
|
+
elsif color.is_a? Hash
|
167
|
+
color[:self]
|
168
|
+
else
|
169
|
+
color
|
170
|
+
end
|
171
|
+
@out << Span.new(
|
172
|
+
span_template: nil,
|
173
|
+
content: text,
|
174
|
+
content_length: text.length,
|
175
|
+
styles: styles
|
176
|
+
)
|
156
177
|
end
|
157
178
|
|
158
179
|
def begin_group(kind)
|
@@ -186,14 +207,6 @@ module RubyJard
|
|
186
207
|
@color_scopes.last
|
187
208
|
end
|
188
209
|
end
|
189
|
-
|
190
|
-
def compose_color(color)
|
191
|
-
if @highlighted
|
192
|
-
[:clear] + color
|
193
|
-
else
|
194
|
-
[:dim] + color
|
195
|
-
end
|
196
|
-
end
|
197
210
|
end
|
198
211
|
end
|
199
212
|
end
|