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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/documentation.yml +65 -0
  6. data/.github/workflows/rspec.yml +96 -0
  7. data/.gitignore +1 -0
  8. data/.rubocop.yml +90 -2
  9. data/CHANGELOG.md +112 -0
  10. data/Gemfile +14 -4
  11. data/README.md +95 -3
  12. data/benchmark/path_filter_bench.rb +58 -0
  13. data/bin/console +1 -2
  14. data/lib/ruby_jard.rb +68 -32
  15. data/lib/ruby_jard/box_drawer.rb +175 -0
  16. data/lib/ruby_jard/color_scheme.rb +28 -0
  17. data/lib/ruby_jard/color_schemes.rb +54 -0
  18. data/lib/ruby_jard/color_schemes/256_color_scheme.rb +50 -0
  19. data/lib/ruby_jard/color_schemes/256_light_color_scheme.rb +50 -0
  20. data/lib/ruby_jard/color_schemes/deep_space_color_scheme.rb +49 -0
  21. data/lib/ruby_jard/color_schemes/gruvbox_color_scheme.rb +48 -0
  22. data/lib/ruby_jard/color_schemes/one_half_dark_color_scheme.rb +47 -0
  23. data/lib/ruby_jard/color_schemes/one_half_light_color_scheme.rb +49 -0
  24. data/lib/ruby_jard/column.rb +26 -0
  25. data/lib/ruby_jard/commands/color_helpers.rb +32 -0
  26. data/lib/ruby_jard/commands/continue_command.rb +4 -9
  27. data/lib/ruby_jard/commands/down_command.rb +9 -8
  28. data/lib/ruby_jard/commands/exit_command.rb +27 -0
  29. data/lib/ruby_jard/commands/frame_command.rb +13 -11
  30. data/lib/ruby_jard/commands/jard/color_scheme_command.rb +74 -0
  31. data/lib/ruby_jard/commands/jard/filter_command.rb +136 -0
  32. data/lib/ruby_jard/commands/jard/hide_command.rb +40 -0
  33. data/lib/ruby_jard/commands/jard/output_command.rb +36 -0
  34. data/lib/ruby_jard/commands/jard/show_command.rb +41 -0
  35. data/lib/ruby_jard/commands/jard_command.rb +52 -0
  36. data/lib/ruby_jard/commands/list_command.rb +31 -0
  37. data/lib/ruby_jard/commands/next_command.rb +11 -8
  38. data/lib/ruby_jard/commands/step_command.rb +11 -8
  39. data/lib/ruby_jard/commands/step_out_command.rb +34 -0
  40. data/lib/ruby_jard/commands/up_command.rb +10 -8
  41. data/lib/ruby_jard/commands/validation_helpers.rb +50 -0
  42. data/lib/ruby_jard/config.rb +61 -0
  43. data/lib/ruby_jard/console.rb +158 -0
  44. data/lib/ruby_jard/control_flow.rb +73 -0
  45. data/lib/ruby_jard/decorators/array_decorator.rb +79 -0
  46. data/lib/ruby_jard/decorators/attributes_decorator.rb +172 -0
  47. data/lib/ruby_jard/decorators/color_decorator.rb +80 -0
  48. data/lib/ruby_jard/decorators/hash_decorator.rb +74 -0
  49. data/lib/ruby_jard/decorators/inspection_decorator.rb +109 -0
  50. data/lib/ruby_jard/decorators/loc_decorator.rb +108 -119
  51. data/lib/ruby_jard/decorators/object_decorator.rb +122 -0
  52. data/lib/ruby_jard/decorators/path_decorator.rb +56 -60
  53. data/lib/ruby_jard/decorators/rails_decorator.rb +194 -0
  54. data/lib/ruby_jard/decorators/source_decorator.rb +3 -1
  55. data/lib/ruby_jard/decorators/string_decorator.rb +41 -0
  56. data/lib/ruby_jard/decorators/struct_decorator.rb +79 -0
  57. data/lib/ruby_jard/frame.rb +68 -0
  58. data/lib/ruby_jard/key_binding.rb +14 -0
  59. data/lib/ruby_jard/key_bindings.rb +96 -0
  60. data/lib/ruby_jard/keys.rb +48 -0
  61. data/lib/ruby_jard/layout.rb +17 -88
  62. data/lib/ruby_jard/layout_calculator.rb +168 -0
  63. data/lib/ruby_jard/layout_picker.rb +34 -0
  64. data/lib/ruby_jard/layouts.rb +52 -0
  65. data/lib/ruby_jard/layouts/narrow_horizontal_layout.rb +32 -0
  66. data/lib/ruby_jard/layouts/narrow_vertical_layout.rb +32 -0
  67. data/lib/ruby_jard/layouts/tiny_layout.rb +29 -0
  68. data/lib/ruby_jard/layouts/wide_layout.rb +50 -0
  69. data/lib/ruby_jard/pager.rb +112 -0
  70. data/lib/ruby_jard/path_classifier.rb +133 -0
  71. data/lib/ruby_jard/path_filter.rb +125 -0
  72. data/lib/ruby_jard/reflection.rb +97 -0
  73. data/lib/ruby_jard/repl_processor.rb +151 -89
  74. data/lib/ruby_jard/repl_proxy.rb +337 -0
  75. data/lib/ruby_jard/row.rb +31 -0
  76. data/lib/ruby_jard/row_renderer.rb +119 -0
  77. data/lib/ruby_jard/screen.rb +14 -41
  78. data/lib/ruby_jard/screen_adjuster.rb +104 -0
  79. data/lib/ruby_jard/screen_drawer.rb +25 -0
  80. data/lib/ruby_jard/screen_manager.rb +167 -82
  81. data/lib/ruby_jard/screen_renderer.rb +152 -0
  82. data/lib/ruby_jard/screens.rb +31 -12
  83. data/lib/ruby_jard/screens/backtrace_screen.rb +118 -116
  84. data/lib/ruby_jard/screens/menu_screen.rb +73 -45
  85. data/lib/ruby_jard/screens/source_screen.rb +86 -106
  86. data/lib/ruby_jard/screens/threads_screen.rb +103 -78
  87. data/lib/ruby_jard/screens/variables_screen.rb +224 -142
  88. data/lib/ruby_jard/session.rb +151 -16
  89. data/lib/ruby_jard/span.rb +23 -0
  90. data/lib/ruby_jard/templates/layout_template.rb +35 -0
  91. data/lib/ruby_jard/templates/screen_template.rb +34 -0
  92. data/lib/ruby_jard/thread_info.rb +69 -0
  93. data/lib/ruby_jard/version.rb +1 -1
  94. data/ruby_jard.gemspec +7 -8
  95. metadata +84 -50
  96. data/.travis.yml +0 -6
  97. data/lib/ruby_jard/commands/finish_command.rb +0 -31
  98. data/lib/ruby_jard/decorators/text_decorator.rb +0 -61
  99. data/lib/ruby_jard/layout_template.rb +0 -101
  100. data/lib/ruby_jard/screens/breakpoints_screen.rb +0 -23
  101. data/lib/ruby_jard/screens/empty_screen.rb +0 -13
  102. data/lib/ruby_jard/screens/expressions_sreen.rb +0 -22
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyJard
4
+ module Commands
5
+ # Command used to explore stacktrace.
6
+ class ListCommand < Pry::ClassCommand
7
+ group 'RubyJard'
8
+ description 'Refresh the current UI'
9
+
10
+ match 'list'
11
+
12
+ banner <<-BANNER
13
+ Usage: list
14
+ Examples:
15
+ list
16
+ l
17
+ whereami
18
+
19
+ Refresh the current UI.
20
+ BANNER
21
+
22
+ def process
23
+ RubyJard::ControlFlow.dispatch(:list)
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ Pry::Commands.add_command(RubyJard::Commands::ListCommand)
30
+ Pry::Commands.alias_command 'l', 'list'
31
+ Pry::Commands.alias_command 'whereami', 'list'
@@ -3,29 +3,32 @@
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
7
+ include RubyJard::Commands::ValidationHelpers
8
+
10
9
  group 'RubyJard'
