ruby-shell 1.1 → 2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|