thor-interactive 0.1.0.pre.2 → 0.1.0.pre.3
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 +4 -4
- data/examples/signal_demo.rb +203 -0
- data/lib/thor/interactive/shell.rb +58 -12
- data/lib/thor/interactive/version.rb +2 -1
- data/lib/thor/interactive/version_constant.rb +8 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '049af6f0fedd7bda65c5845a878bd02afe8e9e5cf705ebad254c333e0151ecd7'
|
4
|
+
data.tar.gz: '049e366b5d3d1e4d0076c7c69d69240b00f0768b3b2687d23e9482dd7bf368dd'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc5ccc2b2765f3f5c4591c181445dbc4b0ef66982c982758fb6053fa79de968c7583f019be85a747c51474e6e2f55f8834b9c4fd3314b1186dd6185a9be9e092
|
7
|
+
data.tar.gz: cc284a24f5f2a24bb016651e4252a9230d89c84d0e86b382fb4b936d11063422f78338d4c0917aedcf0a6e6d9fe7dcb66f8e7fb22c479c2903f355265e00cc79
|
@@ -0,0 +1,203 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "thor/interactive"
|
6
|
+
|
7
|
+
class SignalDemo < Thor
|
8
|
+
include Thor::Interactive::Command
|
9
|
+
|
10
|
+
configure_interactive(
|
11
|
+
prompt: "signal> ",
|
12
|
+
ctrl_c_behavior: :clear_prompt, # Default
|
13
|
+
double_ctrl_c_timeout: 0.5 # 500ms window for double Ctrl-C
|
14
|
+
)
|
15
|
+
|
16
|
+
desc "slow", "Simulate a slow command"
|
17
|
+
def slow
|
18
|
+
puts "Starting slow operation..."
|
19
|
+
5.times do |i|
|
20
|
+
puts "Step #{i + 1}/5"
|
21
|
+
sleep(1)
|
22
|
+
end
|
23
|
+
puts "Done!"
|
24
|
+
rescue Interrupt
|
25
|
+
puts "\nOperation cancelled!"
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "loop", "Run an infinite loop (test Ctrl-C)"
|
29
|
+
def loop
|
30
|
+
puts "Starting infinite loop (press Ctrl-C to stop)..."
|
31
|
+
counter = 0
|
32
|
+
while true
|
33
|
+
print "\rCounter: #{counter}"
|
34
|
+
counter += 1
|
35
|
+
sleep(0.1)
|
36
|
+
end
|
37
|
+
rescue Interrupt
|
38
|
+
puts "\nLoop stopped at #{counter}"
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "input", "Test input with special text"
|
42
|
+
def input
|
43
|
+
puts "Type something with Ctrl chars:"
|
44
|
+
puts " - Ctrl-C to clear and start over"
|
45
|
+
puts " - Ctrl-D to cancel"
|
46
|
+
puts " - Enter to submit"
|
47
|
+
|
48
|
+
print "input> "
|
49
|
+
begin
|
50
|
+
text = $stdin.gets
|
51
|
+
if text
|
52
|
+
puts "You entered: #{text.inspect}"
|
53
|
+
else
|
54
|
+
puts "Cancelled with Ctrl-D"
|
55
|
+
end
|
56
|
+
rescue Interrupt
|
57
|
+
puts "\nInterrupted - input cancelled"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "behaviors", "Demo different Ctrl-C behaviors"
|
62
|
+
def behaviors
|
63
|
+
puts "\n=== Ctrl-C Behavior Options ==="
|
64
|
+
puts
|
65
|
+
puts "1. :clear_prompt (default)"
|
66
|
+
puts " - Shows ^C and hint message"
|
67
|
+
puts " - Clear and friendly"
|
68
|
+
|
69
|
+
puts "\n2. :show_help"
|
70
|
+
puts " - Shows help reminder"
|
71
|
+
puts " - Good for new users"
|
72
|
+
|
73
|
+
puts "\n3. :silent"
|
74
|
+
puts " - Just clears the line"
|
75
|
+
puts " - Minimal interruption"
|
76
|
+
|
77
|
+
puts "\nYou can configure with:"
|
78
|
+
puts " configure_interactive(ctrl_c_behavior: :show_help)"
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "test_clear", "Test with clear_prompt behavior"
|
82
|
+
def test_clear
|
83
|
+
puts "Starting new shell with :clear_prompt behavior"
|
84
|
+
puts "Try pressing Ctrl-C..."
|
85
|
+
puts
|
86
|
+
|
87
|
+
SignalDemo.new.interactive
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "test_help", "Test with show_help behavior"
|
91
|
+
def test_help
|
92
|
+
puts "Starting new shell with :show_help behavior"
|
93
|
+
puts "Try pressing Ctrl-C..."
|
94
|
+
puts
|
95
|
+
|
96
|
+
test_app = Class.new(Thor) do
|
97
|
+
include Thor::Interactive::Command
|
98
|
+
configure_interactive(
|
99
|
+
prompt: "help> ",
|
100
|
+
ctrl_c_behavior: :show_help
|
101
|
+
)
|
102
|
+
|
103
|
+
desc "test", "Test command"
|
104
|
+
def test
|
105
|
+
puts "Test executed"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
test_app.new.interactive
|
110
|
+
end
|
111
|
+
|
112
|
+
desc "test_silent", "Test with silent behavior"
|
113
|
+
def test_silent
|
114
|
+
puts "Starting new shell with :silent behavior"
|
115
|
+
puts "Try pressing Ctrl-C..."
|
116
|
+
puts
|
117
|
+
|
118
|
+
test_app = Class.new(Thor) do
|
119
|
+
include Thor::Interactive::Command
|
120
|
+
configure_interactive(
|
121
|
+
prompt: "silent> ",
|
122
|
+
ctrl_c_behavior: :silent
|
123
|
+
)
|
124
|
+
|
125
|
+
desc "test", "Test command"
|
126
|
+
def test
|
127
|
+
puts "Test executed"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
test_app.new.interactive
|
132
|
+
end
|
133
|
+
|
134
|
+
desc "help_signals", "Explain signal handling"
|
135
|
+
def help_signals
|
136
|
+
puts <<~HELP
|
137
|
+
|
138
|
+
=== Signal Handling in thor-interactive ===
|
139
|
+
|
140
|
+
CTRL-C (SIGINT):
|
141
|
+
Single Press:
|
142
|
+
- Clears current input line
|
143
|
+
- Shows hint about double Ctrl-C
|
144
|
+
- Returns to fresh prompt
|
145
|
+
|
146
|
+
Double Press (within 500ms):
|
147
|
+
- Exits the interactive shell
|
148
|
+
- Same as typing 'exit'
|
149
|
+
|
150
|
+
CTRL-D (EOF):
|
151
|
+
- Exits immediately
|
152
|
+
- Standard Unix EOF behavior
|
153
|
+
- Same as typing 'exit'
|
154
|
+
|
155
|
+
EXIT COMMANDS:
|
156
|
+
- exit
|
157
|
+
- quit
|
158
|
+
- q
|
159
|
+
- /exit, /quit, /q (with slash)
|
160
|
+
|
161
|
+
CONFIGURATION:
|
162
|
+
configure_interactive(
|
163
|
+
ctrl_c_behavior: :clear_prompt, # or :show_help, :silent
|
164
|
+
double_ctrl_c_timeout: 0.5 # seconds
|
165
|
+
)
|
166
|
+
|
167
|
+
BEHAVIOR OPTIONS:
|
168
|
+
:clear_prompt (default)
|
169
|
+
Shows "^C" and hint message
|
170
|
+
|
171
|
+
:show_help
|
172
|
+
Shows help reminder on Ctrl-C
|
173
|
+
|
174
|
+
:silent
|
175
|
+
Just clears the line, no message
|
176
|
+
|
177
|
+
WHY THIS DESIGN?
|
178
|
+
- Matches behavior of Python, Node.js REPLs
|
179
|
+
- Prevents accidental exit
|
180
|
+
- Clear feedback to user
|
181
|
+
- Configurable for different preferences
|
182
|
+
|
183
|
+
HELP
|
184
|
+
end
|
185
|
+
|
186
|
+
default_task :help_signals
|
187
|
+
end
|
188
|
+
|
189
|
+
if __FILE__ == $0
|
190
|
+
puts "Signal Handling Demo"
|
191
|
+
puts "==================="
|
192
|
+
puts
|
193
|
+
puts "Try these:"
|
194
|
+
puts " 1. Press Ctrl-C once (clears prompt)"
|
195
|
+
puts " 2. Press Ctrl-C twice quickly (exits)"
|
196
|
+
puts " 3. Press Ctrl-D (exits immediately)"
|
197
|
+
puts " 4. Type 'exit', 'quit', or 'q' (exits)"
|
198
|
+
puts
|
199
|
+
puts "Starting interactive shell..."
|
200
|
+
puts
|
201
|
+
|
202
|
+
SignalDemo.new.interactive
|
203
|
+
end
|
@@ -29,6 +29,12 @@ class Thor
|
|
29
29
|
@prompt = merged_options[:prompt] || DEFAULT_PROMPT
|
30
30
|
@history_file = File.expand_path(merged_options[:history_file] || DEFAULT_HISTORY_FILE)
|
31
31
|
|
32
|
+
# Ctrl-C handling configuration
|
33
|
+
@ctrl_c_behavior = merged_options[:ctrl_c_behavior] || :clear_prompt
|
34
|
+
@double_ctrl_c_timeout = merged_options.key?(:double_ctrl_c_timeout) ?
|
35
|
+
merged_options[:double_ctrl_c_timeout] : 0.5
|
36
|
+
@last_interrupt_time = nil
|
37
|
+
|
32
38
|
setup_completion
|
33
39
|
load_history
|
34
40
|
end
|
@@ -56,27 +62,36 @@ class Thor
|
|
56
62
|
puts "(Debug: Entering main loop)" if ENV["DEBUG"]
|
57
63
|
|
58
64
|
loop do
|
59
|
-
line = Reline.readline(display_prompt, true)
|
60
|
-
puts "(Debug: Got input: #{line.inspect})" if ENV["DEBUG"]
|
61
|
-
|
62
|
-
if should_exit?(line)
|
63
|
-
puts "(Debug: Exit condition met)" if ENV["DEBUG"]
|
64
|
-
break
|
65
|
-
end
|
66
|
-
|
67
|
-
next if line.nil? || line.strip.empty?
|
68
|
-
|
69
65
|
begin
|
66
|
+
line = Reline.readline(display_prompt, true)
|
67
|
+
puts "(Debug: Got input: #{line.inspect})" if ENV["DEBUG"]
|
68
|
+
|
69
|
+
# Reset interrupt tracking on successful input
|
70
|
+
@last_interrupt_time = nil if line
|
71
|
+
|
72
|
+
if should_exit?(line)
|
73
|
+
puts "(Debug: Exit condition met)" if ENV["DEBUG"]
|
74
|
+
break
|
75
|
+
end
|
76
|
+
|
77
|
+
next if line.nil? || line.strip.empty?
|
78
|
+
|
70
79
|
puts "(Debug: Processing input: #{line.strip})" if ENV["DEBUG"]
|
71
80
|
process_input(line.strip)
|
72
81
|
puts "(Debug: Input processed successfully)" if ENV["DEBUG"]
|
82
|
+
|
73
83
|
rescue Interrupt
|
74
|
-
|
84
|
+
# Handle Ctrl-C
|
85
|
+
if handle_interrupt
|
86
|
+
break # Exit on double Ctrl-C
|
87
|
+
end
|
88
|
+
next # Continue on single Ctrl-C
|
89
|
+
|
75
90
|
rescue SystemExit => e
|
76
91
|
puts "A command tried to exit with code #{e.status}. Staying in interactive mode."
|
77
92
|
puts "(Debug: SystemExit caught in main loop)" if ENV["DEBUG"]
|
78
93
|
rescue => e
|
79
|
-
puts "Error
|
94
|
+
puts "Error: #{e.message}"
|
80
95
|
puts e.backtrace.first(5) if ENV["DEBUG"]
|
81
96
|
puts "(Debug: Error handled, continuing loop)" if ENV["DEBUG"]
|
82
97
|
# Continue the loop - don't let errors break the session
|
@@ -343,6 +358,37 @@ class Thor
|
|
343
358
|
# Handle both /exit and exit for convenience
|
344
359
|
EXIT_COMMANDS.include?(stripped) || EXIT_COMMANDS.include?(stripped.sub(/^\//, ''))
|
345
360
|
end
|
361
|
+
|
362
|
+
def handle_interrupt
|
363
|
+
current_time = Time.now
|
364
|
+
|
365
|
+
# Check for double Ctrl-C
|
366
|
+
if @last_interrupt_time && @double_ctrl_c_timeout && (current_time - @last_interrupt_time) < @double_ctrl_c_timeout
|
367
|
+
puts "\n(Interrupted twice - exiting)"
|
368
|
+
@last_interrupt_time = nil # Reset for next time
|
369
|
+
return true # Signal to exit
|
370
|
+
end
|
371
|
+
|
372
|
+
@last_interrupt_time = current_time
|
373
|
+
|
374
|
+
# Single Ctrl-C behavior
|
375
|
+
case @ctrl_c_behavior
|
376
|
+
when :clear_prompt
|
377
|
+
puts "^C"
|
378
|
+
puts "(Press Ctrl-C again quickly or Ctrl-D to exit)"
|
379
|
+
when :show_help
|
380
|
+
puts "\n^C - Interrupt"
|
381
|
+
puts "Press Ctrl-C again to exit, or type 'help' for commands"
|
382
|
+
when :silent
|
383
|
+
# Just clear the line, no message
|
384
|
+
print "\r#{' ' * 80}\r"
|
385
|
+
else
|
386
|
+
# Default behavior
|
387
|
+
puts "^C"
|
388
|
+
end
|
389
|
+
|
390
|
+
false # Don't exit, just clear prompt
|
391
|
+
end
|
346
392
|
|
347
393
|
def show_welcome(nesting_level = 0)
|
348
394
|
if nesting_level > 0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thor-interactive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.pre.
|
4
|
+
version: 0.1.0.pre.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Petersen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-09-
|
11
|
+
date: 2025-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -57,11 +57,13 @@ files:
|
|
57
57
|
- examples/demo_session.rb
|
58
58
|
- examples/nested_example.rb
|
59
59
|
- examples/sample_app.rb
|
60
|
+
- examples/signal_demo.rb
|
60
61
|
- examples/test_interactive.rb
|
61
62
|
- lib/thor/interactive.rb
|
62
63
|
- lib/thor/interactive/command.rb
|
63
64
|
- lib/thor/interactive/shell.rb
|
64
65
|
- lib/thor/interactive/version.rb
|
66
|
+
- lib/thor/interactive/version_constant.rb
|
65
67
|
- sig/thor/interactive.rbs
|
66
68
|
homepage: https://github.com/scientist-labs/thor-interactive
|
67
69
|
licenses:
|