ruby-shell 3.5.0 → 3.6.1
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/bin/rsh +162 -89
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84b836c4727e19dc59d0e12f980ebb4437b729a474247d94288d017f39d735b3
|
|
4
|
+
data.tar.gz: 72a2ef18a3a12bce6c1081743801d0f996373199268c084ffdb39e5b6f4771e8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de67a854302e2855c5fc9fd74a34cf550a98e3e3827cc83862c6b3f8cb329b3da8e0c491bf4266c57f3c066c41d79f0858899f5d9578ce97641a6a2f3c56a15b
|
|
7
|
+
data.tar.gz: a3a84a2553228efcbb9e77bbca2c01f7e3b689b1570a02e19a65d2b6c3e85a0bded51519a2bb16b04654687d8e7926272d112e7b4c33029318e9c454e0b8952a
|
data/bin/rsh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# Web_site: http://isene.com/
|
|
9
9
|
# Github: https://github.com/isene/rsh
|
|
10
10
|
# License: Public domain
|
|
11
|
-
@version = "3.
|
|
11
|
+
@version = "3.6.1" # Ctrl-L fixes: Position reset, visible length calculation, no duplicate prompts
|
|
12
12
|
|
|
13
13
|
# MODULES, CLASSES AND EXTENSIONS
|
|
14
14
|
class String # Add coloring to strings (with escaping for Readline)
|
|
@@ -185,67 +185,44 @@ end
|
|
|
185
185
|
# HELP TEXT
|
|
186
186
|
@info = <<~INFO
|
|
187
187
|
|
|
188
|
-
Hello #{@user}, welcome to rsh v3.
|
|
189
|
-
|
|
190
|
-
rsh
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
v3.2 Features:
|
|
228
|
-
* Plugin system - Extensible architecture for custom commands, completions, and hooks
|
|
229
|
-
* Auto-correct typos - :config auto_correct on (with confirmation prompt)
|
|
230
|
-
* Command timing alerts - :config slow_command_threshold 5 warns on slow commands
|
|
231
|
-
* Inline calculator - :calc 2 + 2, :calc "Math::PI", full Ruby Math library
|
|
232
|
-
* Enhanced history - !!, !-2, !5:7 for repeat last, nth-to-last, and chaining
|
|
233
|
-
* Stats visualization - :stats --graph for colorful ASCII bar charts
|
|
234
|
-
|
|
235
|
-
v3.1 Features:
|
|
236
|
-
* Multiple named sessions - :save_session "project" and :load_session "project"
|
|
237
|
-
* Stats export - :stats --csv or :stats --json for data analysis
|
|
238
|
-
* Session auto-save - Set @session_autosave = 300 in .rshrc for 5-min auto-save
|
|
239
|
-
* Bookmark import/export - :bm --export file.json and :bm --import file.json
|
|
240
|
-
* Bookmark statistics - :bm --stats shows usage patterns and tag distribution
|
|
241
|
-
* Color themes - :theme solarized|dracula|gruvbox|nord|monokai
|
|
242
|
-
* Config management - :config shows/sets history_dedup, session_autosave, etc.
|
|
243
|
-
* Environment management - :env for listing/setting/exporting environment variables
|
|
244
|
-
|
|
245
|
-
Config file (.rshrc) updates on exit (Ctrl-d) or not (Ctrl-e).
|
|
246
|
-
All colors are themeable in .rshrc (see github link for possibilities).
|
|
247
|
-
|
|
248
|
-
Use `:help` for complete command reference.
|
|
188
|
+
Hello #{@user}, welcome to rsh v3.6 - the Ruby SHell.
|
|
189
|
+
|
|
190
|
+
rsh is a feature-rich shell written in pure Ruby, designed for productivity and customization.
|
|
191
|
+
|
|
192
|
+
KEY FEATURES:
|
|
193
|
+
• Nicks (aliases) with {{parameters}} - :nick gp = git push origin {{branch}}
|
|
194
|
+
• Completion learning - Shell learns and adapts to your patterns
|
|
195
|
+
• Plugin system - Extend with custom commands and hooks
|
|
196
|
+
• Command recording/replay - Automate workflows
|
|
197
|
+
• Sessions & bookmarks - Save and restore your workspace
|
|
198
|
+
• Multi-line prompts - Full support for prompts with newlines
|
|
199
|
+
• Stats & analytics - :stats shows usage patterns and performance
|
|
200
|
+
• Validation rules - :validate rm -rf / = block for safety
|
|
201
|
+
• 6 color themes - :theme solarized|dracula|gruvbox|nord|monokai|default
|
|
202
|
+
• Auto-correct - Smart typo detection with suggestions
|
|
203
|
+
• Shell scripts - Full bash support (for, while, if loops)
|
|
204
|
+
• AI integration - @ for questions, @@ for command suggestions
|
|
205
|
+
• Ruby functions - :defun for custom shell commands
|
|
206
|
+
• 18 tool completions - git, docker, kubectl, terraform, aws, etc.
|
|
207
|
+
|
|
208
|
+
QUICK START:
|
|
209
|
+
• :help - Command reference
|
|
210
|
+
• :info --features - Feature list
|
|
211
|
+
• :nick - Create alias
|
|
212
|
+
• :bm - Bookmark directory
|
|
213
|
+
• :stats - View analytics
|
|
214
|
+
• :theme - Change colors
|
|
215
|
+
• :plugins - Extend functionality
|
|
216
|
+
|
|
217
|
+
CUSTOMIZATION:
|
|
218
|
+
• Config file: ~/.rshrc (edit with :config or manually)
|
|
219
|
+
• Colors: All themeable (see :help)
|
|
220
|
+
• Exit: Ctrl-D (save) or Ctrl-E (don't save)
|
|
221
|
+
|
|
222
|
+
MORE INFO:
|
|
223
|
+
• GitHub: https://github.com/isene/rsh
|
|
224
|
+
• Docs: Type :help for quick reference
|
|
225
|
+
• Version: Type :version for current/latest
|
|
249
226
|
|
|
250
227
|
INFO
|
|
251
228
|
|
|
@@ -356,19 +333,74 @@ def getstr # A custom Readline-like function
|
|
|
356
333
|
@pos = @ai_suggestion.length
|
|
357
334
|
@ai_suggestion = nil
|
|
358
335
|
end
|
|
336
|
+
|
|
337
|
+
# Print prompt ONCE at start (optimization + multi-line support)
|
|
359
338
|
@row0, p = @c.pos
|
|
339
|
+
|
|
340
|
+
# Clear any previous prompt
|
|
341
|
+
@c.row(@row0)
|
|
342
|
+
@c.clear_screen_down
|
|
343
|
+
|
|
344
|
+
print @display_prompt
|
|
345
|
+
|
|
346
|
+
# Calculate positions and save prompt parts
|
|
347
|
+
if @display_prompt.include?("\n")
|
|
348
|
+
# Multi-line prompt
|
|
349
|
+
newline_count = @display_prompt.count("\n")
|
|
350
|
+
@row0 += newline_count
|
|
351
|
+
|
|
352
|
+
# Extract last line and reapply color codes (rcurses pattern)
|
|
353
|
+
# Detect ANY color codes at start of full prompt
|
|
354
|
+
color_codes = ""
|
|
355
|
+
if @display_prompt =~ /^(\001\e\[[0-9;]+m\002)/
|
|
356
|
+
color_codes = $1
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Split and get last line
|
|
360
|
+
lines = @display_prompt.split("\n")
|
|
361
|
+
@prompt_last_line = lines.last
|
|
362
|
+
|
|
363
|
+
# If original had colors and last line doesn't, reapply them
|
|
364
|
+
if !color_codes.empty? && !@prompt_last_line.start_with?("\001")
|
|
365
|
+
@prompt_last_line = color_codes + @prompt_last_line + "\001\e[0m\002"
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Calculate visible length (strip ANSI for counting)
|
|
369
|
+
@pos0 = @prompt_last_line.gsub(/\001?\e\[[0-9;]*m\002?/, '').length
|
|
370
|
+
@cmd_row = @row0
|
|
371
|
+
@is_multiline = true
|
|
372
|
+
else
|
|
373
|
+
# Single-line prompt
|
|
374
|
+
row, col = @c.pos
|
|
375
|
+
@prompt_last_line = @display_prompt
|
|
376
|
+
# Calculate visible length (strip ANSI) - don't trust @c.pos with complex prompts
|
|
377
|
+
@pos0 = @display_prompt.gsub(/\001\e\[[0-9;]*m\002/, '').length
|
|
378
|
+
@cmd_row = @row0
|
|
379
|
+
@is_multiline = false
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
@skip_prompt_print = false # Track if prompt already printed (for Ctrl-L)
|
|
383
|
+
|
|
360
384
|
while chr != "ENTER" # Keep going with readline until user presses ENTER
|
|
361
385
|
@ci = nil
|
|
362
386
|
lift = false
|
|
363
387
|
right = false
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
@c.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
388
|
+
|
|
389
|
+
# Update command line display (not the prompt!)
|
|
390
|
+
@c.row(@cmd_row)
|
|
391
|
+
|
|
392
|
+
# Reprint the prompt line (preserves colors) - skip if Ctrl-L just printed it
|
|
393
|
+
unless @skip_prompt_print
|
|
394
|
+
@c.col(1)
|
|
395
|
+
print @prompt_last_line
|
|
396
|
+
@c.clear_line_after
|
|
397
|
+
end
|
|
398
|
+
@skip_prompt_print = false # Reset flag
|
|
399
|
+
|
|
400
|
+
# Print command text with syntax highlighting
|
|
371
401
|
print cmd_check(@history[0])
|
|
402
|
+
|
|
403
|
+
# Print history suggestion if available
|
|
372
404
|
@ci = @history[1..].find_index {|e| e =~ /^#{Regexp.escape(@history[0].to_s)}./}
|
|
373
405
|
unless @ci == nil
|
|
374
406
|
@ci += 1
|
|
@@ -378,15 +410,14 @@ def getstr # A custom Readline-like function
|
|
|
378
410
|
print @ciprompt.c(@c_stamp)
|
|
379
411
|
right = true
|
|
380
412
|
end
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
end
|
|
413
|
+
|
|
414
|
+
# Position cursor (unified - both now use string length which is 0-indexed)
|
|
415
|
+
# @pos0 = visible length of prompt (14 for "geir@juba: ~/ ")
|
|
416
|
+
# @pos = position in command text (0 = start, 5 = after 5 chars)
|
|
417
|
+
# Terminal columns are 1-indexed, so add +1
|
|
418
|
+
cursor_col = @pos0 + @pos + 1
|
|
419
|
+
@c.row(@cmd_row)
|
|
420
|
+
@c.col(cursor_col)
|
|
390
421
|
chr = getchr
|
|
391
422
|
puts "DEBUG: Got char: '#{chr}' (length: #{chr.length})" if ENV['RSH_DEBUG']
|
|
392
423
|
case chr
|
|
@@ -416,8 +447,37 @@ def getstr # A custom Readline-like function
|
|
|
416
447
|
exit
|
|
417
448
|
when 'C-L' # Clear screen and set position to top of the screen
|
|
418
449
|
@c.row(1)
|
|
419
|
-
@
|
|
450
|
+
@c.col(1) # Also reset column to 1
|
|
420
451
|
@c.clear_screen_down
|
|
452
|
+
# Reprint prompt after clear
|
|
453
|
+
@row0 = 1
|
|
454
|
+
print @display_prompt
|
|
455
|
+
# Recalculate positions (match initial setup exactly)
|
|
456
|
+
if @display_prompt.include?("\n")
|
|
457
|
+
newline_count = @display_prompt.count("\n")
|
|
458
|
+
@row0 += newline_count
|
|
459
|
+
# Reextract color codes and last line (same as init)
|
|
460
|
+
color_codes = ""
|
|
461
|
+
if @display_prompt =~ /^(\001\e\[[0-9;]+m\002)/
|
|
462
|
+
color_codes = $1
|
|
463
|
+
end
|
|
464
|
+
lines = @display_prompt.split("\n")
|
|
465
|
+
@prompt_last_line = lines.last
|
|
466
|
+
if !color_codes.empty? && !@prompt_last_line.start_with?("\001")
|
|
467
|
+
@prompt_last_line = color_codes + @prompt_last_line + "\001\e[0m\002"
|
|
468
|
+
end
|
|
469
|
+
@pos0 = @prompt_last_line.gsub(/\001?\e\[[0-9;]*m\002?/, '').length
|
|
470
|
+
@cmd_row = @row0
|
|
471
|
+
@is_multiline = true
|
|
472
|
+
else
|
|
473
|
+
row, col = @c.pos
|
|
474
|
+
@prompt_last_line = @display_prompt
|
|
475
|
+
# Calculate visible length (match main init)
|
|
476
|
+
@pos0 = @display_prompt.gsub(/\001\e\[[0-9;]*m\002/, '').length
|
|
477
|
+
@cmd_row = @row0
|
|
478
|
+
@is_multiline = false
|
|
479
|
+
end
|
|
480
|
+
@skip_prompt_print = true # Skip reprinting on next loop iteration
|
|
421
481
|
when 'UP' # Go up in history
|
|
422
482
|
if @stk == 0 and @history[0].length > 0
|
|
423
483
|
@tabsearch = @history[0]
|
|
@@ -436,8 +496,6 @@ def getstr # A custom Readline-like function
|
|
|
436
496
|
end
|
|
437
497
|
lift = false
|
|
438
498
|
end
|
|
439
|
-
@c.row(@row0)
|
|
440
|
-
@c.clear_screen_down
|
|
441
499
|
when 'DOWN' # Go down in history
|
|
442
500
|
if lift
|
|
443
501
|
@history.unshift("")
|
|
@@ -457,8 +515,6 @@ def getstr # A custom Readline-like function
|
|
|
457
515
|
@pos = @history[0].length
|
|
458
516
|
end
|
|
459
517
|
lift = false
|
|
460
|
-
@c.row(@row0)
|
|
461
|
-
@c.clear_screen_down
|
|
462
518
|
when 'RIGHT' # Move right on the readline
|
|
463
519
|
if right
|
|
464
520
|
if lift
|
|
@@ -633,10 +689,17 @@ def tab(type)
|
|
|
633
689
|
completed = @tabstr + "/"
|
|
634
690
|
@history[0] = @pretab + completed + @postab
|
|
635
691
|
@pos = @pretab.length + completed.length
|
|
636
|
-
|
|
692
|
+
# Consistent cursor calc
|
|
693
|
+
if @is_multiline
|
|
694
|
+
@c_col = @pos0 + @pos + 1
|
|
695
|
+
else
|
|
696
|
+
@c_col = @pos0 + @pos
|
|
697
|
+
end
|
|
637
698
|
@c.clear_line
|
|
638
699
|
line_display = cmd_check(@history[0]).to_s
|
|
639
|
-
|
|
700
|
+
# Use last line only for multi-line prompts
|
|
701
|
+
prompt_display = @is_multiline ? @display_prompt.split("\n").last : @display_prompt
|
|
702
|
+
print prompt_display + line_display
|
|
640
703
|
@c.col(@c_col)
|
|
641
704
|
return
|
|
642
705
|
end
|
|
@@ -851,6 +914,9 @@ def tab(type)
|
|
|
851
914
|
@newhist0 = @pretab + tabchoice + @postab # Remember now the new value to be given to @history[0]
|
|
852
915
|
line1 = cmd_check(@pretab).to_s # Syntax highlight before @tabstr
|
|
853
916
|
line2 = cmd_check(@postab).to_s # Syntax highlight after @tabstr
|
|
917
|
+
|
|
918
|
+
# For multi-line prompts, only show last line during tab completion
|
|
919
|
+
prompt_for_tab = @display_prompt.include?("\n") ? @display_prompt.split("\n").last : @display_prompt
|
|
854
920
|
# Color and underline the current tabchoice on the commandline with file type color:
|
|
855
921
|
display_choice = tabchoice.dup
|
|
856
922
|
clean_choice = tabchoice.gsub(/['"]/, '').chomp('/')
|
|
@@ -861,9 +927,14 @@ def tab(type)
|
|
|
861
927
|
# Escape regex special characters in @tabstr for pattern matching
|
|
862
928
|
escaped_tabstr = Regexp.escape(@tabstr)
|
|
863
929
|
tabline = display_choice.sub(/(.*)#{escaped_tabstr}(.*)/, '\1'.c(choice_color) + @tabstr.u.c(choice_color) + '\2'.c(choice_color))
|
|
864
|
-
print
|
|
930
|
+
print prompt_for_tab + line1 + tabline + line2 # Print the commandline (use last line only for multi-line)
|
|
865
931
|
@pos = @pretab.length.to_i + tabchoice.length.to_i # Set the position on that commandline
|
|
866
|
-
|
|
932
|
+
# Consistent cursor positioning
|
|
933
|
+
if @is_multiline
|
|
934
|
+
@c_col = @pos0 + @pos + 1 # Multi-line: string length needs +1
|
|
935
|
+
else
|
|
936
|
+
@c_col = @pos0 + @pos # Single-line: already 1-indexed
|
|
937
|
+
end
|
|
867
938
|
@c.col(@c_col) # Set the cursor position
|
|
868
939
|
nextline # Then start showing the completion items
|
|
869
940
|
tabline = @tabarray[i] # Get the next matching tabline
|
|
@@ -3541,9 +3612,11 @@ loop do
|
|
|
3541
3612
|
@cmd = hi if hi
|
|
3542
3613
|
end
|
|
3543
3614
|
# Move cursor to end of line and print the full command before clearing
|
|
3544
|
-
@c.row(@row0)
|
|
3615
|
+
@c.row(@cmd_row || @row0) # Use cmd_row if set (multi-line)
|
|
3545
3616
|
@c.clear_line
|
|
3546
|
-
print
|
|
3617
|
+
# For multi-line, only print last line; for single-line, print full prompt
|
|
3618
|
+
prompt_to_print = (@display_prompt.include?("\n") && @prompt_last_line) ? @prompt_last_line : @display_prompt
|
|
3619
|
+
print prompt_to_print + cmd_check(@cmd)
|
|
3547
3620
|
print "\n"; @c.clear_screen_down
|
|
3548
3621
|
if @cmd == "r" # Integration with rtfm (https://github.com/isene/RTFM)
|
|
3549
3622
|
t = Time.now
|
metadata
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-shell
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'A shell written in Ruby with extensive tab completions, aliases/nicks,
|
|
14
14
|
history, syntax highlighting, theming, auto-cd, auto-opening files and more. UPDATE
|
|
15
|
-
v3.
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
v3.6.0: MULTI-LINE PROMPT SUPPORT - Complete readline refactor with rcurses-inspired
|
|
16
|
+
ANSI handling. Define prompts with newlines! Plus nick/history export, 9 new completions,
|
|
17
|
+
validation templates, startup tips, and critical performance fixes for huge PATHs!'
|
|
18
18
|
email: g@isene.com
|
|
19
19
|
executables:
|
|
20
20
|
- rsh
|