ruby_jard 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/rspec.yml +26 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +3 -0
- data/lib/ruby_jard.rb +8 -2
- data/lib/ruby_jard/commands/continue_command.rb +1 -1
- data/lib/ruby_jard/commands/exit_command.rb +1 -1
- data/lib/ruby_jard/commands/jard/output_command.rb +1 -0
- data/lib/ruby_jard/config.rb +11 -4
- data/lib/ruby_jard/console.rb +47 -19
- data/lib/ruby_jard/control_flow.rb +1 -1
- data/lib/ruby_jard/decorators/color_decorator.rb +13 -4
- data/lib/ruby_jard/decorators/rails_decorator.rb +8 -2
- data/lib/ruby_jard/keys.rb +2 -1
- data/lib/ruby_jard/pager.rb +8 -2
- data/lib/ruby_jard/repl_processor.rb +6 -4
- data/lib/ruby_jard/repl_proxy.rb +41 -16
- data/lib/ruby_jard/screen_manager.rb +8 -12
- data/lib/ruby_jard/session.rb +34 -29
- data/lib/ruby_jard/span.rb +1 -1
- data/lib/ruby_jard/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00607fcc7c6b4de0a2bb272624357ef7f9c8e3a0aa4a46783f127b094fc0a6f9
|
4
|
+
data.tar.gz: 6bd10f20ba123f290b786a875eb5879cf343e126a1a31d420387691e7ee6d5e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64ca81f056c51fd717cb1b1caae5f8490bb71ea8f5b93b587451ab8115e546fea617eb0eb0d4357a281c255ff862d33a024ccae0301a3148800dff1332242662
|
7
|
+
data.tar.gz: b1063583e9f8c31d9ec3e092e8d5e99b339b3d016b2516d2c4e9b27843071099916f8444c5bc648ba506e00d794bdecc030c06b838895385aff61981d696a6ca
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
3
|
+
# Please see the documentation for all configuration options:
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5
|
+
|
6
|
+
version: 2
|
7
|
+
updates:
|
8
|
+
- package-ecosystem: "bundler" # See documentation for possible values
|
9
|
+
directory: "/" # Location of package manifests
|
10
|
+
schedule:
|
11
|
+
interval: "daily"
|
data/.github/workflows/rspec.yml
CHANGED
@@ -32,8 +32,17 @@ jobs:
|
|
32
32
|
uses: ruby/setup-ruby@v1
|
33
33
|
with:
|
34
34
|
ruby-version: ${{ matrix.ruby }}
|
35
|
+
- uses: actions/checkout@v2
|
36
|
+
with:
|
37
|
+
repository: tmux/tmux
|
38
|
+
path: 'tmux'
|
39
|
+
ref: '3.1b'
|
40
|
+
- name: Install dependencies for tmux
|
41
|
+
run: sudo apt install -y libevent-dev
|
35
42
|
- name: Install tmux
|
36
|
-
run: sudo
|
43
|
+
run: cd tmux && sh autogen.sh && ./configure && make && sudo make install
|
44
|
+
- name: Tmux version
|
45
|
+
run: tmux -V
|
37
46
|
- name: Start tmux
|
38
47
|
run: tmux start-server
|
39
48
|
- name: Start dummy tmux session
|
@@ -42,6 +51,8 @@ jobs:
|
|
42
51
|
run: ruby spec/wait_for_tmux.rb
|
43
52
|
- name: Install dependencies
|
44
53
|
run: bundle install
|
54
|
+
- name: Kill tmux
|
55
|
+
run: tmux kill-server || true
|
45
56
|
- name: Run tests
|
46
57
|
run: bundle exec parallel_rspec spec/
|
47
58
|
test-macos:
|
@@ -69,28 +80,39 @@ jobs:
|
|
69
80
|
- name: Run tests
|
70
81
|
run: bundle exec parallel_rspec -n 2 spec/
|
71
82
|
test-byebug:
|
72
|
-
runs-on: ubuntu-latest
|
73
83
|
strategy:
|
74
84
|
fail-fast: false
|
75
85
|
matrix:
|
76
86
|
byebug: [9.1.0, 10.0.2]
|
77
87
|
env:
|
78
88
|
BUNDLE_GEMFILE: "./spec/gemfiles/Gemfile-byebug-${{ matrix.byebug }}"
|
89
|
+
runs-on: ubuntu-latest
|
79
90
|
steps:
|
80
91
|
- uses: actions/checkout@v2
|
81
92
|
- name: Set up Ruby
|
82
93
|
uses: ruby/setup-ruby@v1
|
83
94
|
with:
|
84
95
|
ruby-version: 2.5
|
96
|
+
- uses: actions/checkout@v2
|
97
|
+
with:
|
98
|
+
repository: tmux/tmux
|
99
|
+
path: 'tmux'
|
100
|
+
ref: '3.1b'
|
101
|
+
- name: Install dependencies for tmux
|
102
|
+
run: sudo apt install -y libevent-dev
|
85
103
|
- name: Install tmux
|
86
|
-
run: sudo
|
104
|
+
run: cd tmux && sh autogen.sh && ./configure && make && sudo make install
|
105
|
+
- name: Tmux version
|
106
|
+
run: tmux -V
|
87
107
|
- name: Start tmux
|
88
108
|
run: tmux start-server
|
89
|
-
- name:
|
109
|
+
- name: Start dummy tmux session
|
90
110
|
run: tmux new-session -t dummy -d
|
91
111
|
- name: Wait for tmux
|
92
112
|
run: ruby spec/wait_for_tmux.rb
|
93
113
|
- name: Install dependencies
|
94
114
|
run: bundle install
|
115
|
+
- name: Kill tmux
|
116
|
+
run: tmux kill-server || true
|
95
117
|
- name: Run tests
|
96
118
|
run: bundle exec parallel_rspec spec/
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.3.1]
|
4
|
+
This release fixes bunch of bugs, and performance issues reported by the users after beta launch. No new features are introduced.
|
5
|
+
|
6
|
+
- Pry and Byebug backward compatibility: [#39](https://github.com/nguyenquangminh0711/ruby_jard/issues/39), [#45](https://github.com/nguyenquangminh0711/ruby_jard/issues/45)
|
7
|
+
- Error with non-UTF8 encoding in the output: [#55](https://github.com/nguyenquangminh0711/ruby_jard/issues/55)
|
8
|
+
- Ctrl+D not working: [#34](https://github.com/nguyenquangminh0711/ruby_jard/issues/34)
|
9
|
+
- Errors if putting jard with `<%= jard %>` in ERB: [#35](https://github.com/nguyenquangminh0711/ruby_jard/issues/35)
|
10
|
+
- Handle standard stream redirections, and prevent Jard from attachment in invalid TTY device: [#38](https://github.com/nguyenquangminh0711/ruby_jard/issues/38), [#46](https://github.com/nguyenquangminh0711/ruby_jard/issues/46), [#53](https://github.com/nguyenquangminh0711/ruby_jard/issues/53)
|
11
|
+
- Bring back auto-resize when window size changes: [#40](https://github.com/nguyenquangminh0711/ruby_jard/issues/40)
|
12
|
+
- Improve performance after `exit` command: [#49](https://github.com/nguyenquangminh0711/ruby_jard/issues/49)
|
13
|
+
- Handle edge cases in Jard color decorator: [#54](https://github.com/nguyenquangminh0711/ruby_jard/issues/54)
|
14
|
+
- Escape all special characters and line feeds before printing stuff into the screen: [#57](https://github.com/nguyenquangminh0711/ruby_jard/issues/57)
|
15
|
+
|
3
16
|
## [0.3.0 - Beta 1]
|
4
17
|
- Filter feature
|
5
18
|
- New variable screen look and feel
|
data/Gemfile
CHANGED
data/lib/ruby_jard.rb
CHANGED
@@ -14,7 +14,6 @@ require 'ruby_jard/keys'
|
|
14
14
|
require 'ruby_jard/key_binding'
|
15
15
|
require 'ruby_jard/key_bindings'
|
16
16
|
require 'ruby_jard/repl_proxy'
|
17
|
-
require 'ruby_jard/repl_processor'
|
18
17
|
require 'ruby_jard/screen_manager'
|
19
18
|
require 'ruby_jard/reflection'
|
20
19
|
|
@@ -98,13 +97,20 @@ module RubyJard
|
|
98
97
|
def self.config
|
99
98
|
@config ||= RubyJard::Config.smart_load
|
100
99
|
end
|
100
|
+
|
101
|
+
def self.all_files
|
102
|
+
Dir.glob(File.join(File.expand_path(__dir__, './lib'), '**', '*.rb')) +
|
103
|
+
Dir.glob(File.join(File.expand_path(__dir__, './lib'), '*.rb')) +
|
104
|
+
Dir.glob(File.join(File.expand_path(__dir__, './bin'), '**', '*.rb')) +
|
105
|
+
Dir.glob(File.join(File.expand_path(__dir__, './bin'), '*.rb'))
|
106
|
+
end
|
101
107
|
end
|
102
108
|
|
103
109
|
##
|
104
110
|
# Monkey-patch Kernel module to allow putting jard command anywhere.
|
105
111
|
module Kernel
|
106
112
|
def jard
|
107
|
-
RubyJard::Session.
|
113
|
+
RubyJard::Session.attach
|
108
114
|
end
|
109
115
|
|
110
116
|
if RubyJard.config.alias_to_debugger
|
@@ -14,7 +14,7 @@ module RubyJard
|
|
14
14
|
Examples:
|
15
15
|
continue
|
16
16
|
|
17
|
-
Continue
|
17
|
+
Continue the execution of your program to the end, or stop at the first dynamic break point or `jard` attachment command.
|
18
18
|
BANNER
|
19
19
|
|
20
20
|
def process
|
@@ -14,7 +14,7 @@ module RubyJard
|
|
14
14
|
Examples:
|
15
15
|
exit
|
16
16
|
|
17
|
-
Exit
|
17
|
+
Exit the execution of the program. Interally, when `jard` receives this command, it removes all debugging hooks, and triggers `::Kernel.exit`. Some long-running processes like `puma` or `sidekiq` may capture this event, treat it as an error, and recover to keep the processes running. In such cases, it's recommended to use `continue` command instead.
|
18
18
|
BANNER
|
19
19
|
|
20
20
|
def process
|
data/lib/ruby_jard/config.rb
CHANGED
@@ -8,6 +8,14 @@ module RubyJard
|
|
8
8
|
def smart_load
|
9
9
|
config = RubyJard::Config.new
|
10
10
|
|
11
|
+
unless ENV['JARD_CONFIG_FILE'].nil?
|
12
|
+
unless File.exist?(ENV['JARD_CONFIG_FILE'])
|
13
|
+
raise "Config file '#{ENV['JARD_CONFIG_FILE']}' does not exist"
|
14
|
+
end
|
15
|
+
|
16
|
+
return load_config(config, ENV['JARD_CONFIG_FILE'])
|
17
|
+
end
|
18
|
+
|
11
19
|
path = File.expand_path(File.join(Dir.pwd, CONFIG_FILE_NAME))
|
12
20
|
load_config(config, path) if File.exist?(path)
|
13
21
|
|
@@ -15,10 +23,6 @@ module RubyJard
|
|
15
23
|
load_config(config, path) if File.exist?(path)
|
16
24
|
|
17
25
|
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
26
|
end
|
23
27
|
|
24
28
|
private
|
@@ -28,6 +32,9 @@ module RubyJard
|
|
28
32
|
config.instance_eval(config_content)
|
29
33
|
|
30
34
|
config
|
35
|
+
rescue SyntaxError, StandardError => e
|
36
|
+
# Fallback to default setting
|
37
|
+
raise "Fail to load jard configurations at #{path}. Error: #{e}"
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
data/lib/ruby_jard/console.rb
CHANGED
@@ -8,22 +8,45 @@ module RubyJard
|
|
8
8
|
# Wrapper for utilities to control screen
|
9
9
|
class Console
|
10
10
|
class << self
|
11
|
-
def
|
12
|
-
return unless output.tty?
|
11
|
+
def attachable?
|
12
|
+
return false unless output.tty?
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
# If tput not found, fallback to hard-coded sequence.
|
17
|
-
output.print "\e[?1049h\e[22;0;0t"
|
14
|
+
width, height = screen_size(output)
|
15
|
+
width != 0 && height != 0
|
18
16
|
end
|
19
17
|
|
20
|
-
def
|
21
|
-
|
18
|
+
def redirected?
|
19
|
+
output != $stdout
|
20
|
+
end
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
output
|
22
|
+
def output
|
23
|
+
return @output if defined?(@output)
|
24
|
+
|
25
|
+
@output =
|
26
|
+
if STDOUT.tty?
|
27
|
+
STDOUT
|
28
|
+
else
|
29
|
+
begin
|
30
|
+
File.open('/dev/tty', 'w+')
|
31
|
+
rescue StandardError
|
32
|
+
STDOUT # Give up now.
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def input
|
38
|
+
return @input if defined?(@input)
|
39
|
+
|
40
|
+
@input =
|
41
|
+
if STDIN.tty?
|
42
|
+
STDIN
|
43
|
+
else
|
44
|
+
begin
|
45
|
+
File.open('/dev/tty', 'r+')
|
46
|
+
rescue StandardError
|
47
|
+
STDIN # Give up.
|
48
|
+
end
|
49
|
+
end
|
27
50
|
end
|
28
51
|
|
29
52
|
def move_to(output, x, y)
|
@@ -35,7 +58,12 @@ module RubyJard
|
|
35
58
|
def screen_size(output)
|
36
59
|
return [0, 0] unless output.tty?
|
37
60
|
|
38
|
-
|
61
|
+
if output.respond_to?(:winsize)
|
62
|
+
height, width = output.winsize
|
63
|
+
[width, height]
|
64
|
+
else
|
65
|
+
[TTY::Screen.width, TTY::Screen.height]
|
66
|
+
end
|
39
67
|
end
|
40
68
|
|
41
69
|
def clear_screen(output)
|
@@ -50,7 +78,7 @@ module RubyJard
|
|
50
78
|
output.print "\e[0J"
|
51
79
|
end
|
52
80
|
|
53
|
-
def disable_cursor!(output
|
81
|
+
def disable_cursor!(output)
|
54
82
|
return unless output.tty?
|
55
83
|
|
56
84
|
output.print tput('civis')
|
@@ -59,7 +87,7 @@ module RubyJard
|
|
59
87
|
output.print "\e[?25l"
|
60
88
|
end
|
61
89
|
|
62
|
-
def enable_cursor!(output
|
90
|
+
def enable_cursor!(output)
|
63
91
|
return unless output.tty?
|
64
92
|
|
65
93
|
output.print tput('cnorm')
|
@@ -81,7 +109,7 @@ module RubyJard
|
|
81
109
|
nil
|
82
110
|
end
|
83
111
|
|
84
|
-
def raw!(output
|
112
|
+
def raw!(output)
|
85
113
|
return unless output.tty?
|
86
114
|
|
87
115
|
begin
|
@@ -91,7 +119,7 @@ module RubyJard
|
|
91
119
|
end
|
92
120
|
end
|
93
121
|
|
94
|
-
def cooked!(output
|
122
|
+
def cooked!(output)
|
95
123
|
return unless output.tty?
|
96
124
|
|
97
125
|
begin
|
@@ -102,7 +130,7 @@ module RubyJard
|
|
102
130
|
end
|
103
131
|
end
|
104
132
|
|
105
|
-
def disable_echo!(output
|
133
|
+
def disable_echo!(output)
|
106
134
|
return unless output.tty?
|
107
135
|
|
108
136
|
begin
|
@@ -113,7 +141,7 @@ module RubyJard
|
|
113
141
|
end
|
114
142
|
end
|
115
143
|
|
116
|
-
def enable_echo!(output
|
144
|
+
def enable_echo!(output)
|
117
145
|
return unless output.tty?
|
118
146
|
|
119
147
|
begin
|
@@ -37,17 +37,28 @@ module RubyJard
|
|
37
37
|
|
38
38
|
def decorate(style_names, content)
|
39
39
|
attributes = nil
|
40
|
+
foreground = nil
|
41
|
+
background = nil
|
42
|
+
|
40
43
|
if style_names.is_a?(Symbol)
|
41
44
|
styles = @color_scheme.styles_for(style_names)
|
42
45
|
else
|
43
46
|
attributes = translate_styles(style_names)
|
44
47
|
styles = @color_scheme.styles_for(style_names.shift)
|
45
48
|
end
|
46
|
-
|
47
|
-
|
49
|
+
|
50
|
+
if styles.is_a?(Array)
|
51
|
+
foreground = translate_color(styles.shift, true)
|
52
|
+
background = translate_color(styles.shift, false)
|
53
|
+
elsif !styles.nil?
|
54
|
+
raise RubyJard::Error, "Styles for #{style_names.inspect} must be an array"
|
55
|
+
end
|
56
|
+
|
48
57
|
"#{foreground}#{background}#{attributes}#{content}#{CSI_RESET}"
|
49
58
|
end
|
50
59
|
|
60
|
+
private
|
61
|
+
|
51
62
|
def translate_color(color, foreground)
|
52
63
|
if (matches = HEX_PATTERN_6.match(color.to_s))
|
53
64
|
red = matches[1].to_i(16)
|
@@ -70,8 +81,6 @@ module RubyJard
|
|
70
81
|
end
|
71
82
|
end
|
72
83
|
|
73
|
-
private
|
74
|
-
|
75
84
|
def translate_styles(styles = [])
|
76
85
|
styles.map { |key| STYLES_CSI_MAP[key] }.compact.join
|
77
86
|
end
|
@@ -89,7 +89,9 @@ module RubyJard
|
|
89
89
|
if variable.respond_to?(:loaded?) && variable.loaded?
|
90
90
|
spans = []
|
91
91
|
label = RubyJard::Span.new(
|
92
|
-
content: RubyJard::Reflection.call_to_s(variable).chomp('>'),
|
92
|
+
content: RubyJard::Reflection.call_to_s(variable).chomp('>'),
|
93
|
+
styles: :text_primary,
|
94
|
+
margin_right: variable.length >= 1 ? 1 : 0
|
93
95
|
)
|
94
96
|
spans << label
|
95
97
|
spans += @attributes_decorator.inline_values(
|
@@ -99,6 +101,10 @@ module RubyJard
|
|
99
101
|
)
|
100
102
|
spans << RubyJard::Span.new(content: '>', styles: :text_primary)
|
101
103
|
|
104
|
+
if variable.length <= 0
|
105
|
+
spans << RubyJard::Span.new(content: '(empty)', margin_left: 1, styles: :text_primary)
|
106
|
+
end
|
107
|
+
|
102
108
|
spans
|
103
109
|
else
|
104
110
|
relation_summary(variable, line_limit)
|
@@ -138,7 +144,7 @@ module RubyJard
|
|
138
144
|
width = overview.length + 1 + 12
|
139
145
|
spans = [RubyJard::Span.new(content: overview, styles: :text_primary)]
|
140
146
|
if RubyJard::Reflection.call_respond_to?(variable, :to_sql) && width < line_limit
|
141
|
-
detail = variable.to_sql
|
147
|
+
detail = variable.to_sql.inspect
|
142
148
|
detail = detail[0..line_limit - width - 2] + '…' if width + detail.length < line_limit
|
143
149
|
spans << RubyJard::Span.new(content: detail, styles: :text_dim, margin_left: 1)
|
144
150
|
end
|
data/lib/ruby_jard/keys.rb
CHANGED
@@ -42,7 +42,8 @@ module RubyJard
|
|
42
42
|
F7 => (ACTION_STEP = :step),
|
43
43
|
SHIFT_F7 => (ACTION_STEP_OUT = :step_out),
|
44
44
|
F8 => (ACTION_NEXT = :next),
|
45
|
-
F9 => (ACTION_CONTINUE = :continue)
|
45
|
+
F9 => (ACTION_CONTINUE = :continue),
|
46
|
+
CTRL_D => ACTION_CONTINUE
|
46
47
|
}.freeze
|
47
48
|
end
|
48
49
|
end
|
data/lib/ruby_jard/pager.rb
CHANGED
@@ -46,7 +46,13 @@ module RubyJard
|
|
46
46
|
@pager_start_at_the_end = pager_start_at_the_end
|
47
47
|
@prompt = prompt
|
48
48
|
|
49
|
-
|
49
|
+
# There are two cases:
|
50
|
+
# - If the real pager (less) is triggered, it works on a real tty (fetched
|
51
|
+
# from /dev/tty), in which, the same as RubyJard::Console.output
|
52
|
+
# - Otherwise, it writes directly into pry's REPL output.
|
53
|
+
# That's why there should be two output here
|
54
|
+
@tty_output = RubyJard::Console.redirected? ? RubyJard::Console.output : pry_instance.output
|
55
|
+
@window_width, @window_height = RubyJard::Console.screen_size(RubyJard::Console.output)
|
50
56
|
@tracker = JardPageTracker.new(@window_height, @window_width)
|
51
57
|
@pager = force_open ? open_pager : nil
|
52
58
|
end
|
@@ -93,7 +99,7 @@ module RubyJard
|
|
93
99
|
|
94
100
|
IO.popen(
|
95
101
|
less_command.join(' '), 'w',
|
96
|
-
out: @
|
102
|
+
out: @tty_output, err: @tty_output
|
97
103
|
)
|
98
104
|
end
|
99
105
|
|
@@ -29,7 +29,8 @@ module RubyJard
|
|
29
29
|
@repl_proxy = RubyJard::ReplProxy.new(
|
30
30
|
key_bindings: RubyJard.global_key_bindings
|
31
31
|
)
|
32
|
-
@previous_flow =
|
32
|
+
@previous_flow = RubyJard::ControlFlow.new(:next)
|
33
|
+
@output = RubyJard::Console.output
|
33
34
|
end
|
34
35
|
|
35
36
|
def at_line
|
@@ -123,10 +124,10 @@ module RubyJard
|
|
123
124
|
next_frame = find_frame(options[:frame].to_i)
|
124
125
|
if next_frame.nil?
|
125
126
|
# There must be an error in outer validators
|
126
|
-
|
127
|
+
@output.puts 'Error: Frame not found. There should be an error with Jard.'
|
127
128
|
process_commands(false)
|
128
129
|
elsif next_frame.c_frame?
|
129
|
-
|
130
|
+
@output.puts "Error: Frame #{next_frame} is a c-frame. Not able to inspect c layer!"
|
130
131
|
process_commands(false)
|
131
132
|
else
|
132
133
|
RubyJard::Session.frame = next_frame.real_pos
|
@@ -135,11 +136,12 @@ module RubyJard
|
|
135
136
|
end
|
136
137
|
|
137
138
|
def handle_continue_command(_options = {})
|
138
|
-
|
139
|
+
@output.puts '▸▸ Program resumed ▸▸'
|
139
140
|
Byebug.stop if Byebug.stoppable?
|
140
141
|
end
|
141
142
|
|
142
143
|
def handle_exit_command(_options = {})
|
144
|
+
Byebug.stop if Byebug.stoppable?
|
143
145
|
Kernel.exit
|
144
146
|
end
|
145
147
|
|
data/lib/ruby_jard/repl_proxy.rb
CHANGED
@@ -123,7 +123,10 @@ module RubyJard
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
-
def initialize(key_bindings: nil)
|
126
|
+
def initialize(key_bindings: nil, input: RubyJard::Console.input, output: RubyJard::Console.output)
|
127
|
+
@input = input
|
128
|
+
@output = output
|
129
|
+
|
127
130
|
@state = ReplState.new
|
128
131
|
|
129
132
|
@pry_input_pipe_read, @pry_input_pipe_write = IO.pipe
|
@@ -137,15 +140,23 @@ module RubyJard
|
|
137
140
|
|
138
141
|
@pry_pty_output_thread = Thread.new { pry_pty_output }
|
139
142
|
@pry_pty_output_thread.name = '<<Jard: Pty Output Thread>>'
|
143
|
+
|
144
|
+
Signal.trap('SIGWINCH') do
|
145
|
+
@main_thread.raise FlowInterrupt.new('Resize event', RubyJard::ControlFlow.new(:list))
|
146
|
+
end
|
140
147
|
end
|
141
148
|
|
149
|
+
# rubocop:disable Metrics/MethodLength
|
142
150
|
def repl(current_binding)
|
143
151
|
@state.ready!
|
144
152
|
@openning_pager = false
|
145
153
|
|
146
|
-
RubyJard::Console.disable_echo!
|
147
|
-
RubyJard::Console.raw!
|
154
|
+
RubyJard::Console.disable_echo!(@output)
|
155
|
+
RubyJard::Console.raw!(@output)
|
148
156
|
|
157
|
+
# Internally, Pry sneakily updates Readline to global output config
|
158
|
+
# when STDOUT is piping regardless of what I pass into Pry instance.
|
159
|
+
Pry.config.output = @pry_output_pty_write
|
149
160
|
Readline.input = @pry_input_pipe_read
|
150
161
|
Readline.output = @pry_output_pty_write
|
151
162
|
@pry.binding_stack.clear
|
@@ -168,41 +179,47 @@ module RubyJard
|
|
168
179
|
sleep PTY_OUTPUT_TIMEOUT until @state.exited?
|
169
180
|
RubyJard::ControlFlow.dispatch(e.flow)
|
170
181
|
ensure
|
171
|
-
RubyJard::Console.enable_echo!
|
172
|
-
RubyJard::Console.cooked!
|
173
|
-
Readline.input =
|
174
|
-
Readline.output =
|
182
|
+
RubyJard::Console.enable_echo!(@output)
|
183
|
+
RubyJard::Console.cooked!(@output)
|
184
|
+
Readline.input = @input
|
185
|
+
Readline.output = @output
|
186
|
+
Pry.config.output = @output
|
175
187
|
@key_listen_thread&.exit if @key_listen_thread&.alive?
|
176
188
|
@pry_input_thread&.exit if @pry_input_thread&.alive?
|
177
189
|
@state.exited!
|
178
190
|
end
|
191
|
+
# rubocop:enable Metrics/MethodLength
|
179
192
|
|
180
193
|
private
|
181
194
|
|
182
195
|
def read_key
|
183
|
-
RubyJard::Console.getch(
|
196
|
+
RubyJard::Console.getch(@input, KEY_READ_TIMEOUT)
|
184
197
|
end
|
185
198
|
|
186
199
|
def pry_pty_output
|
187
200
|
loop do
|
188
201
|
if @state.exiting?
|
189
202
|
if @pry_output_pty_read.ready?
|
190
|
-
|
203
|
+
write_output(@pry_output_pty_read.read_nonblock(2048))
|
191
204
|
else
|
192
205
|
@state.exited!
|
193
206
|
end
|
194
207
|
elsif @state.exited?
|
195
208
|
sleep PTY_OUTPUT_TIMEOUT
|
196
209
|
else
|
197
|
-
|
198
|
-
unless
|
199
|
-
|
210
|
+
content = @pry_output_pty_read.read_nonblock(2048)
|
211
|
+
unless content.nil?
|
212
|
+
write_output(content)
|
200
213
|
end
|
201
214
|
end
|
202
215
|
rescue IO::WaitReadable, IO::WaitWritable
|
203
216
|
# Retry
|
204
217
|
sleep PTY_OUTPUT_TIMEOUT
|
205
218
|
end
|
219
|
+
rescue StandardError
|
220
|
+
# This thread shoud never die, or the user may be freezed, and cannot type anything
|
221
|
+
sleep 0.5
|
222
|
+
retry
|
206
223
|
end
|
207
224
|
|
208
225
|
def pry_repl(current_binding)
|
@@ -312,25 +329,33 @@ module RubyJard
|
|
312
329
|
def pry_hooks
|
313
330
|
hooks = Pry::Hooks.default
|
314
331
|
hooks.add_hook(:after_read, :jard_proxy_acquire_lock) do |_read_string, _pry|
|
315
|
-
RubyJard::Console.cooked!
|
332
|
+
RubyJard::Console.cooked!(@output)
|
316
333
|
@state.processing!
|
317
334
|
# Sleep 2 ticks, wait for pry to print out all existing output in the queue
|
318
335
|
sleep PTY_OUTPUT_TIMEOUT * 2
|
319
336
|
end
|
320
337
|
hooks.add_hook(:after_handle_line, :jard_proxy_release_lock) do
|
321
|
-
RubyJard::Console.raw!
|
338
|
+
RubyJard::Console.raw!(@output)
|
322
339
|
@state.ready!
|
323
340
|
end
|
324
341
|
hooks.add_hook(:before_pager, :jard_proxy_before_pager) do
|
325
342
|
@openning_pager = true
|
326
343
|
|
327
344
|
@state.processing!
|
328
|
-
RubyJard::Console.cooked!
|
345
|
+
RubyJard::Console.cooked!(@output)
|
329
346
|
end
|
330
347
|
hooks.add_hook(:after_pager, :jard_proxy_after_pager) do
|
331
348
|
@openning_pager = false
|
332
349
|
@state.ready!
|
333
|
-
RubyJard::Console.raw!
|
350
|
+
RubyJard::Console.raw!(@output)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def write_output(content)
|
355
|
+
if RubyJard::Console.redirected?
|
356
|
+
@output.write content.force_encoding('UTF-8')
|
357
|
+
else
|
358
|
+
@output.write content.force_encoding('UTF-8'), from_jard: true
|
334
359
|
end
|
335
360
|
end
|
336
361
|
end
|
@@ -38,7 +38,7 @@ module RubyJard
|
|
38
38
|
|
39
39
|
attr_reader :output, :output_storage
|
40
40
|
|
41
|
-
def initialize(output:
|
41
|
+
def initialize(output: RubyJard::Console.output)
|
42
42
|
@output = output
|
43
43
|
@screens = {}
|
44
44
|
@started = false
|
@@ -68,9 +68,9 @@ module RubyJard
|
|
68
68
|
|
69
69
|
@started = false
|
70
70
|
|
71
|
-
RubyJard::Console.cooked!
|
72
|
-
RubyJard::Console.enable_echo!
|
73
|
-
RubyJard::Console.enable_cursor!
|
71
|
+
RubyJard::Console.cooked!(@output)
|
72
|
+
RubyJard::Console.enable_echo!(@output)
|
73
|
+
RubyJard::Console.enable_cursor!(@output)
|
74
74
|
end
|
75
75
|
|
76
76
|
def draw_screens
|
@@ -78,7 +78,7 @@ module RubyJard
|
|
78
78
|
@updating = true
|
79
79
|
|
80
80
|
RubyJard::Console.clear_screen(@output)
|
81
|
-
RubyJard::Console.disable_cursor!
|
81
|
+
RubyJard::Console.disable_cursor!(@output)
|
82
82
|
width, height = RubyJard::Console.screen_size(@output)
|
83
83
|
|
84
84
|
@layouts = calculate_layouts(width, height)
|
@@ -102,9 +102,9 @@ module RubyJard
|
|
102
102
|
draw_error(e, height)
|
103
103
|
ensure
|
104
104
|
# You don't want to mess up previous user TTY no matter happens
|
105
|
-
RubyJard::Console.cooked!
|
106
|
-
RubyJard::Console.enable_echo!
|
107
|
-
RubyJard::Console.enable_cursor!
|
105
|
+
RubyJard::Console.cooked!(@output)
|
106
|
+
RubyJard::Console.enable_echo!(@output)
|
107
|
+
RubyJard::Console.enable_cursor!(@output)
|
108
108
|
@updating = false
|
109
109
|
end
|
110
110
|
|
@@ -128,10 +128,6 @@ module RubyJard
|
|
128
128
|
RubyJard.error(exception)
|
129
129
|
end
|
130
130
|
|
131
|
-
def puts(content)
|
132
|
-
@output.write "#{content}\n", from_jard: true
|
133
|
-
end
|
134
|
-
|
135
131
|
private
|
136
132
|
|
137
133
|
def calculate_layouts(width, height)
|
data/lib/ruby_jard/session.rb
CHANGED
@@ -13,7 +13,7 @@ module RubyJard
|
|
13
13
|
extend Forwardable
|
14
14
|
|
15
15
|
def_delegators :instance,
|
16
|
-
:
|
16
|
+
:lock,
|
17
17
|
:sync, :should_stop?,
|
18
18
|
:step_over, :step_into, :frame=,
|
19
19
|
:threads, :current_frame, :current_thread, :current_backtrace,
|
@@ -22,6 +22,21 @@ module RubyJard
|
|
22
22
|
def instance
|
23
23
|
@instance ||= new
|
24
24
|
end
|
25
|
+
|
26
|
+
def attach
|
27
|
+
unless RubyJard::Console.attachable?
|
28
|
+
$stdout.puts 'Failed to attach. Jard could not detect a valid tty device.'
|
29
|
+
$stdout.puts 'This bug occurs when the process Jard trying to access is a non-interactive environment '\
|
30
|
+
' such as docker, daemon, sub-processes, etc.'
|
31
|
+
$stdout.puts 'If you are confused, please submit an issue in https://github.com/nguyenquangminh0711/ruby_jard/issues.'
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
instance.start unless instance.started?
|
36
|
+
|
37
|
+
Byebug.attach
|
38
|
+
Byebug.current_context.step_out(3, true)
|
39
|
+
end
|
25
40
|
end
|
26
41
|
|
27
42
|
OUTPUT_BUFFER_LENGTH = 10_000 # 10k lines
|
@@ -52,31 +67,28 @@ module RubyJard
|
|
52
67
|
Byebug::Setting[:autolist] = false
|
53
68
|
Byebug::Setting[:autoirb] = false
|
54
69
|
Byebug::Setting[:autopry] = false
|
70
|
+
|
71
|
+
require 'ruby_jard/repl_processor'
|
55
72
|
Byebug::Context.processor = RubyJard::ReplProcessor
|
56
73
|
# Exclude all files in Ruby Jard source code from the stacktrace.
|
57
|
-
Byebug::Context.ignored_files = Byebug::Context.all_files +
|
58
|
-
File.join(
|
59
|
-
File.expand_path(__dir__, '../lib'),
|
60
|
-
'**',
|
61
|
-
'*.rb'
|
62
|
-
)
|
63
|
-
)
|
64
|
-
# rubocop:disable Lint/NestedMethodDefinition
|
65
|
-
def $stdout.write(*string, from_jard: false)
|
66
|
-
# NOTE: `RubyJard::ScreenManager.instance` is a must. Jard doesn't work well with delegator
|
67
|
-
# TODO: Debug and fix the issues permanently
|
68
|
-
if from_jard
|
69
|
-
super(*string)
|
70
|
-
return
|
71
|
-
end
|
74
|
+
Byebug::Context.ignored_files = Byebug::Context.all_files + RubyJard.all_files
|
72
75
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
+
$stdout.send(:instance_eval, <<-CODE)
|
77
|
+
def write(*string, from_jard: false)
|
78
|
+
# NOTE: `RubyJard::ScreenManager.instance` is a must. Jard doesn't work well with delegator
|
79
|
+
# TODO: Debug and fix the issues permanently
|
80
|
+
if from_jard
|
81
|
+
super(*string)
|
82
|
+
return
|
83
|
+
end
|
76
84
|
|
77
|
-
|
78
|
-
|
79
|
-
|
85
|
+
unless RubyJard::ScreenManager.instance.updating?
|
86
|
+
RubyJard::Session.instance.append_output_buffer(string)
|
87
|
+
end
|
88
|
+
|
89
|
+
super(*string)
|
90
|
+
end
|
91
|
+
CODE
|
80
92
|
|
81
93
|
at_exit { stop }
|
82
94
|
|
@@ -98,13 +110,6 @@ module RubyJard
|
|
98
110
|
@started == true
|
99
111
|
end
|
100
112
|
|
101
|
-
def attach
|
102
|
-
start unless started?
|
103
|
-
|
104
|
-
Byebug.attach
|
105
|
-
Byebug.current_context.step_out(3, true)
|
106
|
-
end
|
107
|
-
|
108
113
|
def should_stop?
|
109
114
|
@path_filter.match?(@current_context.frame_file)
|
110
115
|
end
|
data/lib/ruby_jard/span.rb
CHANGED
data/lib/ruby_jard/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_jard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Minh Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- ".github/FUNDING.yml"
|
71
71
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
72
72
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
73
|
+
- ".github/dependabot.yml"
|
73
74
|
- ".github/workflows/documentation.yml"
|
74
75
|
- ".github/workflows/rspec.yml"
|
75
76
|
- ".gitignore"
|