ruby-shell 0.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 +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: []
|