11
10
  description 'Next into the execution of the current line'
12
11
 
13
12
  match 'next'
14
13
 
15
14
  banner <<-BANNER
16
- Usage: next
17
-
18
- Continue program execution to the next line. If the current frame reaches the end, it continue the next line of upper frame.
19
-
15
+ Usage: next [times]
20
16
  Examples:
21
17
  next
18
+ next 1
19
+ next 7
20
+
21
+ Continue program execution to the next line. If the current frame reaches the end, it continue the next line of upper frame.
22
22
  BANNER
23
23
 
24
24
  def process
25
- throw :control_flow, command: :next, pry: pry_instance
25
+ times = validate_positive_integer!(args.first || 1)
26
+
27
+ RubyJard::ControlFlow.dispatch(:next, times: times.to_i)
26
28
  end
27
29
  end
28
30
  end
29
31
  end
30
32
 
31
33
  Pry::Commands.add_command(RubyJard::Commands::NextCommand)
34
+ Pry::Commands.alias_command 'n', 'next'
@@ -3,29 +3,32 @@
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
7
+ include RubyJard::Commands::ValidationHelpers
8
+
10
9
  group 'RubyJard'
11
10
  description 'Step into the execution of the current line'
12
11
 
13
12
  match 'step'
14
13
 
15
14
  banner <<-BANNER
