rubsh 0.0.1 → 0.0.3
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/README.rdoc +103 -0
- data/Rakefile +1 -1
- data/bin/rubsh +9 -7
- data/lib/alias.rb +23 -13
- data/lib/commands.rb +64 -66
- data/lib/completion.rb +111 -0
- data/lib/rub_readline.rb +57 -7
- data/lib/rubsh.rb +460 -123
- data/lib/shell_parser.rb +141 -0
- data/lib/shell_pipeline.rb +131 -0
- data/rubsh.gemspec +9 -7
- data/spec/completion_spec.rb +45 -0
- data/spec/rubsh_spec.rb +177 -0
- data/test/rubsh_test.rb +45 -0
- metadata +48 -39
- data/Manifest +0 -11
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ca63773e13fa92b984e44144945aa43955b9f4468881e819581a65b8015f7b4d
|
|
4
|
+
data.tar.gz: 4090f30591db271c5f50230c93270e542d59a2f19c4b3e60e3f09fbc8160b56f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9717cb3327ac4176352ed8dc3ac818cac44f5d0a77a5b9775d0f992902b8723af461c93a66b02acd730471c77c94d11c9d3d390824ac1c1fde41e65f58492899
|
|
7
|
+
data.tar.gz: 54fbae882195e3851d33c9eadef7d2dfbd4181f2fcef701574d38b0ccaf2a4811350a78e8f94e7fa1da7cfb29a6c7258b9aef802e4edabe38829616046ba44ba
|
data/README.rdoc
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
= Rubsh
|
|
2
|
+
|
|
3
|
+
Rubsh was created to be a native shell for ruby users. A irb which can also be
|
|
4
|
+
used as a normal shell without the akwardness of some of the features of bash
|
|
5
|
+
and other shells, and with the power of ruby.
|
|
6
|
+
|
|
7
|
+
= Installation
|
|
8
|
+
|
|
9
|
+
sudo gem source -a http://gemcutter.org
|
|
10
|
+
sudo gem install rubsh
|
|
11
|
+
|
|
12
|
+
Run rubsh on the commandline. Type help for a quick overview of usage.
|
|
13
|
+
|
|
14
|
+
= Features
|
|
15
|
+
|
|
16
|
+
* readline support
|
|
17
|
+
* tab completion
|
|
18
|
+
* aliases
|
|
19
|
+
* commandline history
|
|
20
|
+
|
|
21
|
+
= Usage
|
|
22
|
+
|
|
23
|
+
== aliases
|
|
24
|
+
|
|
25
|
+
Aliases use shell syntax and remain available for the current session:
|
|
26
|
+
|
|
27
|
+
alias ls "ls -Gh"
|
|
28
|
+
|
|
29
|
+
Put the same line in <tt>~/.config/rubsh/config.rb</tt> to load it at startup.
|
|
30
|
+
Alias values are parsed again as shell commands, so quoting and pipeline arguments are preserved.
|
|
31
|
+
|
|
32
|
+
== ~/.config/rubsh/config.rb
|
|
33
|
+
|
|
34
|
+
Define Ruby functions and persistent aliases in this file. Rubsh loads it when the shell starts.
|
|
35
|
+
|
|
36
|
+
== Shortcuts
|
|
37
|
+
|
|
38
|
+
While normal commands as 'ls' etc work transparantly, you can also do things like:
|
|
39
|
+
|
|
40
|
+
'*'.ls
|
|
41
|
+
|
|
42
|
+
shortcut for Dir.glob(...)
|
|
43
|
+
|
|
44
|
+
'*'.ls.du
|
|
45
|
+
|
|
46
|
+
Does du on all the results
|
|
47
|
+
|
|
48
|
+
It also takes arguments:
|
|
49
|
+
|
|
50
|
+
'*'.ls.du(:sh)
|
|
51
|
+
|
|
52
|
+
is equivalent to du -sh *
|
|
53
|
+
|
|
54
|
+
== Prompt
|
|
55
|
+
|
|
56
|
+
use ENV['PR1'] to set your prompt. see 'help prompt' for options from within the shell.
|
|
57
|
+
|
|
58
|
+
%h - hostname
|
|
59
|
+
%u - the username of the current user
|
|
60
|
+
%w - the current working directory, with $HOME abbreviated with a tilde
|
|
61
|
+
%W - the basename of the current working directory, with $HOME abbreviated
|
|
62
|
+
with a tilde
|
|
63
|
+
%Cb - blue color
|
|
64
|
+
%Cc - cyan color
|
|
65
|
+
%Cg - green color
|
|
66
|
+
%CC - color reset
|
|
67
|
+
%t - the current time in 24-hour HH:MM:SS format
|
|
68
|
+
%% - literal %
|
|
69
|
+
%$ - if the effective UID is 0, a #, otherwise a $
|
|
70
|
+
|
|
71
|
+
Example: ENV['PR1'] = "[%u@%h]--(%t)\n\r[%w]%$ "
|
|
72
|
+
|
|
73
|
+
== Ruby command functions
|
|
74
|
+
|
|
75
|
+
Rubsh keeps one Ruby context for the shell session. Define a function over multiple lines,
|
|
76
|
+
then use it as a command or inside backtick command substitution:
|
|
77
|
+
|
|
78
|
+
def foo
|
|
79
|
+
return "ok"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
echo `foo` | tee out.txt
|
|
83
|
+
|
|
84
|
+
The example prints <tt>ok</tt> and writes <tt>ok\n</tt> to <tt>out.txt</tt>. Function arguments
|
|
85
|
+
are the command arguments. A returned string becomes command output; <tt>puts</tt> writes
|
|
86
|
+
directly to the command's output stream.
|
|
87
|
+
|
|
88
|
+
Ruby functions can also call shell commands:
|
|
89
|
+
|
|
90
|
+
def merp
|
|
91
|
+
ls
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
== Pipelines
|
|
95
|
+
|
|
96
|
+
Commands are parsed into argv arrays, so quotes and escaped spaces remain one argument.
|
|
97
|
+
The pipe operator connects command stages, including Ruby functions and external programs.
|
|
98
|
+
Backticks run a nested Rubsh command and substitute its captured output, with a trailing
|
|
99
|
+
newline removed.
|
|
100
|
+
|
|
101
|
+
Rubsh intentionally does not implement job control, background jobs, heredocs, redirects,
|
|
102
|
+
or <tt>&&</tt>/<tt>||</tt> conditionals. Stateful commands such as <tt>cd</tt> run in the
|
|
103
|
+
shell process so directory changes persist.
|
data/Rakefile
CHANGED
data/bin/rubsh
CHANGED
|
@@ -7,12 +7,14 @@ require 'optparse'
|
|
|
7
7
|
$DEBUG = false
|
|
8
8
|
|
|
9
9
|
def parse_options
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
options = {}
|
|
11
|
+
parser = OptionParser.new
|
|
12
|
+
parser.on('-c COMMAND', '--command COMMAND', 'Run one command and exit') { |command| options[:command] = command }
|
|
13
|
+
parser.on('--debug', 'If included run in DEBUG mode') { $DEBUG = true }
|
|
14
|
+
parser.banner = 'rubsh'
|
|
15
|
+
parser.parse!(ARGV)
|
|
16
|
+
options
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
options = parse_options
|
|
20
|
+
exit(Rubsh.new.run(options[:command]) || 0)
|
data/lib/alias.rb
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
class Alias
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
str.scan(/^\s*([^\s]+)(.*)$/) do |name,val|
|
|
5
|
-
@@aliases[name] = val
|
|
6
|
-
end
|
|
2
|
+
def initialize
|
|
3
|
+
@values = {}
|
|
7
4
|
end
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
|
|
6
|
+
def []=(name, value)
|
|
7
|
+
@values[name.to_s] = value.to_s
|
|
10
8
|
end
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
|
|
10
|
+
def [](name)
|
|
11
|
+
@values[name.to_s]
|
|
13
12
|
end
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
|
|
14
|
+
def delete(name)
|
|
15
|
+
@values.delete(name.to_s)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def empty?
|
|
19
|
+
@values.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
@values.dup
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def show(output = $stdout)
|
|
27
|
+
@values.each { |name, value| output.puts "alias #{name}=#{value}" }
|
|
18
28
|
end
|
|
19
29
|
end
|
data/lib/commands.rb
CHANGED
|
@@ -1,86 +1,84 @@
|
|
|
1
1
|
class Commands
|
|
2
2
|
@@oldpwd = nil
|
|
3
|
+
|
|
4
|
+
attr_writer :shell_executor, :shell_source_executor, :completion_executor
|
|
5
|
+
|
|
3
6
|
def get_binding
|
|
4
7
|
binding
|
|
5
8
|
end
|
|
6
9
|
|
|
7
|
-
def
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
%h - hostname
|
|
12
|
-
%u - the username of the current user
|
|
13
|
-
%w - the current working directory, with $HOME abbreviated with a tilde
|
|
14
|
-
%W - the basename of the current working directory, with $HOME abbreviated
|
|
15
|
-
with a tilde
|
|
16
|
-
%Cb - blue color
|
|
17
|
-
%Cc - cyan color
|
|
18
|
-
%Cg - green color
|
|
19
|
-
%CC - color reset
|
|
20
|
-
%t - the current time in 24-hour HH:MM:SS format
|
|
21
|
-
%% - literal %
|
|
22
|
-
%$ - if the effective UID is 0, a #, otherwise a $
|
|
23
|
-
|
|
24
|
-
Example: ENV['PR1'] = "[%u@%h]--(%t)\\n\\r[%w]%$ "
|
|
25
|
-
|
|
|
26
|
-
else
|
|
27
|
-
puts %|
|
|
28
|
-
ralias - alias command
|
|
29
|
-
example: ralias 'ls ls -Gh'
|
|
30
|
-
aliases ls to 'ls -Gh'
|
|
10
|
+
def method_missing(name, *args)
|
|
11
|
+
return super unless @shell_executor
|
|
12
|
+
@shell_executor.call(name.to_s, args)
|
|
13
|
+
end
|
|
31
14
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
15
|
+
def respond_to_missing?(_name, _include_private = false)
|
|
16
|
+
true
|
|
17
|
+
end
|
|
35
18
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
19
|
+
def complete(name, &block)
|
|
20
|
+
@completion_executor.call(name.to_s, &block)
|
|
21
|
+
end
|
|
39
22
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
23
|
+
def __rubsh_shell__(source)
|
|
24
|
+
@shell_source_executor.call(source)
|
|
25
|
+
end
|
|
43
26
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
27
|
+
def command?(name)
|
|
28
|
+
symbol = name.to_sym
|
|
29
|
+
singleton_methods.include?(symbol) ||
|
|
30
|
+
self.class.public_instance_methods(true).include?(symbol) ||
|
|
31
|
+
self.class.protected_instance_methods(true).include?(symbol) ||
|
|
32
|
+
self.class.private_instance_methods(true).include?(symbol)
|
|
33
|
+
end
|
|
48
34
|
|
|
49
|
-
|
|
35
|
+
def call(name, args, stdin: $stdin, stdout: $stdout, stderr: $stderr)
|
|
36
|
+
method = method(name.to_sym)
|
|
37
|
+
if method.parameters.any? { |_kind, key| key == :stdin }
|
|
38
|
+
method.call(args, stdin: stdin, stdout: stdout, stderr: stderr)
|
|
39
|
+
elsif method.arity == 0
|
|
40
|
+
method.call
|
|
41
|
+
else
|
|
42
|
+
method.call(*args)
|
|
50
43
|
end
|
|
51
44
|
end
|
|
52
|
-
def cd(dir)
|
|
53
|
-
dir.gsub! /\s*cd\s*/, ''
|
|
54
|
-
dir.gsub! /\s*$/, ''
|
|
55
|
-
dir.gsub! /("|')/, ''
|
|
56
|
-
if dir.empty?
|
|
57
|
-
@@oldpwd = Dir.pwd
|
|
58
|
-
Dir.chdir ENV['HOME']
|
|
59
|
-
return
|
|
60
|
-
end
|
|
61
45
|
|
|
46
|
+
def help(*_args)
|
|
47
|
+
puts <<~HELP
|
|
48
|
+
Rubsh runs Ruby expressions and external commands.
|
|
49
|
+
Define commands with a multiline Ruby function:
|
|
50
|
+
|
|
51
|
+
def foo
|
|
52
|
+
return "ok"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Backticks capture a command, and | connects pipeline stages.
|
|
56
|
+
HELP
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def cd(args, stdin: $stdin, stdout: $stdout, stderr: $stderr)
|
|
60
|
+
dir = args.first
|
|
61
|
+
dir = ENV['HOME'] if dir.nil? || dir.empty?
|
|
62
62
|
if dir == '-'
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@@oldpwd = curdir
|
|
67
|
-
else
|
|
68
|
-
puts "OLDPWD not set"
|
|
63
|
+
unless @@oldpwd
|
|
64
|
+
stderr.puts 'OLDPWD not set'
|
|
65
|
+
return 1
|
|
69
66
|
end
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
dir.gsub!('~',ENV['HOME'])
|
|
67
|
+
dir, @@oldpwd = @@oldpwd, Dir.pwd
|
|
68
|
+
else
|
|
69
|
+
dir = File.expand_path(dir, ENV['HOME']) if dir.start_with?('~/') || dir == '~'
|
|
70
|
+
@@oldpwd = Dir.pwd
|
|
75
71
|
end
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
Dir.chdir(dir)
|
|
73
|
+
0
|
|
74
|
+
rescue SystemCallError => error
|
|
75
|
+
stderr.puts error.message
|
|
76
|
+
1
|
|
78
77
|
end
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
raise "rubsh: #{m}: command not found"
|
|
78
|
+
|
|
79
|
+
def history(*_args)
|
|
80
|
+
file = File.join(ENV['HOME'], '.rubsh', 'history')
|
|
81
|
+
return unless File.exist?(file)
|
|
82
|
+
File.foreach(file) { |line| puts line }
|
|
85
83
|
end
|
|
86
84
|
end
|
data/lib/completion.rb
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
class CompletionNode
|
|
2
|
+
attr_reader :options, :subcommands, :arguments
|
|
3
|
+
attr_accessor :description, :wrapped_command
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@options = {}
|
|
7
|
+
@subcommands = {}
|
|
8
|
+
@arguments = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def option(name, description: nil, argument: nil)
|
|
12
|
+
@options[name.to_s] = { description: description, argument: argument }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def argument(name = nil, complete: nil)
|
|
16
|
+
@arguments << { name: name, complete: complete }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def subcommand(name, description: nil, &block)
|
|
20
|
+
child = (@subcommands[name.to_s] ||= CompletionNode.new)
|
|
21
|
+
child.description = description if description
|
|
22
|
+
child.instance_eval(&block) if block
|
|
23
|
+
child
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def wraps(command)
|
|
27
|
+
@wrapped_command = command.to_s
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class CompletionRegistry
|
|
32
|
+
def initialize
|
|
33
|
+
@commands = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def complete(name, &block)
|
|
37
|
+
node = (@commands[name.to_s] ||= CompletionNode.new)
|
|
38
|
+
node.instance_eval(&block) if block
|
|
39
|
+
node
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def erase(name)
|
|
43
|
+
@commands.delete(name.to_s)
|
|
44
|
+
end
|
|
45
|
+
def import_fish(text)
|
|
46
|
+
text.each_line do |line|
|
|
47
|
+
next unless line =~ /\Acomplete(?:\s+--no-files)?\s+(\S+)(.*)\z/
|
|
48
|
+
command, rest = Regexp.last_match(1), Regexp.last_match(2)
|
|
49
|
+
complete(command) do
|
|
50
|
+
if rest =~ /\s+-s\s+(\S+)/
|
|
51
|
+
option("-#{Regexp.last_match(1)}", description: rest[/\s+-d\s+'([^']+)'/, 1])
|
|
52
|
+
elsif rest =~ /\s+-a\s+'([^']+)'/
|
|
53
|
+
Regexp.last_match(1).split.each { |value| subcommand(value) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def [](name)
|
|
61
|
+
@commands[name.to_s]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def suggestions(argv, prefix)
|
|
65
|
+
node = @commands[argv.first]
|
|
66
|
+
node = @commands[node.wrapped_command] if node && node.wrapped_command
|
|
67
|
+
return [] unless node
|
|
68
|
+
argv.drop(1).each { |word| node = node.subcommands[word] if node.subcommands.key?(word) }
|
|
69
|
+
(node.subcommands.keys + node.options.keys).grep(/^#{Regexp.escape(prefix)}/)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def show(name = nil)
|
|
73
|
+
return @commands.keys.sort unless name
|
|
74
|
+
node = @commands[name.to_s]
|
|
75
|
+
node ? ruby_definition(name.to_s, node) : []
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def ruby_definition(name, node, indent = 0)
|
|
81
|
+
pad = ' ' * indent
|
|
82
|
+
lines = ["#{pad}complete #{name.inspect} do"]
|
|
83
|
+
node.options.each do |option, details|
|
|
84
|
+
args = [option.inspect]
|
|
85
|
+
args << "description: #{details[:description].inspect}" if details[:description]
|
|
86
|
+
args << "argument: #{details[:argument].inspect}" if details[:argument]
|
|
87
|
+
lines << "#{pad} option #{args.join(', ')}"
|
|
88
|
+
end
|
|
89
|
+
node.arguments.each { |arg| lines << "#{pad} argument #{arg[:name].inspect}" }
|
|
90
|
+
node.subcommands.each do |subcommand, child|
|
|
91
|
+
description = child.description ? ", description: #{child.description.inspect}" : ''
|
|
92
|
+
lines.concat(ruby_subcommand(subcommand, child, indent + 1, description))
|
|
93
|
+
end
|
|
94
|
+
lines << "#{pad}end"
|
|
95
|
+
lines.join("\n")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def ruby_subcommand(name, node, indent, description)
|
|
99
|
+
pad = ' ' * indent
|
|
100
|
+
lines = ["#{pad}subcommand #{name.inspect}#{description} do"]
|
|
101
|
+
node.options.each do |option, details|
|
|
102
|
+
args = [option.inspect]
|
|
103
|
+
args << "description: #{details[:description].inspect}" if details[:description]
|
|
104
|
+
args << "argument: #{details[:argument].inspect}" if details[:argument]
|
|
105
|
+
lines << "#{pad} option #{args.join(', ')}"
|
|
106
|
+
end
|
|
107
|
+
node.subcommands.each { |child_name, child| lines.concat(ruby_subcommand(child_name, child, indent + 1, child.description ? ", description: #{child.description.inspect}" : '')) }
|
|
108
|
+
lines << "#{pad}end"
|
|
109
|
+
lines
|
|
110
|
+
end
|
|
111
|
+
end
|
data/lib/rub_readline.rb
CHANGED
|
@@ -1,10 +1,56 @@
|
|
|
1
|
-
require '
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
require 'reline'
|
|
2
|
+
def get_local_dirs(str)
|
|
3
|
+
Dir[str+'*'].
|
|
4
|
+
grep( /^#{Regexp.escape(str)}/ ). #only return dirs which start with str
|
|
5
|
+
map { |f| f =~ /\s/ ? "\"#{f}\"" : f } # if names have spaces, then quote
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# tab completion here is bad because we don't know if the str has a 'cd' before it.
|
|
9
|
+
# That makes us go through the executables as well when we're doing cd str[TAB]
|
|
10
|
+
def get_executables(str)
|
|
11
|
+
commands = []
|
|
12
|
+
ENV["PATH"].split(':').each do |dir|
|
|
13
|
+
Dir.glob("#{dir}/*").grep( /#{File::SEPARATOR}?#{Regexp.escape(str)}/ ).each do |file|
|
|
14
|
+
begin
|
|
15
|
+
stat = File.stat(file)
|
|
16
|
+
commands << File::basename(file) if stat.executable? and not stat.directory?
|
|
17
|
+
rescue
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
return commands
|
|
22
|
+
end
|
|
23
|
+
Reline.autocompletion = false if Reline.respond_to?(:autocompletion=)
|
|
24
|
+
Reline.completion_proc = Proc.new do |str|
|
|
25
|
+
|
|
26
|
+
line = Reline.line_buffer
|
|
27
|
+
str = str.to_s
|
|
28
|
+
|
|
29
|
+
custom = []
|
|
30
|
+
registered = false
|
|
31
|
+
if defined?($rubsh_completion_registry) && defined?(ShellParser)
|
|
32
|
+
begin
|
|
33
|
+
if line =~ /\A\s*([^\s|]+)\s*\z/ && !line.end_with?(' ')
|
|
34
|
+
command = Regexp.last_match(1)
|
|
35
|
+
command = $rubsh_aliases[command].split.first if defined?($rubsh_aliases) && $rubsh_aliases[command]
|
|
36
|
+
custom = $rubsh_completion_registry.suggestions([command], '')
|
|
37
|
+
registered = !$rubsh_completion_registry[command].nil?
|
|
38
|
+
|
|
39
|
+
else
|
|
40
|
+
before = str.empty? ? line : line[0...-str.length]
|
|
41
|
+
words = ShellParser.parse(before.empty? ? 'x' : before).stages.first.words.map(&:value)
|
|
42
|
+
command = words.first
|
|
43
|
+
command = $rubsh_aliases[command].split.first if defined?($rubsh_aliases) && $rubsh_aliases[command]
|
|
44
|
+
words[0] = command
|
|
45
|
+
custom = $rubsh_completion_registry.suggestions(words, str)
|
|
46
|
+
registered = !$rubsh_completion_registry[command].nil?
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
rescue SyntaxError
|
|
50
|
+
custom = []
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
registered ? custom.uniq : (custom + get_local_dirs(str) + get_executables(str)).uniq
|
|
8
54
|
end
|
|
9
55
|
|
|
10
56
|
def commands_in_path
|
|
@@ -20,3 +66,7 @@ def commands_in_path
|
|
|
20
66
|
end
|
|
21
67
|
return commands
|
|
22
68
|
end
|
|
69
|
+
COMMANDS_IN_PATH = commands_in_path.freeze
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Reline's output modifier also receives subprocess output. Do not rewrite it as input.
|