ruby_jard 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +0 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +12 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +16 -0
  11. data/Rakefile +8 -0
  12. data/bin/console +15 -0
  13. data/lib/ruby_jard.rb +77 -0
  14. data/lib/ruby_jard/commands/continue_command.rb +33 -0
  15. data/lib/ruby_jard/commands/down_command.rb +31 -0
  16. data/lib/ruby_jard/commands/finish_command.rb +31 -0
  17. data/lib/ruby_jard/commands/frame_command.rb +36 -0
  18. data/lib/ruby_jard/commands/next_command.rb +31 -0
  19. data/lib/ruby_jard/commands/step_command.rb +31 -0
  20. data/lib/ruby_jard/commands/up_command.rb +31 -0
  21. data/lib/ruby_jard/decorators/loc_decorator.rb +200 -0
  22. data/lib/ruby_jard/decorators/path_decorator.rb +88 -0
  23. data/lib/ruby_jard/decorators/source_decorator.rb +43 -0
  24. data/lib/ruby_jard/decorators/text_decorator.rb +61 -0
  25. data/lib/ruby_jard/layout.rb +99 -0
  26. data/lib/ruby_jard/layout_template.rb +101 -0
  27. data/lib/ruby_jard/repl_processor.rb +143 -0
  28. data/lib/ruby_jard/screen.rb +61 -0
  29. data/lib/ruby_jard/screen_manager.rb +121 -0
  30. data/lib/ruby_jard/screens.rb +26 -0
  31. data/lib/ruby_jard/screens/backtrace_screen.rb +150 -0
  32. data/lib/ruby_jard/screens/breakpoints_screen.rb +23 -0
  33. data/lib/ruby_jard/screens/empty_screen.rb +13 -0
  34. data/lib/ruby_jard/screens/expressions_sreen.rb +22 -0
  35. data/lib/ruby_jard/screens/menu_screen.rb +62 -0
  36. data/lib/ruby_jard/screens/source_screen.rb +133 -0
  37. data/lib/ruby_jard/screens/threads_screen.rb +116 -0
  38. data/lib/ruby_jard/screens/variables_screen.rb +234 -0
  39. data/lib/ruby_jard/session.rb +54 -0
  40. data/lib/ruby_jard/version.rb +6 -0
  41. data/ruby_jard.gemspec +39 -0
  42. 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