16
- Usage: step
17
-
18
- Step into the execution of the current line
19
-
15
+ Usage: step [times]
20
16
  Examples:
21
17
  step
18
+ step 1
19
+ step 7
20
+
21
+ Step into the execution of the current line.
22
22
  BANNER
23
23
 
24
24
  def process
25
- throw :control_flow, command: :step, pry: pry_instance
25
+ times = validate_positive_integer!(args.first || 1)
26
+
27
+ RubyJard::ControlFlow.dispatch(:step, times: times.to_i)
26
28
  end
27
29
  end
28
30
  end
29
31
  end
30
32
 
31
33
  Pry::Commands.add_command(RubyJard::Commands::StepCommand)
34
+ Pry::Commands.alias_command 's', 'step'
@@ -0,0 +1,34 @@
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
+ include RubyJard::Commands::ValidationHelpers
8
+
9
+ group 'RubyJard'
10
+ description 'Step out of current frame and move to the execution of the upper frame'
11
+
12
+ match 'step-out'
13
+
14
+ banner <<-BANNER
15
+ Usage: step-out [times]
16
+ Examples:
17
+ step-out
18
+ step-out 1
19
+ step-out 7
20
+
21
+ Step out of current frame and move to the execution of the upper frame.
22
+ BANNER
23
+
24
+ def process
25
+ times = validate_positive_integer!(args.first || 1)
26
+
27
+ RubyJard::ControlFlow.dispatch(:step_out, times: times.to_i)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Pry::Commands.add_command(RubyJard::Commands::StepOutCommand)
34
+ Pry::Commands.alias_command 'so', 'step-out'
@@ -3,26 +3,28 @@
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
7
+ include RubyJard::Commands::ValidationHelpers
8
+
10
9
  group 'RubyJard'
11
10
  description 'Explore the frames above the current stopped line in the backtrace'
12
11
 
13
12
  match 'up'
14
13
 
15
14
  banner <<-BANNER
16
- Usage: up
17
-
18
- Explore the frames above the current stopped line in the backtrace.
19
-
15
+ Usage: up [-h] [times]
20
16
  Examples:
21
17
  up
18
+ up 1
19
+ up 7
20
+
21
+ Explore the frames above the current stopped line in the backtrace. All the C frames will be skipped.
22
22
  BANNER
23
23
 
24
24
  def process
25
- throw :control_flow, command: :up, pry: pry_instance
25
+ times = validate_positive_integer!(args.first || 1)
26
+
27
+ RubyJard::ControlFlow.dispatch(:up, times: times.to_i)
26
28
  end
27
29
  end
