Class: Rush::Shell

Inherits:
Object
  • Object
show all
Defined in:
lib/rush/shell.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeShell

Set up the user’s environment, including a pure binding into which env.rb and commands.rb are mixed.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/rush/shell.rb', line 9

def initialize
	root = Rush::Dir.new('/')
	home = Rush::Dir.new(ENV['HOME']) if ENV['HOME']
	pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD']

	@config = Rush::Config.new

	@config.load_history.each do |item|
		Readline::HISTORY.push(item)
	end

	Readline.basic_word_break_characters = ""
	Readline.completion_append_character = nil
	Readline.completion_proc = completion_proc

	@box = Rush::Box.new
	@pure_binding = @box.instance_eval "binding"
	$last_res = nil

	eval @config.load_env, @pure_binding

	commands = @config.load_commands
	Rush::Dir.class_eval commands
	Array.class_eval commands
end

Instance Attribute Details

#suppress_outputObject

Returns the value of attribute suppress_output.



6
7
8
# File 'lib/rush/shell.rb', line 6

def suppress_output
  @suppress_output
end

Instance Method Details

#complete_method(receiver, dot, partial_name, pre) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rush/shell.rb', line 122

def complete_method(receiver, dot, partial_name, pre)
	path = eval("#{receiver}.full_path", @pure_binding) rescue nil
	box = eval("#{receiver}.box", @pure_binding) rescue nil
	if path and box
		(box[path].methods - Object.methods).select do |e|
			e.match(/^#{Regexp.escape(partial_name)}/)
		end.map do |e|
			(pre || '') + receiver + dot + e
		end
	end
end

#complete_path(possible_var, accessor, quote, partial_path, pre) ⇒ Object

:nodoc:



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/rush/shell.rb', line 134

def complete_path(possible_var, accessor, quote, partial_path, pre)		# :nodoc:
	original_var, fixed_path = possible_var, ''
	if /^(.+\/)([^\/]*)$/ === partial_path
		fixed_path, partial_path = $~.captures
		possible_var += "['#{fixed_path}']"
	end
	full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil
	box = eval("#{possible_var}.box", @pure_binding) rescue nil
	if full_path and box
		Rush::Dir.new(full_path, box).entries.select do |e|
			e.name.match(/^#{Regexp.escape(partial_path)}/)
		end.map do |e|
			(pre || '') + original_var + accessor + quote + fixed_path + e.name + (e.dir? ? "/" : "")
		end
	end
end

#complete_variable(partial_name, pre) ⇒ Object



151
152
153
154
155
156
157
158
159
160
# File 'lib/rush/shell.rb', line 151

def complete_variable(partial_name, pre)
	lvars = eval('local_variables', @pure_binding)
	gvars = eval('global_variables', @pure_binding)
	ivars = eval('instance_variables', @pure_binding)
	(lvars + gvars + ivars).select do |e|
		e.match(/^#{Regexp.escape(partial_name)}/)
	end.map do |e|
		(pre || '') + e
	end
end

#completion_procObject

Try to do tab completion on dir square brackets and slash accessors.

Example:

dir[‘subd # presing tab here will produce dir[’subdir/ if subdir exists dir/‘subd # presing tab here will produce dir/’subdir/ if subdir exists

This isn’t that cool yet, because it can’t do multiple levels of subdirs. It does work remotely, though, which is pretty sweet.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/rush/shell.rb', line 171

def completion_proc
	proc do |input|
		receiver, accessor, *rest = path_parts(input)
		if receiver
			case accessor
			when /^[\[\/]$/
				complete_path(receiver, accessor, *rest)
			when /^\.$/
				complete_method(receiver, accessor, *rest)
			when nil
				complete_variable(receiver, *rest)
			end
		end
	end
end

#execute(cmd) ⇒ Object

Run a single command.



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/rush/shell.rb', line 36

def execute(cmd)
	res = eval(cmd, @pure_binding)
	$last_res = res
	eval("_ = $last_res", @pure_binding)
	print_result res
rescue Rush::Exception => e
	puts "Exception #{e.class} -> #{e.message}"
rescue ::Exception => e
	puts "Exception #{e.class} -> #{e.message}"
	e.backtrace.each do |t|
		puts "   #{::File.expand_path(t)}"
	end
end

#finishObject

Save history to ~/.rush/history when the shell exists.



64
65
66
67
68
# File 'lib/rush/shell.rb', line 64

def finish
	@config.save_history(Readline::HISTORY.to_a)
	puts
	exit
end

#path_parts(input) ⇒ Object

:nodoc:



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/rush/shell.rb', line 109

def path_parts(input)		# :nodoc:
	case input
	when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/
		$~.to_a.slice(1, 4).push($~.pre_match)
	when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)(\.)(\w*)$/
		$~.to_a.slice(1, 3).push($~.pre_match)
	when /((?:@{1,2}|\$|)\w+)$/
		$~.to_a.slice(1, 1).push(nil).push($~.pre_match)
	else
		[ nil, nil, nil ]
	end
end

Nice printing of different return types, particularly Rush::SearchResults.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/rush/shell.rb', line 71

def print_result(res)
	return if self.suppress_output
	if res.kind_of? String
		puts res
	elsif res.kind_of? Rush::SearchResults
		widest = res.entries.map { |k| k.full_path.length }.max
		res.entries_with_lines.each do |entry, lines|
			print entry.full_path
			print ' ' * (widest - entry.full_path.length + 2)
			print "=> "
			print res.colorize(lines.first.strip.head(30))
			print "..." if lines.first.strip.length > 30
			if lines.size > 1
				print " (plus #{lines.size - 1} more matches)"
			end
			print "\n"
		end
		puts "#{res.entries.size} matching files with #{res.lines.size} matching lines"
	elsif res.respond_to? :each
		counts = {}
		res.each do |item|
			puts item
			counts[item.class] ||= 0
			counts[item.class] += 1
		end
		if counts == {}
			puts "=> (empty set)"
		else
			count_s = counts.map do |klass, count|
				"#{count} x #{klass}"
			end.join(', ')
			puts "=> #{count_s}"
		end
	else
		puts "=> #{res.inspect}"
	end
end

#runObject

Run the interactive shell using readline.



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rush/shell.rb', line 51

def run
	loop do
		cmd = Readline.readline('rush> ')

		finish if cmd.nil? or cmd == 'exit'
		next if cmd == ""
		Readline::HISTORY.push(cmd)

		execute(cmd)
	end
end