ruby-shell 1.1 → 2.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/rsh +185 -163
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ec039977b048309a3b3762f8b009b83820693ec99857c9ce700322339dbecf5
|
4
|
+
data.tar.gz: 35498c5cdd7dd2eea545b85d994370dca0cfadb74415eb28bf0169f46673c425
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b262ececc24a31380445f8e88e645cccbef5bb4ce043143d4268c6a116bbe4df3293cd5affd26b036be1d58af7bf4fd653b1f1747a78419e40a3ef0e34d41380
|
7
|
+
data.tar.gz: 9de2ac401010abd356a3b1aefca9848de07ba1829eb45c958a963b40d78825449de04737b9e0cf78da5ba2f1530e5306656830d3c5a4ae61d6635ccb749abe6c
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
The Ruby SHell
|
5
5
|
|
6
6
|
# Why?
|
7
|
-
Ruby is my goto language (pun kinda intended). I want full control over my tools and I like challenges that I can tinker with late at night. This is an incomplete project continually being improved. Feel free to add suggestions or code.
|
7
|
+
<img src="img/rsh-logo.jpg" align="left" width="150" height="150">Ruby is my goto language (pun kinda intended). I want full control over my tools and I like challenges that I can tinker with late at night. This is an incomplete project continually being improved. Feel free to add suggestions or code.
|
8
8
|
|
9
9
|
# Design principles
|
10
10
|
Simple. One file. Minimum external requirements.
|
data/bin/rsh
CHANGED
@@ -7,25 +7,20 @@
|
|
7
7
|
# Author: Geir Isene <g@isene.com>
|
8
8
|
# Web_site: http://isene.com/
|
9
9
|
# Github: https://github.com/isene/rsh
|
10
|
-
# License:
|
11
|
-
|
12
|
-
# this software for any purpose. I make no guarantee about the
|
13
|
-
# suitability of this software for any purpose and I am not liable
|
14
|
-
# for any damages resulting from its use. Further, I am under no
|
15
|
-
# obligation to maintain or extend this software. It is provided
|
16
|
-
# on an 'as is' basis without any expressed or implied warranty.
|
17
|
-
@version = "1.1"
|
10
|
+
# License: Public domain
|
11
|
+
@version = "2.0"
|
18
12
|
|
19
13
|
# MODULES, CLASSES AND EXTENSIONS
|
20
14
|
class String # Add coloring to strings (with escaping for Readline)
|
21
|
-
def c(code); color(self, "\001\e[38;5;#{code}m\002"); end
|
22
|
-
def b; color(self, "\001\e[1m\002"); end
|
23
|
-
def i; color(self, "\001\e[3m\002"); end
|
24
|
-
def u; color(self, "\001\e[4m\002"); end
|
25
|
-
def l; color(self, "\001\e[5m\002"); end
|
15
|
+
def c(code); color(self, "\001\e[38;5;#{code}m\002"); end # Color code
|
16
|
+
def b; color(self, "\001\e[1m\002"); end # Bold
|
17
|
+
def i; color(self, "\001\e[3m\002"); end # Italic
|
18
|
+
def u; color(self, "\001\e[4m\002"); end # Underline
|
19
|
+
def l; color(self, "\001\e[5m\002"); end # Blink
|
20
|
+
def r; color(self, "\001\e[7m\002"); end # Reverse
|
26
21
|
def color(text, color_code) "#{color_code}#{text}\001\e[0m\002" end
|
27
22
|
end
|
28
|
-
module Cursor # Terminal cursor movement ANSI codes (thanks to https://github.com/piotrmurach/tty-cursor
|
23
|
+
module Cursor # Terminal cursor movement ANSI codes (thanks to https://github.com/piotrmurach/tty-cursor)
|
29
24
|
module_function
|
30
25
|
ESC = "\e".freeze
|
31
26
|
CSI = "\e[".freeze
|
@@ -93,6 +88,12 @@ module Cursor # Terminal cursor movement ANSI codes (thanks to https://github.co
|
|
93
88
|
print(CSI + 'J')
|
94
89
|
end
|
95
90
|
end
|
91
|
+
def stdin_clear
|
92
|
+
begin
|
93
|
+
$stdin.getc while $stdin.ready?
|
94
|
+
rescue
|
95
|
+
end
|
96
|
+
end
|
96
97
|
|
97
98
|
# INITIALIZATION
|
98
99
|
begin # Requires
|
@@ -102,13 +103,13 @@ begin # Requires
|
|
102
103
|
end
|
103
104
|
begin # Initialization
|
104
105
|
# Theming
|
105
|
-
@c_prompt =
|
106
|
-
@c_cmd =
|
107
|
-
@c_nick =
|
108
|
-
@c_gnick =
|
109
|
-
@c_path =
|
110
|
-
@c_switch =
|
111
|
-
@c_tabselect =
|
106
|
+
@c_prompt = 10 # Color for basic prompt
|
107
|
+
@c_cmd = 2 # Color for valid command
|
108
|
+
@c_nick = 6 # Color for matching nick
|
109
|
+
@c_gnick = 14 # Color for matching gnick
|
110
|
+
@c_path = 3 # Color for valid path
|
111
|
+
@c_switch = 6 # Color for switches/options
|
112
|
+
@c_tabselect = 5 # Color for selected tabcompleted item
|
112
113
|
@c_taboption = 244 # Color for unselected tabcompleted item
|
113
114
|
@c_stamp = 244 # Color for time stamp/command
|
114
115
|
# Prompt
|
@@ -123,12 +124,14 @@ begin # Initialization
|
|
123
124
|
"/usr/bin",
|
124
125
|
"/home/#{@user}/bin"] # Basic paths for executables if not set in .rshrc
|
125
126
|
# History
|
126
|
-
@histsize =
|
127
|
+
@histsize = 200 # Max history if not set in .rshrc
|
127
128
|
@hloaded = false # Variable to determine if history is loaded
|
128
|
-
# Use run-mailcap instead of xgd-open? Set = true in .rshrc if
|
129
|
+
# Use run-mailcap instead of xgd-open? Set "= true" in .rshrc if you want run-mailcap
|
129
130
|
@runmailcap = false
|
130
131
|
# Variable initializations
|
131
132
|
@dirs = ["."]*10
|
133
|
+
def pre_cmd; end # User-defined function to be run BEFORE command execution
|
134
|
+
def post_cmd; end # User-defined function to be run AFTER command execution
|
132
135
|
end
|
133
136
|
|
134
137
|
# HELP TEXT
|
@@ -158,7 +161,7 @@ end
|
|
158
161
|
Special functions/integrations:
|
159
162
|
* Use `r` to launch rtfm (https://github.com/isene/RTFM) - if you have it installed
|
160
163
|
* Use `f` to launch fzf (https://github.com/junegunn/fzf) - if you have it installed
|
161
|
-
* Use `=` followed by xrpn commands separated by commas (https://github.com/isene/xrpn)
|
164
|
+
* Use `=` followed by xrpn commands separated by commas or double-spaces (https://github.com/isene/xrpn)
|
162
165
|
* Use `:` followed by a Ruby expression to access the whole world of Ruby
|
163
166
|
|
164
167
|
Special commands:
|
@@ -177,7 +180,7 @@ def firstrun
|
|
177
180
|
puts "Since there is no rsh configuration file (.rshrc), I will help you set it up to suit your needs.\n\n"
|
178
181
|
puts "The prompt you see now is the very basic rsh prompt:"
|
179
182
|
print "#{@prompt} (press ENTER)"
|
180
|
-
|
183
|
+
$stdin.gets
|
181
184
|
puts "\nI will now change the prompt into something more useful."
|
182
185
|
puts "Feel free to amend the prompt in your .rshrc.\n\n"
|
183
186
|
rc = <<~RSHRC
|
@@ -202,30 +205,33 @@ RSHRC
|
|
202
205
|
File.write(Dir.home+'/.rshrc', rc)
|
203
206
|
end
|
204
207
|
def getchr # Process key presses
|
205
|
-
c =
|
208
|
+
c = $stdin.getch
|
206
209
|
case c
|
207
|
-
when "\e" # ANSI escape sequences
|
208
|
-
|
210
|
+
when "\e" # ANSI escape sequences (with only ESC, it should stop right here)
|
211
|
+
return "ESC" if $stdin.ready? == nil
|
212
|
+
case $stdin.getc
|
209
213
|
when '[' # CSI
|
210
|
-
case
|
214
|
+
case $stdin.getc # Will get (or ASK) for more (remaining part of special character)
|
211
215
|
when 'A' then chr = "UP"
|
212
216
|
when 'B' then chr = "DOWN"
|
213
217
|
when 'C' then chr = "RIGHT"
|
214
218
|
when 'D' then chr = "LEFT"
|
215
219
|
when 'Z' then chr = "S-TAB"
|
216
|
-
when '2' then chr = "INS" ; chr = "C-INS" if
|
217
|
-
when '3' then chr = "DEL" ; chr = "C-DEL" if
|
218
|
-
when '5' then chr = "PgUP" ; chr = "C-PgUP" if
|
219
|
-
when '6' then chr = "PgDOWN" ; chr = "C-PgDOWN" if
|
220
|
-
when '7' then chr = "HOME" ; chr = "C-HOME" if
|
221
|
-
when '8' then chr = "END" ; chr = "C-END" if
|
220
|
+
when '2' then chr = "INS" ; chr = "C-INS" if $stdin.getc == "^"
|
221
|
+
when '3' then chr = "DEL" ; chr = "C-DEL" if $stdin.getc == "^"
|
222
|
+
when '5' then chr = "PgUP" ; chr = "C-PgUP" if $stdin.getc == "^"
|
223
|
+
when '6' then chr = "PgDOWN" ; chr = "C-PgDOWN" if $stdin.getc == "^"
|
224
|
+
when '7' then chr = "HOME" ; chr = "C-HOME" if $stdin.getc == "^"
|
225
|
+
when '8' then chr = "END" ; chr = "C-END" if $stdin.getc == "^"
|
226
|
+
else chr = ""
|
222
227
|
end
|
223
228
|
when 'O' # Set Ctrl+ArrowKey equal to ArrowKey; May be used for other purposes in the future
|
224
|
-
case
|
229
|
+
case $stdin.getc
|
225
230
|
when 'a' then chr = "C-UP"
|
226
231
|
when 'b' then chr = "C-DOWN"
|
227
232
|
when 'c' then chr = "C-RIGHT"
|
228
233
|
when 'd' then chr = "C-LEFT"
|
234
|
+
else chr = ""
|
229
235
|
end
|
230
236
|
end
|
231
237
|
when "", "" then chr = "BACK"
|
@@ -245,7 +251,9 @@ def getchr # Process key presses
|
|
245
251
|
when "\r" then chr = "ENTER"
|
246
252
|
when "\t" then chr = "TAB"
|
247
253
|
when /[[:print:]]/ then chr = c
|
254
|
+
else chr = ""
|
248
255
|
end
|
256
|
+
stdin_clear
|
249
257
|
return chr
|
250
258
|
end
|
251
259
|
def getstr # A custom Readline-like function
|
@@ -260,11 +268,15 @@ def getstr # A custom Readline-like function
|
|
260
268
|
@c.clear_line
|
261
269
|
print @prompt
|
262
270
|
row, @pos0 = @c.pos
|
271
|
+
#@history[0] = "" if @history[0].nil?
|
263
272
|
print cmd_check(@history[0])
|
264
|
-
@ci = @history[1..].find_index {|e| e =~ /^#{Regexp.escape(@history[0])}
|
265
|
-
|
266
|
-
|
267
|
-
|
273
|
+
@ci = @history[1..].find_index {|e| e =~ /^#{Regexp.escape(@history[0].to_s)}./}
|
274
|
+
unless @ci == nil
|
275
|
+
@ci += 1
|
276
|
+
@ciprompt = @history[@ci][@history[0].to_s.length..].to_s
|
277
|
+
end
|
278
|
+
if @history[0].to_s.length > 1 and @ci
|
279
|
+
print @ciprompt.c(@c_stamp)
|
268
280
|
right = true
|
269
281
|
end
|
270
282
|
@c.col(@pos0 + @pos)
|
@@ -283,10 +295,9 @@ def getstr # A custom Readline-like function
|
|
283
295
|
@c.row(1)
|
284
296
|
@c.clear_screen_down
|
285
297
|
when 'UP' # Go up in history
|
286
|
-
#@history[0] = @history[@history[(@stk+1)..].index{|h| h =~ /#{@history[0]}/}+1] #Future magick
|
287
298
|
if @stk == 0 and @history[0].length > 0
|
288
299
|
@tabsearch = @history[0]
|
289
|
-
|
300
|
+
tab("hist")
|
290
301
|
else
|
291
302
|
if lift
|
292
303
|
@history.unshift("")
|
@@ -296,6 +307,7 @@ def getstr # A custom Readline-like function
|
|
296
307
|
unless @stk >= @history.length - 1
|
297
308
|
@stk += 1
|
298
309
|
@history[0] = @history[@stk].dup
|
310
|
+
@history[0] = "" unless @history[0]
|
299
311
|
@pos = @history[0].length
|
300
312
|
end
|
301
313
|
lift = false
|
@@ -368,6 +380,7 @@ def getstr # A custom Readline-like function
|
|
368
380
|
@pos = 0
|
369
381
|
else
|
370
382
|
@history[0] = @history[@stk].dup
|
383
|
+
@history[0] = "" unless @history[0]
|
371
384
|
@pos = @history[0].length
|
372
385
|
end
|
373
386
|
when 'LDEL' # Delete readline (Ctrl-U)
|
@@ -376,7 +389,8 @@ def getstr # A custom Readline-like function
|
|
376
389
|
lift = true
|
377
390
|
when 'TAB' # Tab completion of dirs and files
|
378
391
|
@ci = nil
|
379
|
-
|
392
|
+
#@tabsearch =~ /^-/ ? tabbing("switch") : tabbing("all")
|
393
|
+
tab("all")
|
380
394
|
lift = true
|
381
395
|
when 'S-TAB'
|
382
396
|
@ci = nil
|
@@ -387,138 +401,133 @@ def getstr # A custom Readline-like function
|
|
387
401
|
@pos += 1
|
388
402
|
lift = true
|
389
403
|
end
|
390
|
-
while
|
391
|
-
chr =
|
404
|
+
while $stdin.ready?
|
405
|
+
chr = $stdin.getc
|
392
406
|
@history[0].insert(@pos,chr)
|
393
407
|
@pos += 1
|
394
408
|
end
|
395
409
|
end
|
410
|
+
@c.col(@pos0 + @history[0].length)
|
411
|
+
@c.clear_screen_down
|
396
412
|
end
|
397
|
-
|
398
|
-
|
399
|
-
@tabend = @history[@stk][@pos..]
|
400
|
-
elements = @tabstr.split(" ")
|
401
|
-
if @tabstr.match(" $")
|
402
|
-
elements.append("")
|
403
|
-
@tabsearch = ""
|
404
|
-
else
|
405
|
-
@tabsearch = elements.last.to_s
|
406
|
-
@tabstr = @tabstr[0...-@tabsearch.length]
|
407
|
-
end
|
408
|
-
i = elements.length - 1
|
409
|
-
if @tabsearch =~ /^-/
|
410
|
-
until i == 0
|
411
|
-
i -= 1
|
412
|
-
if elements[i] !~ /^-/
|
413
|
-
tab_switch(elements[i])
|
414
|
-
break
|
415
|
-
end
|
416
|
-
end
|
417
|
-
elsif type == 'all'
|
418
|
-
tab_all(@tabsearch)
|
419
|
-
elsif type == 'hist'
|
420
|
-
tab_hist(@tabsearch)
|
421
|
-
end
|
422
|
-
@history[@stk] = @tabstr.to_s + @tabsearch.to_s + @tabend.to_s
|
423
|
-
end
|
424
|
-
def tab_all(str) # TAB completion for Dirs/files, nicks and commands
|
425
|
-
exe = []
|
426
|
-
@path.each do |p|
|
427
|
-
Dir.glob(p).each do |c|
|
428
|
-
exe.append(File.basename(c)) if File.executable?(c) and not Dir.exist?(c)
|
429
|
-
end
|
430
|
-
end
|
431
|
-
exe.prepend(*@nick.keys)
|
432
|
-
exe.prepend(*@gnick.keys)
|
433
|
-
compl = exe.select {|s| s =~ Regexp.new("^" + str)}
|
434
|
-
fdir = str
|
435
|
-
fdir += "/" if Dir.exist?(fdir)
|
436
|
-
fdir += "*"
|
437
|
-
compl.prepend(*Dir.glob(fdir))
|
438
|
-
match = tabselect(compl) unless compl.empty?
|
439
|
-
@tabsearch = match if match
|
440
|
-
@pos = @tabstr.length + @tabsearch.length if match
|
441
|
-
end
|
442
|
-
def tab_switch(str) # TAB completion for command switches (TAB after "-")
|
443
|
-
begin
|
444
|
-
hlp = `#{str} --help 2>/dev/null`
|
445
|
-
hlp = hlp.split("\n").grep(/^\s*-{1,2}[^-]/)
|
446
|
-
hlp = hlp.map{|h| h.sub(/^\s*/, '').sub(/^--/, ' --')}
|
447
|
-
switch = tabselect(hlp)
|
448
|
-
switch = switch.sub(/ .*/, '').sub(/,/, '')
|
449
|
-
@tabsearch = switch if switch
|
450
|
-
@pos = @tabstr.length + @tabsearch.length
|
451
|
-
rescue
|
452
|
-
end
|
453
|
-
end
|
454
|
-
def tab_hist(str)
|
455
|
-
sel = @history.select {|el| el =~ /#{str}/}
|
456
|
-
sel.shift
|
457
|
-
sel.delete("")
|
458
|
-
hist = tabselect(sel, true)
|
459
|
-
if hist
|
460
|
-
@tabsearch = hist
|
461
|
-
@tabstr = ""
|
462
|
-
@pos = @tabsearch.length
|
463
|
-
end
|
464
|
-
end
|
465
|
-
def tabselect(ary, hist=false) # Let user select from the incoming array
|
466
|
-
ary.uniq!
|
467
|
-
@c_row, @c_col = @c.pos
|
468
|
-
chr = ""
|
413
|
+
|
414
|
+
def tab(type)
|
469
415
|
i = 0
|
416
|
+
chr = ""
|
417
|
+
@tabarray = []
|
418
|
+
@pretab = @history[0][0...@pos].to_s # Extract the current line up to cursor
|
419
|
+
@postab = @history[0][@pos..].to_s # Extract the current line from cursor to end
|
420
|
+
@c_row, @c_col = @c.pos # Get cursor position
|
421
|
+
@tabstr = @pretab.split(/[|, ]/).last.to_s # Get the sustring that is being tab completed
|
422
|
+
@tabstr = "" if @pretab[-1] =~ /[ |]/
|
423
|
+
@pretab = @pretab.delete_suffix(@tabstr)
|
424
|
+
type = "switch" if @tabstr[0] == "-"
|
470
425
|
while chr != "ENTER"
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
if
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
426
|
+
case type
|
427
|
+
when "hist" # Handle history completions ('UP' key)
|
428
|
+
@tabarray = @history.select {|el| el =~ /#{@tabstr}/} # Select history items matching @tabstr
|
429
|
+
@tabarray.shift # Take away @history[0]
|
430
|
+
return if @tabarray.empty?
|
431
|
+
when "switch"
|
432
|
+
cmdswitch = @pretab.split(/[|, ]/).last.to_s
|
433
|
+
hlp = `#{cmdswitch} --help 2>/dev/null`
|
434
|
+
hlp = hlp.split("\n").grep(/^\s*-{1,2}[^-]/)
|
435
|
+
hlp = hlp.map{|h| h.sub(/^\s*/, '').sub(/^--/, ' --')}
|
436
|
+
hlp = hlp.reject{|h| /-</ =~ h}
|
437
|
+
@tabarray = hlp
|
438
|
+
when "all" # Handle all other tab completions
|
439
|
+
exe = []
|
440
|
+
@path.each do |p| # Add all executables in @path
|
441
|
+
Dir.glob(p).each do |c|
|
442
|
+
exe.append(File.basename(c)) if File.executable?(c) and not Dir.exist?(c)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
exe.sort!
|
446
|
+
exe.prepend(*@nick.keys) # Add nicks
|
447
|
+
exe.prepend(*@gnick.keys) # Add gnicks
|
448
|
+
compl = exe.select {|s| s =~ Regexp.new(@tabstr)} # Select only that which matches so far
|
449
|
+
fdir = @tabstr + "*"
|
450
|
+
compl.prepend(*Dir.glob(fdir)).map! do |e|
|
451
|
+
if e =~ /(?<!\\) /
|
452
|
+
e = e.sub(/(.*\/|^)(.*)/, '\1\'\2\'') unless e =~ /'/
|
453
|
+
end
|
454
|
+
Dir.exist?(e) ? e + "/" : e # Add matching dirs
|
455
|
+
end
|
456
|
+
@tabarray = compl # Finally put it into @tabarray
|
457
|
+
end
|
458
|
+
return if @tabarray.empty?
|
459
|
+
@tabarray.delete("") # Don't remember why
|
460
|
+
@c.clear_screen_down # Here we go
|
461
|
+
@tabarray.length.to_i - i < 5 ? l = @tabarray.length.to_i - i : l = 5 # Max 5 rows of completion items
|
462
|
+
l.times do |x| # Iterate through
|
463
|
+
if x == 0 # First item goes onto the commandline
|
464
|
+
@c.clear_line # Clear the line
|
465
|
+
tabchoice = @tabarray[i] # Select the item from the @tabarray
|
466
|
+
tabchoice = tabchoice.sub(/\s*(-.*?)[,\s].*/, '\1') if type == "switch"
|
467
|
+
@newhist0 = @pretab + tabchoice + @postab # Remember now the new value to be given to @history[0]
|
468
|
+
line1 = cmd_check(@pretab).to_s # Syntax highlight before @tabstr
|
469
|
+
line2 = cmd_check(@postab).to_s # Syntax highlight after @tabstr
|
470
|
+
# Color and underline the current tabchoice on the commandline:
|
471
|
+
tabline = tabchoice.sub(/(.*)#{@tabstr}(.*)/, '\1'.c(@c_tabselect) + @tabstr.u.c(@c_tabselect) + '\2'.c(@c_tabselect))
|
472
|
+
print @prompt + line1 + tabline + line2 # Print the commandline
|
473
|
+
@pos = @pretab.length.to_i + tabchoice.length.to_i # Set the position on that commandline
|
474
|
+
@c_col = @pos0 + @pos # The cursor position must include the prompt as well
|
475
|
+
@c.col(@c_col) # Set the cursor position
|
476
|
+
nextline # Then start showing the completion items
|
477
|
+
tabline = @tabarray[i] # Get the next matching tabline
|
478
|
+
# Can't nest ANSI codes, they must each complete/conclude or they will mess eachother up
|
479
|
+
tabline1 = tabline.sub(/(.*?)#{@tabstr}.*/, '\1').c(@c_tabselect) # Color the part before the @tabstr
|
480
|
+
tabline2 = tabline.sub(/.*?#{@tabstr}(.*)/, '\1').c(@c_tabselect) # Color the part after the @tabstr
|
481
|
+
print " " + tabline1 + @tabstr.c(@c_tabselect).u + tabline2 # Color & underline @tabstr
|
483
482
|
else
|
484
483
|
begin
|
485
|
-
|
484
|
+
tabline = @tabarray[i+x] # Next tabline, and next, etc (usually 4 times here)
|
485
|
+
tabline1 = tabline.sub(/(.*?)#{@tabstr}.*/, '\1').c(@c_taboption) # Color before @tabstr
|
486
|
+
tabline2 = tabline.sub(/.*?#{@tabstr}(.*)/, '\1').c(@c_taboption) # Color after @tabstr
|
487
|
+
print " " + tabline1 + @tabstr.c(@c_taboption).u + tabline2 # Print the whole line
|
486
488
|
rescue
|
487
489
|
end
|
488
490
|
end
|
489
|
-
nextline
|
491
|
+
nextline # To not run off screen
|
490
492
|
end
|
491
|
-
@c.row(@c_row)
|
492
|
-
@c.col(@c_col)
|
493
|
-
chr = getchr
|
494
|
-
case chr
|
495
|
-
when 'C-G', 'C-C'
|
496
|
-
|
497
|
-
return @tabsearch
|
493
|
+
@c.row(@c_row) # Set cursor row to commandline
|
494
|
+
@c.col(@c_col) # Set cursor col on commandline
|
495
|
+
chr = getchr # Now get user input
|
496
|
+
case chr # Treat the keypress
|
497
|
+
when 'C-G', 'C-C', 'ESC'
|
498
|
+
tabend; return
|
498
499
|
when 'DOWN'
|
499
|
-
i += 1 unless i >
|
500
|
+
i += 1 unless i > @tabarray.length.to_i - 2
|
500
501
|
when 'UP'
|
501
502
|
i -= 1 unless i == 0
|
502
|
-
when 'TAB'
|
503
|
+
when 'TAB', 'RIGHT' # Effectively the same as ENTER
|
503
504
|
chr = "ENTER"
|
504
|
-
when 'BACK'
|
505
|
-
if @
|
506
|
-
@
|
507
|
-
|
505
|
+
when 'BACK', 'LEFT' # Delete one character to the left
|
506
|
+
if @tabstr == ""
|
507
|
+
@history[0] = @pretab + @postab
|
508
|
+
tabend
|
509
|
+
return
|
510
|
+
end
|
511
|
+
@tabstr.chop!
|
512
|
+
when 'WBACK' # Delete one word to the left (Ctrl-W)
|
513
|
+
if @tabstr == ""
|
514
|
+
@history[0] = @pretab + @postab
|
515
|
+
tabend
|
516
|
+
return
|
508
517
|
end
|
509
|
-
@
|
510
|
-
|
511
|
-
|
518
|
+
@tabstr.sub!(/#{@tabstr.split(/[|, ]/).last}.*/, '')
|
519
|
+
when ' '
|
520
|
+
@tabstr += " "
|
521
|
+
chr = "ENTER"
|
512
522
|
when /^[[:print:]]$/
|
513
|
-
@
|
514
|
-
|
515
|
-
return @tabsearch
|
523
|
+
@tabstr += chr
|
524
|
+
i = 0
|
516
525
|
end
|
517
526
|
end
|
518
527
|
@c.clear_screen_down
|
519
528
|
@c.row(@c_row)
|
520
529
|
@c.col(@c_col)
|
521
|
-
|
530
|
+
@history[0] = @newhist0
|
522
531
|
end
|
523
532
|
def nextline # Handle going to the next line in the terminal
|
524
533
|
row, col = @c.pos
|
@@ -528,26 +537,33 @@ def nextline # Handle going to the next line in the terminal
|
|
528
537
|
end
|
529
538
|
@c.next_line
|
530
539
|
end
|
540
|
+
def tabend
|
541
|
+
@c.clear_screen_down
|
542
|
+
@pos = @history[0].length
|
543
|
+
@c_col = @pos0 + @pos
|
544
|
+
@c.col(@c_col)
|
545
|
+
end
|
531
546
|
def hist_clean # Clean up @history
|
532
547
|
@history.uniq!
|
533
548
|
@history.compact!
|
534
549
|
@history.delete("")
|
535
550
|
end
|
536
551
|
def cmd_check(str) # Check if each element on the readline matches commands, nicks, paths; color them
|
537
|
-
str.
|
552
|
+
return if str.nil?
|
553
|
+
str.gsub(/(?:\S'[^']*'|[^ '])+/) do |el|
|
538
554
|
if @nick.include?(el)
|
539
555
|
el.c(@c_nick)
|
540
|
-
elsif el == "r"
|
556
|
+
elsif el == "r" or el == "f"
|
541
557
|
el.c(@c_nick)
|
542
558
|
elsif @gnick.include?(el)
|
543
559
|
el.c(@c_gnick)
|
544
|
-
elsif File.exist?(el.
|
560
|
+
elsif File.exist?(el.gsub("'", ""))
|
545
561
|
el.c(@c_path)
|
546
562
|
elsif system "which #{el}", %i[out err] => File::NULL
|
547
563
|
el.c(@c_cmd)
|
548
564
|
elsif el == "cd"
|
549
565
|
el.c(@c_cmd)
|
550
|
-
elsif el
|
566
|
+
elsif el[0] == "-"
|
551
567
|
el.c(@c_switch)
|
552
568
|
else
|
553
569
|
el
|
@@ -568,7 +584,7 @@ def rshrc # Write updates to .rshrc
|
|
568
584
|
conf.sub!(/^@history.*\n/, "")
|
569
585
|
conf += "@history = #{@history.last(@histsize)}\n"
|
570
586
|
File.write(Dir.home+'/.rshrc', conf)
|
571
|
-
puts ".rshrc updated"
|
587
|
+
puts "\n.rshrc updated"
|
572
588
|
end
|
573
589
|
|
574
590
|
# RSH FUNCTIONS
|
@@ -610,9 +626,9 @@ def gnick(nick_str) # Define a generic/global nick to match not only commands (f
|
|
610
626
|
end
|
611
627
|
def nick? # Show nicks
|
612
628
|
puts " Command nicks:".c(@c_nick)
|
613
|
-
@nick.each {|key, value| puts " #{key} = #{value}"}
|
629
|
+
@nick.sort.each {|key, value| puts " #{key} = #{value}"}
|
614
630
|
puts " General nicks:".c(@c_gnick)
|
615
|
-
@gnick.each {|key, value| puts " #{key} = #{value}"}
|
631
|
+
@gnick.sort.each {|key, value| puts " #{key} = #{value}"}
|
616
632
|
end
|
617
633
|
def dirs
|
618
634
|
puts "Past direactories:"
|
@@ -654,6 +670,7 @@ loop do
|
|
654
670
|
h = @history; load(Dir.home+'/.rshrc') if File.exist?(Dir.home+'/.rshrc'); @history = h # reload prompt but not history
|
655
671
|
@prompt.gsub!(/#{Dir.home}/, '~') # Simplify path in prompt
|
656
672
|
system("printf \"\033]0;rsh: #{Dir.pwd}\007\"") # Set Window title to path
|
673
|
+
@history[0] = "" unless @history[0]
|
657
674
|
getstr # Main work is here
|
658
675
|
@cmd = @history[0]
|
659
676
|
@dirs.unshift(Dir.pwd)
|
@@ -676,7 +693,10 @@ loop do
|
|
676
693
|
system("git status .") if Dir.exist?(".git")
|
677
694
|
next
|
678
695
|
end
|
679
|
-
|
696
|
+
if @cmd =~ /^\=/ # Integration with xrpn (https://github.com/isene/xrpn)
|
697
|
+
@cmd.gsub!(" ", ",")
|
698
|
+
@cmd = "echo \"#{@cmd[1...]},prx,off\" | xrpn"
|
699
|
+
end
|
680
700
|
if @cmd.match(/^\s*:/) # Ruby commands are prefixed with ":"
|
681
701
|
begin
|
682
702
|
eval(@cmd[1..-1])
|
@@ -689,7 +709,7 @@ loop do
|
|
689
709
|
else # Execute command
|
690
710
|
ca = @nick.transform_keys {|k| /((^\K\s*\K)|(\|\K\s*\K))\b(?<!-)#{Regexp.escape k}\b/}
|
691
711
|
@cmd = @cmd.gsub(Regexp.union(ca.keys), @nick)
|
692
|
-
ga = @gnick.transform_keys {|k| /\b#{Regexp.escape k}\b/}
|
712
|
+
ga = @gnick.transform_keys {|k| /\b(?<!-)#{Regexp.escape k}\b/}
|
693
713
|
@cmd = @cmd.gsub(Regexp.union(ga.keys), @gnick)
|
694
714
|
@cmd = "~" if @cmd == "cd"
|
695
715
|
@cmd.sub!(/^cd (\S*).*/, '\1')
|
@@ -717,15 +737,17 @@ loop do
|
|
717
737
|
end
|
718
738
|
else
|
719
739
|
begin
|
720
|
-
|
740
|
+
pre_cmd
|
741
|
+
puts " Not executed: #{@cmd}" unless system (@cmd) # Try execute the command
|
742
|
+
post_cmd
|
721
743
|
rescue StandardError => err
|
722
744
|
puts "\n#{err}"
|
723
745
|
end
|
724
746
|
end
|
725
747
|
end
|
726
748
|
end
|
727
|
-
rescue StandardError => err # Throw error nicely
|
728
|
-
|
749
|
+
#rescue StandardError => err # Throw error nicely
|
750
|
+
# puts "\n#{err}"
|
729
751
|
end
|
730
752
|
end
|
731
753
|
|
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: '
|
4
|
+
version: '2.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geir Isene
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-10-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. In
|
15
|
-
continual development. New in
|
16
|
-
|
15
|
+
continual development. New in 2.0: Full rewrite of tab completion engine. Lots of
|
16
|
+
other bug fixes.'
|
17
17
|
email: g@isene.com
|
18
18
|
executables:
|
19
19
|
- rsh
|
@@ -28,7 +28,7 @@ licenses:
|
|
28
28
|
- Unlicense
|
29
29
|
metadata:
|
30
30
|
source_code_uri: https://github.com/isene/rsh
|
31
|
-
post_install_message:
|
31
|
+
post_install_message:
|
32
32
|
rdoc_options: []
|
33
33
|
require_paths:
|
34
34
|
- lib
|
@@ -43,8 +43,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
43
|
- !ruby/object:Gem::Version
|
44
44
|
version: '0'
|
45
45
|
requirements: []
|
46
|
-
rubygems_version: 3.
|
47
|
-
signing_key:
|
46
|
+
rubygems_version: 3.4.20
|
47
|
+
signing_key:
|
48
48
|
specification_version: 4
|
49
49
|
summary: rsh - Ruby SHell
|
50
50
|
test_files: []
|