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.
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