28
30
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyJard
4
+ module Commands
5
+ ##
6
+ # Helpers to help validate commands
7
+ module ValidationHelpers
8
+ def validate_positive_integer!(input)
9
+ input = input.to_s.strip
10
+ unless input =~ /^[+\-\d]+$/
11
+ raise ::Pry::CommandError, '`argument is not an integer'
12
+ end
13
+
14
+ input = input.to_i
15
+ raise ::Pry::CommandError, 'argument must be positive' if input <= 0
16
+
17
+ input
18
+ end
19
+
20
+ def validate_non_negative_integer!(input)
21
+ input = input.to_s.strip
22
+ unless input =~ /^[+\-\d]+$/
23
+ raise ::Pry::CommandError, 'argument is not an integer'
24
+ end
25
+
26
+ input = input.to_i
27
+ raise ::Pry::CommandError, 'argument must be positive' if input < 0
28
+
29
+ input.to_i
30
+ end
31
+
32
+ def validate_present!(input)
33
+ input = input.to_s.strip
34
+ if input.empty?
35
+ raise ::Pry::CommandError, 'argument must be present'
36
+ end
37
+
38
+ input
39
+ end
40
+
41
+ def validate_range!(input, from, to)
42
+ if input < from || input > to
43
+ raise ::Pry::CommandError, "argument must be from #{from} to #{to}"
44
+ end
45
+
46
+ input
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyJard
4
+ ##
5
+ # Another reinvent-the-wheel configuration
6
+ class Config
7
+ class << self
8
+ def smart_load
9
+ config = RubyJard::Config.new
10
+
11
+ path = File.expand_path(File.join(Dir.pwd, CONFIG_FILE_NAME))
12
+ load_config(config, path) if File.exist?(path)
13
+
14
+ path = File.expand_path(File.join('~/', CONFIG_FILE_NAME))
15
+ load_config(config, path) if File.exist?(path)
16
+
17
+ config
18
+ rescue StandardError => e
19
+ # Fallback to default setting
20
+ STDOUT.puts "Fail to load jard configurations at #{path}. Error: #{e}"
21
+ RubyJard::Config.new
22
+ end
23
+
24
+ private
25
+
26
+ def load_config(config, path)
27
+ config_content = File.read(path)
28
+ config.instance_eval(config_content)
29
+
30
+ config
31
+ end
32
+ end
33
+
34
+ attr_accessor :color_scheme, :alias_to_debugger, :layout, :enabled_screens,
35
+ :filter, :filter_included, :filter_excluded
36
+
37
+ CONFIG_FILE_NAME = '.jardrc'
38
+ DEFAULTS = [
39
+ DEFAULT_COLOR_SCHEME = '256',
40
+ DEFAULT_ALIAS_TO_DEBUGGER = false,
41
+ DEFAULT_LAYOUT = nil, # Pick layout automatically
42
+ DEFAULT_FILTER = RubyJard::PathFilter::FILTER_APPLICATION,
43
+ DEFAULT_FILTER_INCLUDED = [].freeze,
44
+ DEFAULT_FILTER_EXCLUDED = [].freeze
45
+ ].freeze
46
+
47
+ def initialize
48
+ @color_scheme = DEFAULT_COLOR_SCHEME
49
+ @alias_to_debugger = DEFAULT_ALIAS_TO_DEBUGGER
50
+ @layout = DEFAULT_LAYOUT
51
+ @enabled_screens = RubyJard::Screens.names
52
+ @filter = DEFAULT_FILTER
53
+ @filter_included = DEFAULT_FILTER_INCLUDED.dup
54
+ @filter_excluded = DEFAULT_FILTER_EXCLUDED.dup
55
+ end
56
+
57
+ def config
58
+ self
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-screen'
4
+ require 'io/console'
5
+ require 'English'
6
+
7
+ module RubyJard
8
+ # Wrapper for utilities to control screen
9
+ class Console
10
+ class << self
11
+ def start_alternative_terminal(output)
12
+ return unless output.tty?
13
+
14
+ output.print tput('smcup')
15
+ rescue RubyJard::Error
16
+ # If tput not found, fallback to hard-coded sequence.
17
+ output.print "\e[?1049h\e[22;0;0t"
18
+ end
19
+
20
+ def stop_alternative_terminal(output)
21
+ return unless output.tty?
22
+
23
+ output.print tput('rmcup')
24
+ rescue RubyJard::Error
25
+ # If tput not found, fallback to hard-coded sequence.
26
+ output.print "\e[?1049l\e[23;0;0t"
27
+ end
28
+
29
+ def move_to(output, x, y)
30
+ return unless output.tty?
31
+
32
+ output.print format("\e[%<row>d;%<col>dH", row: y + 1, col: x + 1)
33
+ end
34
+
35
+ def screen_size(output)
36
+ return [0, 0] unless output.tty?
37
+
38
+ [TTY::Screen.width, TTY::Screen.height]
39
+ end
40
+
41
+ def clear_screen(output)
42
+ return unless output.tty?
43
+
44
+ output.print "\e[3J"
45
+ end
46
+
47
+ def clear_screen_to_end(output)
48
+ return unless output.tty?
49
+
50
+ output.print "\e[0J"
51
+ end
52
+
53
+ def disable_cursor!(output = STDOUT)
54
+ return unless output.tty?
55
+
56
+ output.print tput('civis')
57
+ rescue RubyJard::Error
58
+ # If tput not found, fallback to hard-coded sequence.
59
+ output.print "\e[?25l"
60
+ end
61
+
62
+ def enable_cursor!(output = STDOUT)
63
+ return unless output.tty?
64
+
65
+ output.print tput('cnorm')
66
+ rescue RubyJard::Error
67
+ # If tput not found, fallback to hard-coded sequence.
68
+ output.print "\e[?12l\e[?25h"
69
+ end
70
+
71
+ def getch(input, timeout)
72
+ input.read_nonblock(255)
73
+ rescue IO::WaitReadable
74
+ io = IO.select([input], nil, nil, timeout)
75
+ if io.nil?
76
+ nil
77
+ else
78
+ retry
79
+ end
80
+ rescue IO::WaitWritable
81
+ nil
82
+ end
83
+
84
+ def raw!(output = STDOUT)
85
+ return unless output.tty?
86
+
87
+ begin
88
+ output.raw!
89
+ rescue StandardError
90
+ stty('raw')
91
+ end
92
+ end
93
+
94
+ def cooked!(output = STDOUT)
95
+ return unless output.tty?
96
+
97
+ begin
98
+ output.cooked!
99
+ rescue StandardError
100
+ # If stty not found, or raise error, nothing I can do
101
+ stty('-raw')
102
+ end
103
+ end
104
+
105
+ def disable_echo!(output = STDOUT)
106
+ return unless output.tty?
107
+
108
+ begin
109
+ output.echo = false
110
+ rescue StandardError
111
+ # If stty not found, or raise error, nothing I can do
112
+ stty('-echo')
113
+ end
114
+ end
115
+
116
+ def enable_echo!(output = STDOUT)
117
+ return unless output.tty?
118
+
119
+ begin
120
+ output.echo = true
121
+ rescue StandardError
122
+ # If stty not found, or raise error, nothing I can do
123
+ stty('echo')
124
+ end
125
+ end
126
+
127
+ def cached_tput
128
+ @cached_tput ||= {}
129
+ end
130
+
131
+ def tput(*args)
132
+ command = "tput #{args.join(' ')}"
133
+ return cached_tput[command] unless cached_tput[command].nil?
134
+
135
+ output = `#{command}`
136
+ if $CHILD_STATUS.success?
137
+ cached_tput[command] = output
138
+ else
139
+ raise RubyJard::Error, "Fail to call `#{command}`: #{$CHILD_STATUS}"
140
+ end
141
+ rescue StandardError => e
142
+ raise RubyJard::Error, "Fail to call `#{command}`. Error: #{e}"
143
+ end
144
+
145
+ def stty(*args)
146
+ command = "stty #{args.join(' ')}"
147
+ output = `#{command}`
148
+ if $CHILD_STATUS.success?
149
+ output
150
+ else
151
+ raise RubyJard::Error, "Fail to call `#{command}`: #{$CHILD_STATUS}"
152
+ end
153
+ rescue StandardError => e
154
+ raise RubyJard::Error, "Fail to call `#{command}`. Error: #{e}"
155
+ end
156
+ end
157
+ end
158
+ end