ruby_jard 0.1.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +16 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/lib/ruby_jard.rb +77 -0
- data/lib/ruby_jard/commands/continue_command.rb +33 -0
- data/lib/ruby_jard/commands/down_command.rb +31 -0
- data/lib/ruby_jard/commands/finish_command.rb +31 -0
- data/lib/ruby_jard/commands/frame_command.rb +36 -0
- data/lib/ruby_jard/commands/next_command.rb +31 -0
- data/lib/ruby_jard/commands/step_command.rb +31 -0
- data/lib/ruby_jard/commands/up_command.rb +31 -0
- data/lib/ruby_jard/decorators/loc_decorator.rb +200 -0
- data/lib/ruby_jard/decorators/path_decorator.rb +88 -0
- data/lib/ruby_jard/decorators/source_decorator.rb +43 -0
- data/lib/ruby_jard/decorators/text_decorator.rb +61 -0
- data/lib/ruby_jard/layout.rb +99 -0
- data/lib/ruby_jard/layout_template.rb +101 -0
- data/lib/ruby_jard/repl_processor.rb +143 -0
- data/lib/ruby_jard/screen.rb +61 -0
- data/lib/ruby_jard/screen_manager.rb +121 -0
- data/lib/ruby_jard/screens.rb +26 -0
- data/lib/ruby_jard/screens/backtrace_screen.rb +150 -0
- data/lib/ruby_jard/screens/breakpoints_screen.rb +23 -0
- data/lib/ruby_jard/screens/empty_screen.rb +13 -0
- data/lib/ruby_jard/screens/expressions_sreen.rb +22 -0
- data/lib/ruby_jard/screens/menu_screen.rb +62 -0
- data/lib/ruby_jard/screens/source_screen.rb +133 -0
- data/lib/ruby_jard/screens/threads_screen.rb +116 -0
- data/lib/ruby_jard/screens/variables_screen.rb +234 -0
- data/lib/ruby_jard/session.rb +54 -0
- data/lib/ruby_jard/version.rb +6 -0
- data/ruby_jard.gemspec +39 -0
- metadata +160 -0
@@ -0,0 +1,31 @@
|
|
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
|
+
# Data attached in the throw:
|
7
|
+
# * command: constant symbol (:step)
|
8
|
+
# * pry: current context pry instance
|
9
|
+
class StepCommand < Pry::ClassCommand
|
10
|
+
group 'RubyJard'
|
11
|
+
description 'Step into the execution of the current line'
|
12
|
+
|
13
|
+
match 'step'
|
14
|
+
|
15
|
+
banner <<-BANNER
|
16
|
+
Usage: step
|
17
|
+
|
18
|
+
Step into the execution of the current line
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
step
|
22
|
+
BANNER
|
23
|
+
|
24
|
+
def process
|
25
|
+
throw :control_flow, command: :step, pry: pry_instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Pry::Commands.add_command(RubyJard::Commands::StepCommand)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Commands
|
5
|
+
# Command used to explore stacktrace.
|
6
|
+
# Data attached in the throw:
|
7
|
+
# * command: constant symbol (:up)
|
8
|
+
# * pry: current context pry instance
|
9
|
+
class UpCommand < Pry::ClassCommand
|
10
|
+
group 'RubyJard'
|
11
|
+
description 'Explore the frames above the current stopped line in the backtrace'
|
12
|
+
|
13
|
+
match 'up'
|
14
|
+
|
15
|
+
banner <<-BANNER
|
16
|
+
Usage: up
|
17
|
+
|
18
|
+
Explore the frames above the current stopped line in the backtrace.
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
up
|
22
|
+
BANNER
|
23
|
+
|
24
|
+
def process
|
25
|
+
throw :control_flow, command: :up, pry: pry_instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Pry::Commands.add_command(RubyJard::Commands::UpCommand)
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
##
|
6
|
+
# Decorate a line of code fetched from the source file.
|
7
|
+
# The line is tokenized, and feed into JardEncoder to append color (with
|
8
|
+
# Pastel).
|
9
|
+
class LocDecorator
|
10
|
+
attr_reader :loc, :tokens
|
11
|
+
|
12
|
+
def initialize(color_decorator, loc, highlighted)
|
13
|
+
@loc = loc
|
14
|
+
@highlighted = highlighted
|
15
|
+
@encoder = JardLocEncoder.new(
|
16
|
+
color_decorator: color_decorator,
|
17
|
+
highlighted: highlighted
|
18
|
+
)
|
19
|
+
|
20
|
+
decorate
|
21
|
+
end
|
22
|
+
|
23
|
+
def decorate
|
24
|
+
@tokens = CodeRay.scan(@loc, :ruby)
|
25
|
+
@loc = @encoder.encode_tokens(tokens)
|
26
|
+
end
|
27
|
+
|
28
|
+
# A shameless copy from https://github.com/rubychan/coderay/blob/master/lib/coderay/encoders/terminal.rb
|
29
|
+
class JardLocEncoder < CodeRay::Encoders::Encoder
|
30
|
+
TOKEN_COLORS = {
|
31
|
+
debug: [:white, :on_blue],
|
32
|
+
annotation: [:blue],
|
33
|
+
attribute_name: [:blue],
|
34
|
+
attribute_value: [:blue],
|
35
|
+
binary: {
|
36
|
+
self: [:blue],
|
37
|
+
char: [:blue],
|
38
|
+
delimiter: [:blue]
|
39
|
+
},
|
40
|
+
char: {
|
41
|
+
self: [:blue],
|
42
|
+
delimiter: [:blue]
|
43
|
+
},
|
44
|
+
class: [:underline, :green],
|
45
|
+
class_variable: [:green],
|
46
|
+
color: [:green],
|
47
|
+
comment: {
|
48
|
+
self: [:white],
|
49
|
+
char: [:white],
|
50
|
+
delimiter: [:white]
|
51
|
+
},
|
52
|
+
constant: [:underline, :green],
|
53
|
+
decorator: [:blue],
|
54
|
+
definition: [:blue],
|
55
|
+
directive: [:blue],
|
56
|
+
docstring: [:blue],
|
57
|
+
doctype: [:blue],
|
58
|
+
done: [:blue],
|
59
|
+
entity: [:blue],
|
60
|
+
error: [:white, :on_red],
|
61
|
+
exception: [:blue],
|
62
|
+
float: [:blue],
|
63
|
+
function: [:green],
|
64
|
+
method: [:green],
|
65
|
+
global_variable: [:green],
|
66
|
+
hex: [:blue],
|
67
|
+
id: [:blue],
|
68
|
+
include: [:blue],
|
69
|
+
integer: [:blue],
|
70
|
+
imaginary: [:blue],
|
71
|
+
important: [:blue],
|
72
|
+
key: {
|
73
|
+
self: [:blue],
|
74
|
+
char: [:blue],
|
75
|
+
delimiter: [:blue]
|
76
|
+
},
|
77
|
+
label: [:blue],
|
78
|
+
local_variable: [:blue],
|
79
|
+
namespace: [:blue],
|
80
|
+
octal: [:blue],
|
81
|
+
predefined: [:blue],
|
82
|
+
predefined_constant: [:blue],
|
83
|
+
predefined_type: [:green],
|
84
|
+
preprocessor: [:blue],
|
85
|
+
pseudo_class: [:blue],
|
86
|
+
regexp: {
|
87
|
+
self: [:blue],
|
88
|
+
delimiter: [:blue],
|
89
|
+
modifier: [:blue],
|
90
|
+
char: [:blue]
|
91
|
+
},
|
92
|
+
reserved: [:blue],
|
93
|
+
keyword: [:blue],
|
94
|
+
shell: {
|
95
|
+
self: [:blue],
|
96
|
+
char: [:blue],
|
97
|
+
delimiter: [:blue],
|
98
|
+
escape: [:blue]
|
99
|
+
},
|
100
|
+
string: {
|
101
|
+
self: [:blue],
|
102
|
+
modifier: [:blue],
|
103
|
+
char: [:blue],
|
104
|
+
delimiter: [:blue],
|
105
|
+
escape: [:blue]
|
106
|
+
},
|
107
|
+
symbol: {
|
108
|
+
self: [:blue],
|
109
|
+
delimiter: [:blue]
|
110
|
+
},
|
111
|
+
tag: [:green],
|
112
|
+
type: [:blue],
|
113
|
+
value: [:blue],
|
114
|
+
variable: [:blue],
|
115
|
+
insert: {
|
116
|
+
self: [:on_green],
|
117
|
+
insert: [:green, :on_green],
|
118
|
+
eyecatcher: [:italic]
|
119
|
+
},
|
120
|
+
delete: {
|
121
|
+
self: [:on_red],
|
122
|
+
delete: [:blue, :on_red],
|
123
|
+
eyecatcher: [:italic]
|
124
|
+
},
|
125
|
+
change: {
|
126
|
+
self: [:on_blue],
|
127
|
+
change: [:white, :on_blue]
|
128
|
+
},
|
129
|
+
head: {
|
130
|
+
self: [:on_red],
|
131
|
+
filename: [:white, :on_red]
|
132
|
+
}
|
133
|
+
}.freeze
|
134
|
+
|
135
|
+
protected
|
136
|
+
|
137
|
+
def setup(options)
|
138
|
+
super
|
139
|
+
@opened = []
|
140
|
+
@color_scopes = [TOKEN_COLORS]
|
141
|
+
@color_decorator = options[:color_decorator]
|
142
|
+
@highlighted = options[:highlighted]
|
143
|
+
end
|
144
|
+
|
145
|
+
public
|
146
|
+
|
147
|
+
def text_token(text, kind)
|
148
|
+
color = @color_scopes.last[kind]
|
149
|
+
text.gsub!("\n", '')
|
150
|
+
if color
|
151
|
+
color = color[:self] if color.is_a? Hash
|
152
|
+
@out << @color_decorator.decorate(text, *compose_color(color))
|
153
|
+
else
|
154
|
+
@out << @color_decorator.decorate(text, *compose_color([]))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def begin_group(kind)
|
159
|
+
@opened << kind
|
160
|
+
open_token(kind)
|
161
|
+
end
|
162
|
+
alias begin_line begin_group
|
163
|
+
|
164
|
+
def end_group(_kind)
|
165
|
+
return unless @opened.pop
|
166
|
+
|
167
|
+
@color_scopes.pop
|
168
|
+
end
|
169
|
+
|
170
|
+
def end_line(kind)
|
171
|
+
end_group(kind)
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def open_token(kind)
|
177
|
+
color = @color_scopes.last[kind]
|
178
|
+
@color_scopes <<
|
179
|
+
if color
|
180
|
+
if color.is_a?(Hash)
|
181
|
+
color
|
182
|
+
else
|
183
|
+
@color_scopes.last
|
184
|
+
end
|
185
|
+
else
|
186
|
+
@color_scopes.last
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def compose_color(color)
|
191
|
+
if @highlighted
|
192
|
+
[:clear] + color
|
193
|
+
else
|
194
|
+
[:dim] + color
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
##
|
6
|
+
# Simplify and generate labels to indicate the location of a path.
|
7
|
+
# If it's from gem, strip Gem paths, or Bundler paths to expose relative
|
8
|
+
# location of the file.
|
9
|
+
# If it's from the current working dir, strip the working dir.
|
10
|
+
class PathDecorator
|
11
|
+
GEM_PATTERN = /(.*)\-(\d+\.\d+[\.\d]*[\.\d]*[\-\.\w]*)/i.freeze
|
12
|
+
PATH_TYPES = [
|
13
|
+
TYPE_UNKNOWN = :unknown,
|
14
|
+
TYPE_PWD = :pwd,
|
15
|
+
TYPE_GEM = :gem
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
attr_reader :path, :lineno, :gem, :gem_version
|
19
|
+
|
20
|
+
def initialize(path, lineno)
|
21
|
+
@gem = nil
|
22
|
+
@gem_version = nil
|
23
|
+
@path = path
|
24
|
+
@lineno = lineno
|
25
|
+
@type = TYPE_UNKNOWN
|
26
|
+
|
27
|
+
decorate
|
28
|
+
end
|
29
|
+
|
30
|
+
def decorate
|
31
|
+
if path.start_with?(Dir.pwd)
|
32
|
+
@type = TYPE_PWD
|
33
|
+
@path = @path[Dir.pwd.length..-1]
|
34
|
+
else
|
35
|
+
decorate_gem_path
|
36
|
+
end
|
37
|
+
|
38
|
+
@path = @path[1..-1] if @path.start_with?('/')
|
39
|
+
end
|
40
|
+
|
41
|
+
def gem?
|
42
|
+
@type == TYPE_GEM
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def decorate_gem_path
|
48
|
+
gem_paths.each do |gem_path|
|
49
|
+
next unless path.start_with?(gem_path)
|
50
|
+
|
51
|
+
@type = TYPE_GEM
|
52
|
+
splitted_path =
|
53
|
+
@path[gem_path.length..-1]
|
54
|
+
.split('/')
|
55
|
+
.reject(&:empty?)
|
56
|
+
@path = splitted_path[1..-1].join('/')
|
57
|
+
@gem = splitted_path.first
|
58
|
+
match = GEM_PATTERN.match(@gem)
|
59
|
+
if match
|
60
|
+
@gem = match[1]
|
61
|
+
@gem_version = match[2]
|
62
|
+
end
|
63
|
+
|
64
|
+
break
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def gem_paths
|
69
|
+
paths = []
|
70
|
+
|
71
|
+
if defined?(Gem)
|
72
|
+
Gem.path.each do |gem_path|
|
73
|
+
paths << File.join(gem_path, 'gems')
|
74
|
+
paths << gem_path
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if defined?(Bundler)
|
79
|
+
bundle_path = Bundler.bundle_path.to_s
|
80
|
+
paths << File.join(bundle_path, 'gems')
|
81
|
+
paths << bundle_path
|
82
|
+
end
|
83
|
+
|
84
|
+
paths
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
##
|
6
|
+
# Decorator to decorate a file of source code
|
7
|
+
# It loads a window of source code that centers the current line position.
|
8
|
+
class SourceDecorator
|
9
|
+
attr_reader :codes, :window_start, :window_end
|
10
|
+
|
11
|
+
def initialize(file, lineno, window)
|
12
|
+
@file = file
|
13
|
+
@lineno = lineno
|
14
|
+
@window = window
|
15
|
+
@codes = []
|
16
|
+
|
17
|
+
decorate
|
18
|
+
end
|
19
|
+
|
20
|
+
def decorate
|
21
|
+
begin
|
22
|
+
file = File.open(@file)
|
23
|
+
rescue Errno::ENOENT
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
@window_start = @lineno - @window / 2
|
28
|
+
@window_start = 0 if @window_start.negative?
|
29
|
+
@window_end = @window_start + @window
|
30
|
+
|
31
|
+
until file.eof?
|
32
|
+
loc = file.gets
|
33
|
+
next if file.lineno < @window_start
|
34
|
+
break if @window_end < file.lineno
|
35
|
+
|
36
|
+
@codes << loc
|
37
|
+
end
|
38
|
+
|
39
|
+
file.close
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyJard
|
4
|
+
module Decorators
|
5
|
+
##
|
6
|
+
# Decorate text colos and styles. It acts as a wrapper for Pastel gem
|
7
|
+
# TODO: the current color handling sucks. This should be migrated to a
|
8
|
+
# color scheme system instead.
|
9
|
+
class TextDecorator
|
10
|
+
attr_reader :length, :content
|
11
|
+
|
12
|
+
def initialize(color, highlighted = false)
|
13
|
+
@length = 0
|
14
|
+
@content = ''
|
15
|
+
@color = color
|
16
|
+
@highlighted = highlighted
|
17
|
+
end
|
18
|
+
|
19
|
+
def text(sentence, *styles)
|
20
|
+
return self + sentence if sentence.is_a?(TextDecorator)
|
21
|
+
|
22
|
+
sentence = sentence.to_s
|
23
|
+
@length += sentence.length
|
24
|
+
|
25
|
+
@content +=
|
26
|
+
if styles.empty?
|
27
|
+
sentence
|
28
|
+
else
|
29
|
+
@color.decorate(sentence, *compose_styles(styles))
|
30
|
+
end
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_highlight(highlighted)
|
36
|
+
@highlighted = highlighted
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def +(other)
|
42
|
+
if other.is_a?(RubyJard::Decorators::TextDecorator)
|
43
|
+
@length = other.length
|
44
|
+
@content += other.content
|
45
|
+
else
|
46
|
+
text(other.to_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def compose_styles(styles)
|
55
|
+
styles.delete(:clear)
|
56
|
+
styles.delete(:dim)
|
57
|
+
styles.prepend(@highlighted ? :clear : :dim)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|