ruby-shell 3.6.15 → 3.6.17
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 +59 -44
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 47a8896d9151658c431c1f2302f18b0d143887c9fe67d410a64d9c18bd143347
|
|
4
|
+
data.tar.gz: 230a1b7784618c69c860d169e36be458a09509d66052cd3350e1bb6e16620908
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c841a0dc5675dbd5bf1bb9341b0f9edeead426cec47357a2e1655dc1d67465732822a596d71468a5972f511c2a6f0f2f983ee19ed24317567de6b05affef665
|
|
7
|
+
data.tar.gz: 61c8fb056e30ad6eba62bca87be4d4d60da8817fb69acee4924d672fa32dcd8f050250a7accae2af6bb288de43907b06c95b38956980af53cdf205c437952f28
|
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.6.
|
|
11
|
+
@version = "3.6.17" # Optimize core input loop
|
|
12
12
|
|
|
13
13
|
# MODULES, CLASSES AND EXTENSIONS
|
|
14
14
|
class String # Add coloring to strings (with escaping for Readline)
|
|
@@ -162,6 +162,7 @@ begin # Initialization
|
|
|
162
162
|
@history_dedup = 'smart' # History dedup mode: 'off', 'full', 'smart'
|
|
163
163
|
@auto_correct = false # Auto-correct typos (default: off)
|
|
164
164
|
@slow_command_threshold = 0 # Threshold for slow command alerts (0 = disabled)
|
|
165
|
+
@config_dirty = [] # Track which config vars were changed via :config
|
|
165
166
|
@plugin_dir = Dir.home + '/.rsh/plugins' # Plugins directory
|
|
166
167
|
@plugins = [] # Loaded plugin instances
|
|
167
168
|
@plugin_enabled = [] # List of enabled plugin names (whitelist)
|
|
@@ -387,38 +388,39 @@ def getstr # A custom Readline-like function
|
|
|
387
388
|
lift = false
|
|
388
389
|
right = false
|
|
389
390
|
|
|
390
|
-
#
|
|
391
|
-
@
|
|
392
|
-
@
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
print @prompt_last_line
|
|
397
|
-
print cmd_check(@history[0])
|
|
391
|
+
# Cache syntax highlighting (skip re-highlighting on cursor-only moves)
|
|
392
|
+
cmd_text = @history[0].to_s
|
|
393
|
+
if cmd_text != @_hl_text
|
|
394
|
+
@_hl_text = cmd_text.dup
|
|
395
|
+
@_hl_result = cmd_check(@history[0])
|
|
396
|
+
end
|
|
398
397
|
|
|
399
|
-
#
|
|
400
|
-
@ci = @history[1..].find_index {|e| e =~ /^#{Regexp.escape(
|
|
398
|
+
# History suggestion lookup
|
|
399
|
+
@ci = @history[1..].find_index {|e| e =~ /^#{Regexp.escape(cmd_text)}./}
|
|
401
400
|
unless @ci == nil
|
|
402
401
|
@ci += 1
|
|
403
|
-
@ciprompt = @history[@ci][
|
|
404
|
-
end
|
|
405
|
-
if @history[0].to_s.length > 1 and @ci
|
|
406
|
-
print @ciprompt.c(@c_stamp)
|
|
407
|
-
right = true
|
|
402
|
+
@ciprompt = @history[@ci][cmd_text.length..].to_s
|
|
408
403
|
end
|
|
404
|
+
right = cmd_text.length > 1 && @ci
|
|
409
405
|
|
|
410
|
-
#
|
|
411
|
-
|
|
412
|
-
printed_len
|
|
413
|
-
printed_len += @ciprompt.to_s.length if right # Include suggestion text
|
|
406
|
+
# Compute scroll adjustment before drawing (avoids terminal round-trip)
|
|
407
|
+
printed_len = @pos0 + cmd_text.length
|
|
408
|
+
printed_len += @ciprompt.to_s.length if right
|
|
414
409
|
expected_rows = printed_len > 0 ? (printed_len - 1) / @maxcol : 0
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
@cmd_row = [actual_start, 1].max # Terminal scrolled; adjust (min row 1)
|
|
410
|
+
if @cmd_row + expected_rows > @maxrow
|
|
411
|
+
@cmd_row = [@maxrow - expected_rows, 1].max
|
|
418
412
|
end
|
|
419
413
|
|
|
414
|
+
# Redraw prompt + command
|
|
415
|
+
@c.row(@cmd_row)
|
|
416
|
+
@c.col(1)
|
|
417
|
+
@c.clear_screen_down
|
|
418
|
+
print @prompt_last_line
|
|
419
|
+
print @_hl_result
|
|
420
|
+
print @ciprompt.c(@c_stamp) if right
|
|
421
|
+
|
|
420
422
|
# Position cursor with wrapping math
|
|
421
|
-
total_pos = @pos0 + @pos
|
|
423
|
+
total_pos = @pos0 + @pos
|
|
422
424
|
cursor_row = @cmd_row + total_pos / @maxcol
|
|
423
425
|
cursor_col = total_pos % @maxcol + 1
|
|
424
426
|
@c.row(cursor_row)
|
|
@@ -429,7 +431,7 @@ def getstr # A custom Readline-like function
|
|
|
429
431
|
when 'C-G' # Ctrl-G opens command in $EDITOR
|
|
430
432
|
temp_file = "/tmp/rsh_edit_#{Process.pid}.tmp"
|
|
431
433
|
File.write(temp_file, @history[0] || "")
|
|
432
|
-
system(
|
|
434
|
+
system(ENV['EDITOR'] || 'vi', temp_file)
|
|
433
435
|
if File.exist?(temp_file)
|
|
434
436
|
edited = File.read(temp_file).strip
|
|
435
437
|
# Convert multi-line to single line with proper separators
|
|
@@ -561,7 +563,7 @@ def getstr # A custom Readline-like function
|
|
|
561
563
|
end
|
|
562
564
|
lift = true
|
|
563
565
|
when 'C-Y' # Copy command line to primary selection
|
|
564
|
-
|
|
566
|
+
IO.popen('xclip', 'w') { |io| io.write(@history[0]) }
|
|
565
567
|
puts "\n#{Time.now.strftime("%H:%M:%S")}: Copied to primary selection (paste with middle buttoni)".c(@c_stamp)
|
|
566
568
|
when 'C-Z' # Suspend current process (background job)
|
|
567
569
|
if @current_pid
|
|
@@ -968,7 +970,7 @@ def tab(type)
|
|
|
968
970
|
begin
|
|
969
971
|
count = Dir.entries(clean_item).length - 2
|
|
970
972
|
print " [#{count}]".c(244)
|
|
971
|
-
rescue
|
|
973
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
972
974
|
end
|
|
973
975
|
else
|
|
974
976
|
size = File.size(clean_item)
|
|
@@ -1004,7 +1006,7 @@ def tab(type)
|
|
|
1004
1006
|
begin
|
|
1005
1007
|
count = Dir.entries(clean_item).length - 2
|
|
1006
1008
|
print " [#{count}]".c(244)
|
|
1007
|
-
rescue
|
|
1009
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
1008
1010
|
end
|
|
1009
1011
|
else
|
|
1010
1012
|
size = File.size(clean_item)
|
|
@@ -1088,6 +1090,7 @@ def tabend
|
|
|
1088
1090
|
@c.col(@c_col)
|
|
1089
1091
|
end
|
|
1090
1092
|
def get_command_switches(command) # Helper function to extract switches from --help
|
|
1093
|
+
require 'shellwords' unless defined?(Shellwords)
|
|
1091
1094
|
# Check cache first (cache expires after 1 hour)
|
|
1092
1095
|
cache_key = command.to_s.strip
|
|
1093
1096
|
return [] if cache_key.empty?
|
|
@@ -1098,9 +1101,10 @@ def get_command_switches(command) # Helper function to extract switches from --h
|
|
|
1098
1101
|
end
|
|
1099
1102
|
|
|
1100
1103
|
# Parse --help output
|
|
1101
|
-
|
|
1104
|
+
escaped = Shellwords.escape(command)
|
|
1105
|
+
hlp = `#{escaped} --help 2>/dev/null`
|
|
1102
1106
|
# Try -h if --help didn't work
|
|
1103
|
-
hlp = `#{
|
|
1107
|
+
hlp = `#{escaped} -h 2>/dev/null` if hlp.empty?
|
|
1104
1108
|
return [] if hlp.empty?
|
|
1105
1109
|
|
|
1106
1110
|
switches = []
|
|
@@ -1242,6 +1246,7 @@ def config(*args) # Configure rsh settings
|
|
|
1242
1246
|
when 'history_dedup'
|
|
1243
1247
|
if %w[off full smart].include?(value)
|
|
1244
1248
|
@history_dedup = value
|
|
1249
|
+
@config_dirty << '@history_dedup'
|
|
1245
1250
|
puts "History deduplication set to '#{value}'"
|
|
1246
1251
|
rshrc
|
|
1247
1252
|
else
|
|
@@ -1249,30 +1254,37 @@ def config(*args) # Configure rsh settings
|
|
|
1249
1254
|
end
|
|
1250
1255
|
when 'session_autosave'
|
|
1251
1256
|
@session_autosave = value.to_i
|
|
1257
|
+
@config_dirty << '@session_autosave'
|
|
1252
1258
|
puts "Session auto-save set to #{value}s #{value.to_i > 0 ? '(enabled)' : '(disabled)'}"
|
|
1253
1259
|
rshrc
|
|
1254
1260
|
when 'auto_correct'
|
|
1255
1261
|
@auto_correct = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1262
|
+
@config_dirty << '@auto_correct'
|
|
1256
1263
|
puts "Auto-correct #{@auto_correct ? 'enabled' : 'disabled'}"
|
|
1257
1264
|
rshrc
|
|
1258
1265
|
when 'slow_command_threshold'
|
|
1259
1266
|
@slow_command_threshold = value.to_i
|
|
1267
|
+
@config_dirty << '@slow_command_threshold'
|
|
1260
1268
|
puts "Slow command threshold set to #{value}s #{value.to_i > 0 ? '(enabled)' : '(disabled)'}"
|
|
1261
1269
|
rshrc
|
|
1262
1270
|
when 'completion_learning'
|
|
1263
1271
|
@completion_learning = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1272
|
+
@config_dirty << '@completion_learning'
|
|
1264
1273
|
puts "Completion learning #{@completion_learning ? 'enabled' : 'disabled'}"
|
|
1265
1274
|
rshrc
|
|
1266
1275
|
when 'completion_limit'
|
|
1267
1276
|
@completion_limit = value.to_i
|
|
1277
|
+
@config_dirty << '@completion_limit'
|
|
1268
1278
|
puts "Completion limit set to #{value}"
|
|
1269
1279
|
rshrc
|
|
1270
1280
|
when 'completion_show_metadata'
|
|
1271
1281
|
@completion_show_metadata = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1282
|
+
@config_dirty << '@completion_show_metadata'
|
|
1272
1283
|
puts "Completion metadata display #{@completion_show_metadata ? 'enabled' : 'disabled'}"
|
|
1273
1284
|
rshrc
|
|
1274
1285
|
when 'show_tips'
|
|
1275
1286
|
@show_tips = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1287
|
+
@config_dirty << '@show_tips'
|
|
1276
1288
|
puts "Startup tips #{@show_tips ? 'enabled' : 'disabled'}"
|
|
1277
1289
|
rshrc
|
|
1278
1290
|
else
|
|
@@ -1432,7 +1444,7 @@ def format_tab_item(item, show_metadata: false) # Format tab item with color and
|
|
|
1432
1444
|
begin
|
|
1433
1445
|
count = Dir.entries(clean_name).length - 2 # Exclude . and ..
|
|
1434
1446
|
meta = "[dir, #{count} items]"
|
|
1435
|
-
rescue
|
|
1447
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
1436
1448
|
meta = "[dir]"
|
|
1437
1449
|
end
|
|
1438
1450
|
else
|
|
@@ -1511,7 +1523,7 @@ def rshrc # Write user configuration to .rshrc (portable between machines)
|
|
|
1511
1523
|
begin
|
|
1512
1524
|
file_nicks = eval($1)
|
|
1513
1525
|
@nick = file_nicks.merge(@nick) if file_nicks.is_a?(Hash)
|
|
1514
|
-
rescue
|
|
1526
|
+
rescue SyntaxError, StandardError
|
|
1515
1527
|
# If eval fails, keep current @nick
|
|
1516
1528
|
end
|
|
1517
1529
|
end
|
|
@@ -1521,7 +1533,7 @@ def rshrc # Write user configuration to .rshrc (portable between machines)
|
|
|
1521
1533
|
begin
|
|
1522
1534
|
file_gnicks = eval($1)
|
|
1523
1535
|
@gnick = file_gnicks.merge(@gnick) if file_gnicks.is_a?(Hash)
|
|
1524
|
-
rescue
|
|
1536
|
+
rescue SyntaxError, StandardError
|
|
1525
1537
|
# If eval fails, keep current @gnick
|
|
1526
1538
|
end
|
|
1527
1539
|
end
|
|
@@ -1535,21 +1547,23 @@ def rshrc # Write user configuration to .rshrc (portable between machines)
|
|
|
1535
1547
|
conf.sub!(/^#{Regexp.escape(var)} =.*\n?/, "")
|
|
1536
1548
|
end
|
|
1537
1549
|
|
|
1538
|
-
# Persist
|
|
1550
|
+
# Persist runtime data (nicks, bookmarks, etc. change during normal use)
|
|
1539
1551
|
conf = persist_var(conf, '@nick', @nick)
|
|
1540
1552
|
conf = persist_var(conf, '@gnick', @gnick)
|
|
1541
1553
|
conf = persist_var(conf, '@bookmarks', @bookmarks, !@bookmarks.empty?)
|
|
1542
1554
|
conf = persist_var(conf, '@defuns', @defuns, !@defuns.empty?)
|
|
1543
|
-
conf = persist_var(conf, '@history_dedup', @history_dedup)
|
|
1544
|
-
conf = persist_var(conf, '@session_autosave', @session_autosave)
|
|
1545
|
-
conf = persist_var(conf, '@auto_correct', @auto_correct)
|
|
1546
|
-
conf = persist_var(conf, '@slow_command_threshold', @slow_command_threshold)
|
|
1547
|
-
conf = persist_var(conf, '@completion_learning', @completion_learning)
|
|
1548
|
-
conf = persist_var(conf, '@show_tips', @show_tips)
|
|
1549
|
-
conf = persist_var(conf, '@completion_show_metadata', @completion_show_metadata)
|
|
1550
1555
|
conf = persist_var(conf, '@plugin_disabled', @plugin_disabled, !@plugin_disabled.empty?)
|
|
1551
1556
|
conf = persist_var(conf, '@validation_rules', @validation_rules, !@validation_rules.empty?)
|
|
1552
1557
|
|
|
1558
|
+
# Only persist config vars that were explicitly changed via :config in this session
|
|
1559
|
+
conf = persist_var(conf, '@history_dedup', @history_dedup, @config_dirty.include?('@history_dedup'))
|
|
1560
|
+
conf = persist_var(conf, '@session_autosave', @session_autosave, @config_dirty.include?('@session_autosave'))
|
|
1561
|
+
conf = persist_var(conf, '@auto_correct', @auto_correct, @config_dirty.include?('@auto_correct'))
|
|
1562
|
+
conf = persist_var(conf, '@slow_command_threshold', @slow_command_threshold, @config_dirty.include?('@slow_command_threshold'))
|
|
1563
|
+
conf = persist_var(conf, '@completion_learning', @completion_learning, @config_dirty.include?('@completion_learning'))
|
|
1564
|
+
conf = persist_var(conf, '@show_tips', @show_tips, @config_dirty.include?('@show_tips'))
|
|
1565
|
+
conf = persist_var(conf, '@completion_show_metadata', @completion_show_metadata, @config_dirty.include?('@completion_show_metadata'))
|
|
1566
|
+
|
|
1553
1567
|
File.write(Dir.home+'/.rshrc', conf)
|
|
1554
1568
|
rshstate # Also save runtime state
|
|
1555
1569
|
end
|
|
@@ -3850,12 +3864,13 @@ loop do
|
|
|
3850
3864
|
puts "#{Time.now.strftime("%H:%M:%S")}: #{@cmd}".c(@c_stamp)
|
|
3851
3865
|
# Only auto-open files if it's a single filename (no spaces = no command with args)
|
|
3852
3866
|
if File.read(@cmd).force_encoding("UTF-8").valid_encoding?
|
|
3853
|
-
system(
|
|
3867
|
+
system(ENV['EDITOR'] || 'vi', @cmd) # Try open with user's editor
|
|
3854
3868
|
else
|
|
3869
|
+
require 'shellwords' unless defined?(Shellwords)
|
|
3855
3870
|
if @runmailcap
|
|
3856
|
-
Thread.new { system("run-mailcap #{@cmd} 2>/dev/null") }
|
|
3871
|
+
Thread.new { system("run-mailcap #{Shellwords.escape(@cmd)} 2>/dev/null") }
|
|
3857
3872
|
else
|
|
3858
|
-
Thread.new { system("xdg-open #{@cmd} 2>/dev/null") }
|
|
3873
|
+
Thread.new { system("xdg-open #{Shellwords.escape(@cmd)} 2>/dev/null") }
|
|
3859
3874
|
end
|
|
3860
3875
|
end
|
|
3861
3876
|
else
|
metadata
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-shell
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.6.
|
|
4
|
+
version: 3.6.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-23 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.6.
|
|
16
|
-
|
|
15
|
+
v3.6.17: Optimize core input loop with cached syntax highlighting and math-based
|
|
16
|
+
scroll detection.'
|
|
17
17
|
email: g@isene.com
|
|
18
18
|
executables:
|
|
19
19
|
- rsh
|