ruby-shell 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rshrc +12 -0
- data/README.md +49 -0
- data/bin/rsh +486 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a6549e60bd6e3eadce8bb94374b5ce63808623993dc3392dacef9baa6aa096db
|
4
|
+
data.tar.gz: a182ba6217152b67ce55167fc7298b46870daf207a472815e3b8dfe2502b8dd2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: faeff68227a6184f93db225bebfff949243b5e5bc92f8d3d6fdb8a84859d5b5af27cd783983fd478402ff0caef56f0bc464bcf076792b38affac0199a8369d3f
|
7
|
+
data.tar.gz: b53005bad6d781cc5e113110ee7e56fb90e79f7643044a61ddfc4f4648637f245f84b2d5af6b8134b6693d941251cea472100eb14a7bc934f09dab96b46ec86e
|
data/.rshrc
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
@prompt = "#{@user}@#{@node}".c(46) + ":".c(255) + " #{Dir.pwd}/".c(196) + " ".c(7)
|
2
|
+
@nick = {"ls"=>"ls --color -F", "la"=>"ls -a --color -F", "ll"=>"ls -l --color -F", "l"=>"less"}
|
3
|
+
@gnick = {}
|
4
|
+
@history = []
|
5
|
+
@c_prompt = 196 # Color for basic prompt
|
6
|
+
@c_cmd = 48 # Color for valid command
|
7
|
+
@c_nick = 51 # Color for matching nick
|
8
|
+
@c_gnick = 87 # Color for matching gnick
|
9
|
+
@c_path = 208 # Color for valid path
|
10
|
+
@c_tabselect = 207 # Color for selected tabcompleted item
|
11
|
+
@c_taboption = 244 # Color for unselected tabcompleted item
|
12
|
+
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# rsh
|
2
|
+
The Ruby SHell
|
3
|
+
|
4
|
+
# Why?
|
5
|
+
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.
|
6
|
+
|
7
|
+
# Design principles
|
8
|
+
Simple. One file. Minimum external requirements.
|
9
|
+
|
10
|
+
# Installation
|
11
|
+
Clone this repo and drop `rsh` into your preferred bin directory. Drop `.rshrc` into your home directory and edit as you see fit.
|
12
|
+
|
13
|
+
Or simply `gem install rsh`.
|
14
|
+
|
15
|
+
# Features
|
16
|
+
* Aliases (called `nicks`in rsh) - both for commands and general nicks
|
17
|
+
* Syntax highlighting, matching nicks, system commands and valid dirs/files
|
18
|
+
* Tab completions for nicks, system commands, command switches and dirs/files
|
19
|
+
* Tab completion presents matches in a list to pick from
|
20
|
+
* History with simple editing
|
21
|
+
* Config file (`.rshrc`) updates on exit
|
22
|
+
* Set of simple rsh specific commands like `nick`, `nick?`, `history` and `rmhistory`
|
23
|
+
* rsh specific commands and full set of Ruby commands available via `:command`
|
24
|
+
* All colors are themable in `.rshrc`
|
25
|
+
|
26
|
+
## Nicks
|
27
|
+
Add command nicks (aliases) with `:nick "some_nick = some_command"`, e.g. `:nick "ls = ls --color"`. Add general nicks that will substitute anything on a command line (not just commands) like this `:gnick "some_gnick = some_command"`, e.g. `:gnick "x = /home/user/somewhere"`. List (g)nicks with `:nicks?`. Remove a nick with `:nick "-some_command"`, e.g. `:nick "-ls"` to remove an `ls` nick. Same for gnicks.
|
28
|
+
|
29
|
+
## Tab completion
|
30
|
+
You can tab complete almost anything. Hitting `TAB` will try to complete in this priority: nicks, gnicks, commands, dirs/files. Hitting `TAB`after a `-` will list the command switches for the preceeding command with a short explanation (from the command's --help), like this `ls -`(`TAB`) will list all the switches/options for the `ls` command.
|
31
|
+
|
32
|
+
## Syntax highlighting
|
33
|
+
rsh will highlight nicks, gnicks, commands and dirs/files as they are written on the command line.
|
34
|
+
|
35
|
+
## Theming
|
36
|
+
In the supplied `.rshrc`, you will find a set of colors that you can change:
|
37
|
+
|
38
|
+
Variable | Description
|
39
|
+
----------------|-----------------------------------------
|
40
|
+
`@c_prompt` | Color for basic prompt
|
41
|
+
`@c_cmd` | Color for valid command
|
42
|
+
`@c_nick` | Color for matching nick
|
43
|
+
`@c_gnick` | Color for matching gnick
|
44
|
+
`@c_path` | Color for valid path
|
45
|
+
`@c_tabselect` | Color for selected tabcompleted item
|
46
|
+
`@c_taboption` | Color for unselected tabcompleted item
|
47
|
+
|
48
|
+
# License and copyringht
|
49
|
+
Forget it.
|
data/bin/rsh
ADDED
@@ -0,0 +1,486 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# SCRIPT INFO
|
5
|
+
# Name: RTFM - Ruby Terminal File Manager
|
6
|
+
# Language: Pure Ruby, best viewed in VIM
|
7
|
+
# Author: Geir Isene <g@isene.com>
|
8
|
+
# Web_site: http://isene.com/
|
9
|
+
# Github: https://github.com/isene/RTFM
|
10
|
+
# License: I release all copyright claims. This code is in the public domain.
|
11
|
+
# Permission is granted to use, copy modify, distribute, and sell
|
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 = "0.1"
|
18
|
+
|
19
|
+
# MODULES, CLASSES AND EXTENSIONS
|
20
|
+
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
|
26
|
+
def color(text, color_code) "#{color_code}#{text}\001\e[0m\002" end
|
27
|
+
end
|
28
|
+
module Cursor # Terminal cursor movement ANSI codes (thanks to https://github.com/piotrmurach/tty-cursor/tree/master)
|
29
|
+
module_function
|
30
|
+
ESC = "\e".freeze
|
31
|
+
CSI = "\e[".freeze
|
32
|
+
def save # Save current position
|
33
|
+
print(Gem.win_platform? ? CSI + 's' : ESC + '7')
|
34
|
+
end
|
35
|
+
def restore # Restore cursor position
|
36
|
+
print(Gem.win_platform? ? CSI + 'u' : ESC + '8')
|
37
|
+
end
|
38
|
+
def pos # Query cursor current position
|
39
|
+
res = ''
|
40
|
+
$stdin.raw do |stdin|
|
41
|
+
$stdout << "\e[6n"
|
42
|
+
$stdout.flush
|
43
|
+
while (c = stdin.getc) != 'R'
|
44
|
+
res << c if c
|
45
|
+
end
|
46
|
+
end
|
47
|
+
m = res.match /(?<row>\d+);(?<col>\d+)/
|
48
|
+
return m[:row].to_i, m[:col].to_i
|
49
|
+
end
|
50
|
+
def up(n = nil) # Move cursor up by n
|
51
|
+
print(CSI + "#{(n || 1)}A")
|
52
|
+
end
|
53
|
+
def down(n = nil) # Move the cursor down by n
|
54
|
+
print(CSI + "#{(n || 1)}B")
|
55
|
+
end
|
56
|
+
def left(n = nil) # Move the cursor backward by n
|
57
|
+
print(CSI + "#{n || 1}D")
|
58
|
+
end
|
59
|
+
def right(n = nil) # Move the cursor forward by n
|
60
|
+
print(CSI + "#{n || 1}C")
|
61
|
+
end
|
62
|
+
def col(n = nil) # Cursor moves to nth position horizontally in the current line
|
63
|
+
print(CSI + "#{n || 1}G")
|
64
|
+
end
|
65
|
+
def row(n = nil) # Cursor moves to the nth position vertically in the current column
|
66
|
+
print(CSI + "#{n || 1}d")
|
67
|
+
end
|
68
|
+
def next_line # Move cursor down to beginning of next line
|
69
|
+
print(CSI + 'E' + CSI + "1G")
|
70
|
+
end
|
71
|
+
def prev_line # Move cursor up to beginning of previous line
|
72
|
+
print(CSI + 'A' + CSI + "1G")
|
73
|
+
end
|
74
|
+
def clear_char(n = nil) # Erase n characters from the current cursor position
|
75
|
+
print(CSI + "#{n}X")
|
76
|
+
end
|
77
|
+
def clear_line # Erase the entire current line and return to beginning of the line
|
78
|
+
print(CSI + '2K' + CSI + "1G")
|
79
|
+
end
|
80
|
+
def clear_line_before # Erase from the beginning of the line up to and including the current cursor position.
|
81
|
+
print(CSI + '1K')
|
82
|
+
end
|
83
|
+
def clear_line_after # Erase from the current position (inclusive) to the end of the line
|
84
|
+
print(CSI + '0K')
|
85
|
+
end
|
86
|
+
def scroll_up # Scroll display up one line
|
87
|
+
print(ESC + 'M')
|
88
|
+
end
|
89
|
+
def scroll_down # Scroll display down one line
|
90
|
+
print(ESC + 'D')
|
91
|
+
end
|
92
|
+
def clear_screen_down
|
93
|
+
print(CSI + 'J')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
# INITIALIZATION
|
97
|
+
begin # Requires
|
98
|
+
require 'etc'
|
99
|
+
require 'io/console'
|
100
|
+
end
|
101
|
+
begin # Initialization
|
102
|
+
# Theming
|
103
|
+
@c_prompt = 196 # Color for basic prompt
|
104
|
+
@c_cmd = 48 # Color for valid command
|
105
|
+
@c_nick = 51 # Color for matching nick
|
106
|
+
@c_gnick = 87 # Color for matching gnick
|
107
|
+
@c_path = 208 # Color for valid path
|
108
|
+
@c_tabselect = 207 # Color for selected tabcompleted item
|
109
|
+
@c_taboption = 244 # Color for unselected tabcompleted item
|
110
|
+
# Prompt
|
111
|
+
@user = Etc.getlogin # For use in @prompt
|
112
|
+
@node = Etc.uname[:nodename] # For use in @prompt
|
113
|
+
@prompt = "rsh >".c(@c_prompt).b # Very basic prompt if not defined in .rshrc
|
114
|
+
# Hash & array initializations
|
115
|
+
@nick = {} # Initiate alias/nick hash
|
116
|
+
@gnick = {} # Initiate generic/global alias/nick hash
|
117
|
+
@history = [] # Initiate history array
|
118
|
+
# Paths
|
119
|
+
@path = ["/bin",
|
120
|
+
"/usr/bin",
|
121
|
+
"/home/#{@user}/bin"] # Basic paths for executables if not set in .rshrc
|
122
|
+
# History
|
123
|
+
@histsize = 100 # Max history if not set in .rshrc
|
124
|
+
@hloaded = false # Variable to determine if history is loaded
|
125
|
+
# Variable initializations
|
126
|
+
@cmd = "" # Initiate variable @cmd
|
127
|
+
end
|
128
|
+
# GENERIC FUNCTIONS
|
129
|
+
def getchr # Process key presses
|
130
|
+
c = STDIN.getch
|
131
|
+
case c
|
132
|
+
when "\e" # ANSI escape sequences
|
133
|
+
case $stdin.getc
|
134
|
+
when '[' # CSI
|
135
|
+
case $stdin.getc
|
136
|
+
when 'A' then chr = "UP"
|
137
|
+
when 'B' then chr = "DOWN"
|
138
|
+
when 'C' then chr = "RIGHT"
|
139
|
+
when 'D' then chr = "LEFT"
|
140
|
+
when 'Z' then chr = "S-TAB"
|
141
|
+
when '2' then chr = "INS" ; chr = "C-INS" if STDIN.getc == "^"
|
142
|
+
when '3' then chr = "DEL" ; chr = "C-DEL" if STDIN.getc == "^"
|
143
|
+
when '5' then chr = "PgUP" ; chr = "C-PgUP" if STDIN.getc == "^"
|
144
|
+
when '6' then chr = "PgDOWN" ; chr = "C-PgDOWN" if STDIN.getc == "^"
|
145
|
+
when '7' then chr = "HOME" ; chr = "C-HOME" if STDIN.getc == "^"
|
146
|
+
when '8' then chr = "END" ; chr = "C-END" if STDIN.getc == "^"
|
147
|
+
end
|
148
|
+
when 'O' # Set Ctrl+ArrowKey equal to ArrowKey; May be used for other purposes in the future
|
149
|
+
case $stdin.getc
|
150
|
+
when 'a' then chr = "C-UP"
|
151
|
+
when 'b' then chr = "C-DOWN"
|
152
|
+
when 'c' then chr = "C-RIGHT"
|
153
|
+
when 'd' then chr = "C-LEFT"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
when "", "" then chr = "BACK"
|
157
|
+
when "" then chr = "C-C"
|
158
|
+
when "" then chr = "C-D"
|
159
|
+
when "" then chr = "C-G"
|
160
|
+
when "" then chr = "C-K"
|
161
|
+
when "" then chr = "C-L"
|
162
|
+
when "" then chr = "C-N"
|
163
|
+
when "" then chr = "C-O"
|
164
|
+
when "" then chr = "C-P"
|
165
|
+
when "" then chr = "C-T"
|
166
|
+
when "" then chr = "C-Y"
|
167
|
+
when "" then chr = "WBACK"
|
168
|
+
when "" then chr = "LDEL"
|
169
|
+
when "\r" then chr = "ENTER"
|
170
|
+
when "\t" then chr = "TAB"
|
171
|
+
when /./ then chr = c
|
172
|
+
end
|
173
|
+
return chr
|
174
|
+
end
|
175
|
+
def getstr # A custom Readline-like function
|
176
|
+
@stk = 0
|
177
|
+
@pos = 0
|
178
|
+
chr = ""
|
179
|
+
@history.insert(0, "")
|
180
|
+
@history_copy = @history.map(&:clone)
|
181
|
+
while chr != "ENTER" # Keep going with readline until user presses ENTER
|
182
|
+
text = @history_copy[@stk]
|
183
|
+
@c.clear_line
|
184
|
+
print @prompt
|
185
|
+
row, pos0 = @c.pos
|
186
|
+
print cmd_check(text)
|
187
|
+
@c.col(pos0 + @pos)
|
188
|
+
chr = getchr
|
189
|
+
return "\n" if chr == "C-G" # Ctrl-G escapes the reasdline
|
190
|
+
case chr
|
191
|
+
when 'C-C' # Ctrl-C exits gracefully but without updating .rshrc
|
192
|
+
print "\n"
|
193
|
+
exit
|
194
|
+
when 'C-D' # Ctrl-D exits after updating .rshrc
|
195
|
+
rshrc
|
196
|
+
exit
|
197
|
+
when 'C-L' # Clear screen and set position to top of the screen
|
198
|
+
@c.row(1)
|
199
|
+
@c.clear_screen_down
|
200
|
+
when 'UP' # Go up in history
|
201
|
+
unless @stk >= @history_copy.length - 1
|
202
|
+
@stk += 1
|
203
|
+
@pos = @history_copy[@stk].length
|
204
|
+
end
|
205
|
+
when 'DOWN' # Go down in history
|
206
|
+
unless @stk == 0
|
207
|
+
@stk -= 1
|
208
|
+
@pos = @history_copy[@stk].length
|
209
|
+
end
|
210
|
+
when 'RIGHT' # Move right on the readline
|
211
|
+
@pos += 1 unless @pos >= @history_copy[@stk].length
|
212
|
+
when 'LEFT' # Move left on the readline
|
213
|
+
@pos -= 1 unless @pos <= 0
|
214
|
+
when 'HOME' # Go to beginning of the readline
|
215
|
+
@pos = 0
|
216
|
+
when 'END' # Go to the end of the readline
|
217
|
+
@pos = @history_copy[@stk].length
|
218
|
+
when 'DEL' # Delete one character
|
219
|
+
@history_copy[@stk][@pos] = ""
|
220
|
+
when 'BACK' # Delete one character to the left
|
221
|
+
unless @pos <= 0
|
222
|
+
@pos -= 1
|
223
|
+
@history_copy[@stk][@pos] = ""
|
224
|
+
end
|
225
|
+
when 'WBACK' # Delete one word to the left (Ctrl-W)
|
226
|
+
unless @pos == pos0
|
227
|
+
until @history_copy[@stk][@pos - 1] == " " or @pos == 0
|
228
|
+
@pos -= 1
|
229
|
+
@history_copy[@stk][@pos] = ""
|
230
|
+
end
|
231
|
+
if @history_copy[@stk][@pos - 1] == " "
|
232
|
+
@pos -= 1
|
233
|
+
@history_copy[@stk][@pos] = ""
|
234
|
+
end
|
235
|
+
end
|
236
|
+
when 'C-K' # Kill/delete that entry in the history
|
237
|
+
@history_copy.delete_at(@stk)
|
238
|
+
@history.delete_at(@stk)
|
239
|
+
@stk -= 1
|
240
|
+
@pos = @history_copy[@stk].length
|
241
|
+
when 'LDEL' # Delete readline (Ctrl-U)
|
242
|
+
@history_copy[@stk] = ""
|
243
|
+
@pos = 0
|
244
|
+
when 'TAB' # Tab completion of dirs and files
|
245
|
+
@tabstr = @history_copy[@stk][0...@pos]
|
246
|
+
@tabend = @history_copy[@stk][@pos..]
|
247
|
+
elements = @tabstr.split(" ")
|
248
|
+
elements.append("") if @tabstr.match(" $")
|
249
|
+
i = elements.length - 1
|
250
|
+
m = elements[i].to_s
|
251
|
+
if m == "-"
|
252
|
+
until i == 0
|
253
|
+
i -= 1
|
254
|
+
if elements[i] !~ /^-/
|
255
|
+
@tabstr.chop!
|
256
|
+
tab_switch(elements[i])
|
257
|
+
break
|
258
|
+
end
|
259
|
+
end
|
260
|
+
else
|
261
|
+
tab_all(m)
|
262
|
+
end
|
263
|
+
@history_copy[@stk] = @tabstr + @tabend
|
264
|
+
when /^.$/
|
265
|
+
@history_copy[@stk].insert(@pos,chr)
|
266
|
+
@pos += 1
|
267
|
+
end
|
268
|
+
end
|
269
|
+
@history.insert(0, @history_copy[@stk])
|
270
|
+
@history[0]
|
271
|
+
end
|
272
|
+
def tab_switch(str) # TAB completion for command switches (TAB after "-")
|
273
|
+
hlp = `#{str} --help`
|
274
|
+
hlp = hlp.split("\n").grep(/^\s*-{1,2}[^-]/)
|
275
|
+
hlp = hlp.map {|h| h.sub(/^\s*/, '')}
|
276
|
+
switch, tab = tabselect(hlp)
|
277
|
+
switch = switch.sub(/ .*/, '').sub(/,/, '')
|
278
|
+
@tabstr += switch
|
279
|
+
@pos = @tabstr.length
|
280
|
+
end
|
281
|
+
def tab_all(str) # TAB completion for Dirs/files, nicks and commands
|
282
|
+
exe = []
|
283
|
+
@path.each do |p|
|
284
|
+
Dir.glob(p).each do |c|
|
285
|
+
exe.append(File.basename(c)) if File.executable?(c) and not Dir.exist?(c)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
exe.prepend(*@nick.keys)
|
289
|
+
exe.prepend(*@gnick.keys)
|
290
|
+
compl = exe.select {|s| s =~ Regexp.new("^" + str)}
|
291
|
+
fdir = File.expand_path(str)
|
292
|
+
fdir += "/" if Dir.exist?(fdir)
|
293
|
+
fdir += "*"
|
294
|
+
compl.prepend(*Dir.glob(fdir))
|
295
|
+
@tabstr = @tabstr.sub(/#{str}$/, '') unless compl.empty?
|
296
|
+
match, tab = tabselect(compl) unless compl.empty?
|
297
|
+
@tabstr += match if match
|
298
|
+
@tabstr += str if match == ""
|
299
|
+
@pos = @tabstr.length if match
|
300
|
+
end
|
301
|
+
def nextline # Handle going to the next line in the terminal
|
302
|
+
row, col = @c.pos
|
303
|
+
if row == @maxrow
|
304
|
+
@c.scroll_down
|
305
|
+
@c_row -= 1
|
306
|
+
end
|
307
|
+
@c.next_line
|
308
|
+
end
|
309
|
+
def tabselect(ary) # Let user select from the incoming array
|
310
|
+
@c_row, @c_col = @c.pos
|
311
|
+
chr = ""
|
312
|
+
tab = false
|
313
|
+
i = 0
|
314
|
+
ary.length < 5 ? l = ary.length : l = 5
|
315
|
+
while chr != "ENTER"
|
316
|
+
@c.clear_screen_down
|
317
|
+
l.times do |x|
|
318
|
+
if x == 0
|
319
|
+
@c.clear_line
|
320
|
+
print "#{@prompt}#{@tabstr}#{ary[i].sub(/(.*?)[ ,].*/, '\1')}#{@tabend}"
|
321
|
+
nextline
|
322
|
+
print " #{ary[i]}".c(@c_tabselect)
|
323
|
+
else
|
324
|
+
print " #{ary[x+i]}".c(@c_taboption)
|
325
|
+
end
|
326
|
+
nextline
|
327
|
+
end
|
328
|
+
@c.row(@c_row)
|
329
|
+
@c.col(@c_col)
|
330
|
+
chr = getchr
|
331
|
+
case chr
|
332
|
+
when 'C-G', 'C-C'
|
333
|
+
@c.clear_screen_down
|
334
|
+
return ""
|
335
|
+
when 'DOWN'
|
336
|
+
i += 1 unless i > ary.length - 2
|
337
|
+
when 'UP'
|
338
|
+
i -= 1 unless i == 0
|
339
|
+
when'TAB'
|
340
|
+
chr = "ENTER"
|
341
|
+
tab = true
|
342
|
+
end
|
343
|
+
end
|
344
|
+
@c.clear_screen_down
|
345
|
+
@c.row(@c_row)
|
346
|
+
@c.col(@c_col)
|
347
|
+
return ary[i], tab
|
348
|
+
end
|
349
|
+
def hist_clean # Clean up @history
|
350
|
+
@history.uniq!
|
351
|
+
@history.compact!
|
352
|
+
@history.delete("")
|
353
|
+
end
|
354
|
+
def cmd_check(str) # Check if each element on the readline matches commands, nicks, paths; color them
|
355
|
+
str.gsub(/\S+/) do |el|
|
356
|
+
if @nick.include?(el)
|
357
|
+
el.c(@c_nick)
|
358
|
+
elsif @gnick.include?(el)
|
359
|
+
el.c(@c_gnick)
|
360
|
+
elsif File.exists?(el.sub(/^~/, "/home/#{@user}"))
|
361
|
+
el.c(@c_path)
|
362
|
+
elsif system("which '#{el}' >/dev/null")
|
363
|
+
el.c(@c_cmd)
|
364
|
+
else
|
365
|
+
el
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
def rshrc # Write updates to .rshrc
|
370
|
+
hist_clean
|
371
|
+
if File.exist?(Dir.home+'/.rshrc')
|
372
|
+
conf = File.read(Dir.home+'/.rshrc')
|
373
|
+
else
|
374
|
+
conf = ""
|
375
|
+
end
|
376
|
+
conf.sub!(/^@nick.*\n/, "")
|
377
|
+
conf += "@nick = #{@nick}\n"
|
378
|
+
conf.sub!(/^@gnick.*\n/, "")
|
379
|
+
conf += "@gnick = #{@gnick}\n"
|
380
|
+
conf.sub!(/^@history.*\n/, "")
|
381
|
+
conf += "@history = #{@history.last(@histsize)}\n"
|
382
|
+
File.write(Dir.home+'/.rshrc', conf)
|
383
|
+
puts "\n .rshrc updated"
|
384
|
+
end
|
385
|
+
# RSH FUNCTIONS
|
386
|
+
def history # Show history
|
387
|
+
puts "History:"
|
388
|
+
puts @history
|
389
|
+
end
|
390
|
+
def rmhistory # Delete history
|
391
|
+
@history = []
|
392
|
+
puts "History deleted."
|
393
|
+
end
|
394
|
+
def nick(nick_str) # Define a new nick like this: `:nick "ls = ls --color"`
|
395
|
+
if nick_str.match(/^\s*-/)
|
396
|
+
source = nick_str.sub(/^\s*-/, '')
|
397
|
+
@nick.delete(source)
|
398
|
+
else
|
399
|
+
source = nick_str.sub(/ =.*/, '')
|
400
|
+
target = nick_str.sub(/.*= /, '')
|
401
|
+
@nick[source] = target
|
402
|
+
end
|
403
|
+
rshrc
|
404
|
+
end
|
405
|
+
def gnick(nick_str) # Define a generic/global nick to match not only commands (format like nick)
|
406
|
+
if nick_str.match(/^\s*-/)
|
407
|
+
source = nick_str.sub(/^\s*-/, '')
|
408
|
+
@gnick.delete(source)
|
409
|
+
else
|
410
|
+
source = nick_str.sub(/ =.*/, '')
|
411
|
+
target = nick_str.sub(/.*= /, '')
|
412
|
+
@gnick[source] = target
|
413
|
+
end
|
414
|
+
rshrc
|
415
|
+
end
|
416
|
+
def nick? # Show nicks
|
417
|
+
puts " Command nicks:".c(@c_nick)
|
418
|
+
@nick.each {|key, value| puts " #{key} = #{value}"}
|
419
|
+
puts " General nicks:".c(@c_gnick)
|
420
|
+
@gnick.each {|key, value| puts " #{key} = #{value}"}
|
421
|
+
end
|
422
|
+
|
423
|
+
# INITIAL SETUP
|
424
|
+
begin # Load .rshrc and populate @history
|
425
|
+
trap "SIGINT" do print "\n"; exit end
|
426
|
+
load(Dir.home+'/.rshrc') if File.exist?(Dir.home+'/.rshrc') # Initial loading - to get history
|
427
|
+
@c = Cursor # Initiate @c as Cursor
|
428
|
+
@c.save # Get max row & col
|
429
|
+
@c.row(8000)
|
430
|
+
@c.col(8000)
|
431
|
+
@maxrow, @maxcol = @c.pos
|
432
|
+
@c.restore # Max row & col gotten, cursor restored
|
433
|
+
hist_clean # Remove duplicates, etc
|
434
|
+
@path.map! {|p| p + "/*"} # Set proper format for path search
|
435
|
+
end
|
436
|
+
|
437
|
+
# MAIN PART
|
438
|
+
loop do
|
439
|
+
h = @history; load(Dir.home+'/.rshrc') if File.exist?(Dir.home+'/.rshrc'); @history = h # reload prompt but not history
|
440
|
+
@prompt.gsub!(/#{Dir.home}/, '~') # Simplify path in prompt
|
441
|
+
@cmd = getstr # Main work is here
|
442
|
+
hist_clean # Clean up the history
|
443
|
+
@cmd = "ls" if @cmd == "" # Default to ls when no command is given
|
444
|
+
print "\n" # Newline
|
445
|
+
if @cmd == "x" then rshrc; break; end # A simple way to exit rsh
|
446
|
+
if @cmd == "r" # Integration with rtfm (https://github.com/isene/RTFM)
|
447
|
+
File.write("/tmp/.rshpwd", Dir.pwd)
|
448
|
+
system("rtfm /tmp/.rshpwd")
|
449
|
+
Dir.chdir(File.read("/tmp/.rshpwd"))
|
450
|
+
next
|
451
|
+
end
|
452
|
+
begin # Execute command
|
453
|
+
if @cmd.match(/^\s*:/) # Ruby commands are prefixed with ":"
|
454
|
+
eval(@cmd[1..-1])
|
455
|
+
else
|
456
|
+
begin # Try cd to cmd
|
457
|
+
@cmd.sub!(/^cd (\S*).*/, '\1')
|
458
|
+
@cmd = Dir.home if @cmd == "~"
|
459
|
+
Dir.chdir(@cmd.strip.sub(/~/, Dir.home))
|
460
|
+
rescue # If cd fails, execute cmd (unless no such cmd)
|
461
|
+
ca = @nick.transform_keys {|k| /((^\K\s*\K)|(\|\K\s*\K))\b(?<!-)#{Regexp.escape k}\b/}
|
462
|
+
@cmd = @cmd.gsub(Regexp.union(ca.keys), @nick)
|
463
|
+
ga = @gnick.transform_keys {|k| /\b#{Regexp.escape k}\b/}
|
464
|
+
@cmd = @cmd.gsub(Regexp.union(ga.keys), @gnick)
|
465
|
+
puts "#{Time.now.strftime("%H:%M:%S")}: #{@cmd}".c(244)
|
466
|
+
begin
|
467
|
+
if @cmd == "fzf" # fzf integration (https://github.com/junegunn/fzf)
|
468
|
+
res = `#{@cmd}`.chomp
|
469
|
+
Dir.chdir(File.dirname(res))
|
470
|
+
elsif system(@cmd) # Try execute the command
|
471
|
+
else
|
472
|
+
puts "No such command: #{@cmd}"
|
473
|
+
end
|
474
|
+
rescue
|
475
|
+
if File.exist?(@cmd) # Try to open file with user's editor
|
476
|
+
system("#{ENV['EDITOR']} #{@cmd}")
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
rescue StandardError => err # Throw error nicely
|
482
|
+
puts "#{err}"
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
# vim: set sw=2 sts=2 et fdm=syntax fdn=2 fcs=fold\:\ :
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-shell
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Geir Isene
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-23 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A shell written in Ruby with extensive tab completions, aliases/nicks,
|
14
|
+
history, syntax highlighting and theming.
|
15
|
+
email: g@isene.com
|
16
|
+
executables:
|
17
|
+
- rsh
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".rshrc"
|
22
|
+
- README.md
|
23
|
+
- bin/rsh
|
24
|
+
homepage: https://isene.com/
|
25
|
+
licenses:
|
26
|
+
- Unlicense
|
27
|
+
metadata:
|
28
|
+
source_code_uri: https://github.com/isene/rsh
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubygems_version: 3.3.5
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: rsh - Ruby SHell
|
48
|
+
test_files: []
|