yap-shell 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -24
- data/Gemfile +1 -5
- data/LICENSE.txt +17 -18
- data/README.md +28 -14
- data/Rakefile +4 -1
- data/bin/yap +1 -3
- data/lib/.gitkeep +0 -0
- data/yap-shell.gemspec +12 -11
- metadata +19 -184
- data/.rspec +0 -2
- data/.travis.yml +0 -11
- data/DESIGN.md +0 -87
- data/Gemfile.travis +0 -8
- data/Gemfile.travis.lock +0 -104
- data/WISHLIST.md +0 -54
- data/bin/yap-dev +0 -45
- data/lib/tasks/gem.rake +0 -62
- data/lib/yap.rb +0 -52
- data/lib/yap/addon.rb +0 -24
- data/lib/yap/addon/base.rb +0 -52
- data/lib/yap/addon/export_as.rb +0 -12
- data/lib/yap/addon/loader.rb +0 -84
- data/lib/yap/addon/path.rb +0 -56
- data/lib/yap/addon/rc_file.rb +0 -21
- data/lib/yap/addon/reference.rb +0 -22
- data/lib/yap/cli.rb +0 -4
- data/lib/yap/cli/commands.rb +0 -6
- data/lib/yap/cli/commands/addon.rb +0 -14
- data/lib/yap/cli/commands/addon/disable.rb +0 -35
- data/lib/yap/cli/commands/addon/enable.rb +0 -35
- data/lib/yap/cli/commands/addon/list.rb +0 -37
- data/lib/yap/cli/commands/addon/search.rb +0 -99
- data/lib/yap/cli/commands/generate.rb +0 -13
- data/lib/yap/cli/commands/generate/addon.rb +0 -258
- data/lib/yap/cli/commands/generate/addonrb.template +0 -22
- data/lib/yap/cli/commands/generate/gemspec.template +0 -25
- data/lib/yap/cli/commands/generate/license.template +0 -21
- data/lib/yap/cli/commands/generate/rakefile.template +0 -6
- data/lib/yap/cli/commands/generate/readme.template +0 -40
- data/lib/yap/cli/options.rb +0 -162
- data/lib/yap/cli/options/addon.rb +0 -64
- data/lib/yap/cli/options/addon/disable.rb +0 -62
- data/lib/yap/cli/options/addon/enable.rb +0 -63
- data/lib/yap/cli/options/addon/list.rb +0 -65
- data/lib/yap/cli/options/addon/search.rb +0 -76
- data/lib/yap/cli/options/generate.rb +0 -59
- data/lib/yap/cli/options/generate/addon.rb +0 -63
- data/lib/yap/configuration.rb +0 -74
- data/lib/yap/gem_helper.rb +0 -195
- data/lib/yap/gem_tasks.rb +0 -6
- data/lib/yap/shell.rb +0 -116
- data/lib/yap/shell/aliases.rb +0 -58
- data/lib/yap/shell/builtins.rb +0 -18
- data/lib/yap/shell/builtins/alias.rb +0 -42
- data/lib/yap/shell/builtins/cd.rb +0 -57
- data/lib/yap/shell/builtins/env.rb +0 -11
- data/lib/yap/shell/commands.rb +0 -163
- data/lib/yap/shell/evaluation.rb +0 -439
- data/lib/yap/shell/evaluation/shell_expansions.rb +0 -99
- data/lib/yap/shell/event_emitter.rb +0 -18
- data/lib/yap/shell/execution.rb +0 -16
- data/lib/yap/shell/execution/builtin_command_execution.rb +0 -20
- data/lib/yap/shell/execution/command_execution.rb +0 -30
- data/lib/yap/shell/execution/context.rb +0 -128
- data/lib/yap/shell/execution/file_system_command_execution.rb +0 -137
- data/lib/yap/shell/execution/result.rb +0 -18
- data/lib/yap/shell/execution/ruby_command_execution.rb +0 -80
- data/lib/yap/shell/execution/shell_command_execution.rb +0 -30
- data/lib/yap/shell/prompt.rb +0 -21
- data/lib/yap/shell/repl.rb +0 -237
- data/lib/yap/shell/version.rb +0 -5
- data/lib/yap/world.rb +0 -286
- data/rcfiles/yaprc +0 -390
- data/scripts/4 +0 -8
- data/scripts/bg-vim +0 -4
- data/scripts/fail +0 -3
- data/scripts/letters +0 -8
- data/scripts/lots-of-output +0 -6
- data/scripts/pass +0 -3
- data/scripts/simulate-long-running +0 -4
- data/scripts/write-to-stderr.rb +0 -3
- data/scripts/write-to-stdout.rb +0 -3
- data/spec/features/addons/generating_an_addon_spec.rb +0 -55
- data/spec/features/addons/using_an_addon_spec.rb +0 -182
- data/spec/features/aliases_spec.rb +0 -78
- data/spec/features/environment_variables_spec.rb +0 -69
- data/spec/features/filesystem_commands_spec.rb +0 -61
- data/spec/features/first_time_spec.rb +0 -45
- data/spec/features/grouping_spec.rb +0 -81
- data/spec/features/line_editing_spec.rb +0 -174
- data/spec/features/range_spec.rb +0 -35
- data/spec/features/redirection_spec.rb +0 -234
- data/spec/features/repetition_spec.rb +0 -118
- data/spec/features/shell_expansions_spec.rb +0 -127
- data/spec/spec_helper.rb +0 -172
- data/spec/support/matchers/have_not_printed.rb +0 -30
- data/spec/support/matchers/have_printed.rb +0 -68
- data/spec/support/very_soon.rb +0 -9
- data/spec/support/yap_spec_dsl.rb +0 -258
- data/test.rb +0 -206
- data/update-rawline.sh +0 -6
@@ -1,18 +0,0 @@
|
|
1
|
-
module Yap::Shell::Execution
|
2
|
-
class Result
|
3
|
-
attr_reader :status_code, :directory, :n, :of
|
4
|
-
|
5
|
-
def initialize(status_code:, directory:, n:, of:)
|
6
|
-
@status_code = status_code
|
7
|
-
@directory = directory
|
8
|
-
@n = n
|
9
|
-
@of = of
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class SuspendExecution < Result
|
14
|
-
end
|
15
|
-
|
16
|
-
class ResumeExecution < Result
|
17
|
-
end
|
18
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
module Yap::Shell::Execution
|
2
|
-
class RubyCommandExecution < CommandExecution
|
3
|
-
on_execute do |command:, n:, of:, wait:|
|
4
|
-
result = nil
|
5
|
-
stdin, stdout, stderr, world = @stdin, @stdout, @stderr, @world
|
6
|
-
|
7
|
-
exit_code = 0
|
8
|
-
first_command = n == 1
|
9
|
-
|
10
|
-
f = nil
|
11
|
-
ruby_result = nil
|
12
|
-
begin
|
13
|
-
ruby_command = command.to_executable_str
|
14
|
-
|
15
|
-
Treefell['shell'].puts "ruby execution: reading stdin from #{stdin.inspect}"
|
16
|
-
contents = if stdin.is_a?(String)
|
17
|
-
f = File.open stdin
|
18
|
-
f.read
|
19
|
-
elsif stdin != $stdin
|
20
|
-
stdin.read
|
21
|
-
end
|
22
|
-
|
23
|
-
Treefell['shell'].puts "ruby execution: contents=#{contents.inspect}, setting to world.content"
|
24
|
-
world.contents = contents
|
25
|
-
|
26
|
-
method = ruby_command.scan(/^(\w+(?:[!?]|\s*=)?)/).flatten.first.gsub(/\s/, '')
|
27
|
-
Treefell['shell'].puts "ruby execution: method=#{method.inspect}"
|
28
|
-
|
29
|
-
obj = if first_command
|
30
|
-
world
|
31
|
-
elsif contents.respond_to?(method)
|
32
|
-
contents
|
33
|
-
else
|
34
|
-
world
|
35
|
-
end
|
36
|
-
|
37
|
-
if ruby_command =~ /^[A-Z0-9]|::/
|
38
|
-
Treefell['shell'].puts "ruby executing: eval(#{ruby_command.inspect})"
|
39
|
-
ruby_result = eval ruby_command
|
40
|
-
else
|
41
|
-
ruby_command = "self.#{ruby_command}"
|
42
|
-
Treefell['shell'].puts "ruby executing: #{obj.class.name} instance instance_eval(#{ruby_command.inspect})"
|
43
|
-
ruby_result = obj.instance_eval ruby_command
|
44
|
-
end
|
45
|
-
rescue Exception => ex
|
46
|
-
ruby_result = <<-EOT.gsub(/^\s*\S/, '')
|
47
|
-
|Failed processing ruby: #{ruby_command}
|
48
|
-
|#{ex}
|
49
|
-
|#{ex.backtrace.join("\n")}
|
50
|
-
EOT
|
51
|
-
exit_code = 1
|
52
|
-
ensure
|
53
|
-
f.close if f && !f.closed?
|
54
|
-
end
|
55
|
-
|
56
|
-
# The next line causes issues sometimes?
|
57
|
-
# puts "WRITING #{ruby_result.length} bytes" if ENV["DEBUG"]
|
58
|
-
ruby_result = ruby_result.to_s
|
59
|
-
ruby_result << "\n" unless ruby_result.end_with?("\n")
|
60
|
-
|
61
|
-
stdout.write ruby_result
|
62
|
-
stdout.flush
|
63
|
-
stderr.flush
|
64
|
-
|
65
|
-
stdout.close if stdout != $stdout && !stdout.closed?
|
66
|
-
stderr.close if stderr != $stderr && !stderr.closed?
|
67
|
-
|
68
|
-
# Pass current execution to give any other threads a chance
|
69
|
-
# to be scheduled before we send back our status code. This could
|
70
|
-
# probably use a more elaborate signal or message passing scheme,
|
71
|
-
# but that's for another day.
|
72
|
-
# Thread.pass
|
73
|
-
|
74
|
-
# Make up an exit code
|
75
|
-
Result.new(status_code:exit_code, directory:Dir.pwd, n:n, of:of).tap do |result|
|
76
|
-
Treefell['shell'].puts "ruby execution done with result=#{result.inspect}"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Yap::Shell::Execution
|
2
|
-
class ShellCommandExecution < CommandExecution
|
3
|
-
on_execute do |command:, n:, of:, wait:|
|
4
|
-
Treefell['shell'].puts "shell command execution: #{command}"
|
5
|
-
|
6
|
-
possible_parameters = {
|
7
|
-
command: command.str,
|
8
|
-
args: command.args,
|
9
|
-
stdin: (@stdin != $stdin ? @stdin : StringIO.new),
|
10
|
-
stdout: @stdout,
|
11
|
-
stderr: @stderr,
|
12
|
-
world: @world,
|
13
|
-
line: command.line
|
14
|
-
}
|
15
|
-
|
16
|
-
func = command.to_proc
|
17
|
-
params = func.parameters.reduce({}) do |h, (type, name)|
|
18
|
-
h[name] = possible_parameters[name]
|
19
|
-
h
|
20
|
-
end
|
21
|
-
|
22
|
-
Treefell['shell'].puts "shell command executing with params: #{params.inspect} $stdout=#{$stdout.inspect} $stderr=#{$stderr.inspect}"
|
23
|
-
func.call(**params)
|
24
|
-
|
25
|
-
@stdout.close if @stdout != $stdout && !@stdout.closed?
|
26
|
-
@stderr.close if @stderr != $stderr && !@stderr.closed?
|
27
|
-
@stdin.close if @stdin != $stdin && !@stdin.closed?
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/yap/shell/prompt.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Yap::Shell
|
2
|
-
class Prompt
|
3
|
-
attr_reader :text
|
4
|
-
|
5
|
-
def initialize(text:, &blk)
|
6
|
-
@text = text
|
7
|
-
@blk = blk
|
8
|
-
end
|
9
|
-
|
10
|
-
def text=(text)
|
11
|
-
@text = text
|
12
|
-
end
|
13
|
-
|
14
|
-
def update
|
15
|
-
if @blk
|
16
|
-
@text = @blk.call
|
17
|
-
end
|
18
|
-
self
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/yap/shell/repl.rb
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
require 'shellwords'
|
2
|
-
require 'term/ansicolor'
|
3
|
-
|
4
|
-
module Yap::Shell
|
5
|
-
module Color
|
6
|
-
extend Term::ANSIColor
|
7
|
-
end
|
8
|
-
|
9
|
-
class Repl
|
10
|
-
attr_reader :editor
|
11
|
-
|
12
|
-
def initialize(world:nil)
|
13
|
-
@world = world
|
14
|
-
@editor= world.editor
|
15
|
-
|
16
|
-
Treefell['shell'].puts "installing default keybindings"
|
17
|
-
install_default_keybindings
|
18
|
-
|
19
|
-
Treefell['shell'].puts "installing default tab completion"
|
20
|
-
install_default_tab_completion_proc
|
21
|
-
end
|
22
|
-
|
23
|
-
def on_input(&blk)
|
24
|
-
@blk = blk
|
25
|
-
|
26
|
-
@world.editor.on_read_line do |event|
|
27
|
-
line_read = event[:payload][:line]
|
28
|
-
Treefell['shell'].puts "editor line read: #{line_read.inspect}"
|
29
|
-
# editor.history = true?
|
30
|
-
line = line_read << "\n"
|
31
|
-
begin
|
32
|
-
@blk.call(line)
|
33
|
-
rescue Yap::Shell::Parser::Lexer::NonterminatedString,
|
34
|
-
Yap::Shell::Parser::Lexer::LineContinuationFound => ex
|
35
|
-
Treefell['shell'].puts "rescued #{ex}, asking user for more input"
|
36
|
-
more_input = read_another_line_of_input
|
37
|
-
if more_input
|
38
|
-
line << more_input
|
39
|
-
retry
|
40
|
-
end
|
41
|
-
rescue ::Yap::Shell::Parser::ParseError => ex
|
42
|
-
Treefell['shell'].puts "rescued #{ex}, telling user"
|
43
|
-
puts " Parse error: #{ex.message}"
|
44
|
-
end
|
45
|
-
|
46
|
-
ensure_process_group_controls_the_tty
|
47
|
-
@world.refresh_prompt
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def read_another_line_of_input
|
54
|
-
print @world.secondary_prompt.update.text
|
55
|
-
gets
|
56
|
-
end
|
57
|
-
|
58
|
-
def kill_ring
|
59
|
-
@kill_ring ||= []
|
60
|
-
end
|
61
|
-
|
62
|
-
def install_default_keybindings
|
63
|
-
editor.terminal.keys.merge!(enter: [13])
|
64
|
-
editor.bind(:return){ editor.newline }
|
65
|
-
|
66
|
-
# Move to beginning of line
|
67
|
-
editor.bind(:ctrl_a) { editor.move_to_beginning_of_input }
|
68
|
-
|
69
|
-
# Move to end of line
|
70
|
-
editor.bind(:ctrl_e) { editor.move_to_end_of_input }
|
71
|
-
|
72
|
-
# Move backward one word at a time
|
73
|
-
editor.bind(:ctrl_b) {
|
74
|
-
text = editor.line.text[0...editor.line.position].reverse
|
75
|
-
position = text.index(/\s+/, 1)
|
76
|
-
position = position ? (text.length - position) : 0
|
77
|
-
editor.move_to_position position
|
78
|
-
}
|
79
|
-
|
80
|
-
# Move forward one word at a time
|
81
|
-
editor.bind(:ctrl_f) {
|
82
|
-
text = editor.line.text
|
83
|
-
position = text.index(/\s+/, editor.line.position)
|
84
|
-
position = position ? (position + 1) : text.length
|
85
|
-
editor.move_to_position position
|
86
|
-
}
|
87
|
-
|
88
|
-
# Yank text from the kill ring and insert it at the cursor position
|
89
|
-
editor.bind(:ctrl_y){
|
90
|
-
text = kill_ring[-1]
|
91
|
-
if text
|
92
|
-
editor.yank_forward text.without_ansi
|
93
|
-
end
|
94
|
-
}
|
95
|
-
|
96
|
-
# Backwards delete one word
|
97
|
-
editor.bind(:ctrl_w){
|
98
|
-
before_text = editor.line.text[0...editor.line.position]
|
99
|
-
after_text = editor.line.text[editor.line.position..-1]
|
100
|
-
|
101
|
-
have_only_seen_whitespace = true
|
102
|
-
position = 0
|
103
|
-
|
104
|
-
before_text.reverse.each_char.with_index do |ch, i|
|
105
|
-
if ch =~ /\s/ && !have_only_seen_whitespace
|
106
|
-
position = before_text.length - i
|
107
|
-
break
|
108
|
-
else
|
109
|
-
have_only_seen_whitespace = false
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
killed_text = before_text[position...editor.line.position]
|
114
|
-
kill_ring.push killed_text
|
115
|
-
|
116
|
-
text = [before_text.slice(0, position), after_text].join
|
117
|
-
editor.overwrite_line text
|
118
|
-
editor.move_to_position position
|
119
|
-
}
|
120
|
-
|
121
|
-
# History forward, but if at the end of the history then give user a
|
122
|
-
# blank line rather than remain on the last command
|
123
|
-
editor.bind(:down_arrow) {
|
124
|
-
if editor.history.searching? && !editor.history.end?
|
125
|
-
editor.history_forward
|
126
|
-
else
|
127
|
-
editor.overwrite_line ""
|
128
|
-
end
|
129
|
-
}
|
130
|
-
|
131
|
-
editor.bind(:enter) { editor.newline }
|
132
|
-
editor.bind(:tab) { editor.complete }
|
133
|
-
editor.bind(:backspace) { editor.delete_left_character }
|
134
|
-
|
135
|
-
# Delete to end of line from cursor position
|
136
|
-
editor.bind(:ctrl_k) {
|
137
|
-
kill_ring.push editor.kill_forward
|
138
|
-
}
|
139
|
-
|
140
|
-
# Delete to beginning of line from cursor position
|
141
|
-
editor.bind(:ctrl_u) {
|
142
|
-
kill_ring.push editor.line.text[0...editor.line.position]
|
143
|
-
editor.overwrite_line editor.line.text[editor.line.position..-1]
|
144
|
-
editor.move_to_position 0
|
145
|
-
}
|
146
|
-
|
147
|
-
# Forward delete a character, leaving the cursor in place
|
148
|
-
editor.bind("\e[3~") {
|
149
|
-
before_text = editor.line.text[0...editor.line.position]
|
150
|
-
after_text = editor.line.text[(editor.line.position+1)..-1]
|
151
|
-
text = [before_text, after_text].join
|
152
|
-
position = editor.line.position
|
153
|
-
editor.overwrite_line text
|
154
|
-
editor.move_to_position position
|
155
|
-
}
|
156
|
-
|
157
|
-
editor.bind(:ctrl_l){
|
158
|
-
editor.clear_screen
|
159
|
-
}
|
160
|
-
|
161
|
-
editor.bind(:ctrl_r) {
|
162
|
-
$r = $r ? false : true
|
163
|
-
# editor.redo
|
164
|
-
}
|
165
|
-
editor.bind(:left_arrow) { editor.move_left }
|
166
|
-
editor.bind(:right_arrow) { editor.move_right }
|
167
|
-
editor.bind(:up_arrow) { editor.history_back }
|
168
|
-
editor.bind(:down_arrow) { editor.history_forward }
|
169
|
-
editor.bind(:delete) { editor.delete_character }
|
170
|
-
editor.bind(:insert) { editor.toggle_mode }
|
171
|
-
|
172
|
-
editor.bind(:ctrl_g) { editor.clear_history }
|
173
|
-
# editor.bind(:ctrl_l) { editor.debug_line }
|
174
|
-
editor.bind(:ctrl_h) { editor.show_history }
|
175
|
-
editor.bind(:ctrl_d) { puts; puts "Exiting..."; exit }
|
176
|
-
|
177
|
-
# character-search; wraps around as necessary
|
178
|
-
editor.bind(:ctrl_n) {
|
179
|
-
line = editor.line
|
180
|
-
text, start_position = line.text, line.position
|
181
|
-
i, new_position = start_position, nil
|
182
|
-
|
183
|
-
break_on_bytes = [editor.terminal.keys[:ctrl_c]].flatten
|
184
|
-
byte = [editor.read_character].flatten.first
|
185
|
-
|
186
|
-
unless break_on_bytes.include?(byte)
|
187
|
-
loop do
|
188
|
-
i += 1
|
189
|
-
i = 0 if i >= text.length # wrap-around to the beginning
|
190
|
-
break if i == start_position # back to where we started
|
191
|
-
(editor.move_to_position(i) ; break) if text[i] == byte.chr # found a match; move and break
|
192
|
-
end
|
193
|
-
end
|
194
|
-
}
|
195
|
-
end
|
196
|
-
|
197
|
-
def install_default_tab_completion_proc
|
198
|
-
editor.completion_proc = lambda do |word, line, word_index|
|
199
|
-
Dir["#{word}*"].map{ |str| str.gsub(/ /, '\ ')}
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
# This is to prevent the Errno::EIO error from occurring by ensuring that
|
204
|
-
# if we haven't been made the process group controlling the TTY that we
|
205
|
-
# become so. This method intentionally blocks.
|
206
|
-
def ensure_process_group_controls_the_tty
|
207
|
-
return unless STDIN.isatty
|
208
|
-
|
209
|
-
tty_pgrp = Termios.tcgetpgrp(STDIN)
|
210
|
-
while ![Process.pid, Process.getpgrp].include?(tty_pgrp)
|
211
|
-
Termios.tcsetpgrp(STDIN, Process.pid)
|
212
|
-
sleep 0.1
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def process_heredoc(_input)
|
217
|
-
if _input =~ /<<-?([A-z0-9\-]+)\s*$/
|
218
|
-
input = _input.dup
|
219
|
-
marker = $1
|
220
|
-
input << "\n"
|
221
|
-
else
|
222
|
-
return _input
|
223
|
-
end
|
224
|
-
|
225
|
-
Treefell['shell'].puts "asking for heredoc input with @world.secondary_prompt"
|
226
|
-
loop do
|
227
|
-
str = editor.read(@world.secondary_prompt.update.text, false)
|
228
|
-
input << "#{str}\n"
|
229
|
-
if str =~ /^#{Regexp.escape(marker)}$/
|
230
|
-
Treefell['shell'].puts "done asking for heredoc input"
|
231
|
-
break
|
232
|
-
end
|
233
|
-
end
|
234
|
-
input
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
data/lib/yap/shell/version.rb
DELETED
data/lib/yap/world.rb
DELETED
@@ -1,286 +0,0 @@
|
|
1
|
-
require 'term/ansicolor'
|
2
|
-
require 'fileutils'
|
3
|
-
require 'forwardable'
|
4
|
-
require 'rawline'
|
5
|
-
require 'termios'
|
6
|
-
|
7
|
-
module Yap
|
8
|
-
require 'yap/addon'
|
9
|
-
require 'yap/shell/execution'
|
10
|
-
require 'yap/shell/prompt'
|
11
|
-
|
12
|
-
class World
|
13
|
-
include Term::ANSIColor
|
14
|
-
extend Forwardable
|
15
|
-
|
16
|
-
DEFAULTS = {
|
17
|
-
primary_prompt_text: "yap> ",
|
18
|
-
secondary_prompt_text: "> "
|
19
|
-
}
|
20
|
-
|
21
|
-
attr_accessor :prompt, :secondary_prompt, :contents, :repl, :editor, :env
|
22
|
-
attr_reader :addons
|
23
|
-
|
24
|
-
attr_accessor :last_result
|
25
|
-
|
26
|
-
def self.instance(*args)
|
27
|
-
@instance ||= new(*args)
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(addons:)
|
31
|
-
@env = ENV.to_h.dup
|
32
|
-
dom = build_editor_dom
|
33
|
-
|
34
|
-
# ensure yap directory exists
|
35
|
-
if !File.exists?(configuration.yap_path)
|
36
|
-
if configuration.skip_first_time?
|
37
|
-
# skipping first time
|
38
|
-
else
|
39
|
-
puts
|
40
|
-
puts yellow("Yap directory not found: #{configuration.yap_path}")
|
41
|
-
puts
|
42
|
-
puts "Initializing yap for the first time:"
|
43
|
-
puts
|
44
|
-
|
45
|
-
print " Creating #{configuration.yap_path} "
|
46
|
-
FileUtils.mkdir_p configuration.yap_path
|
47
|
-
puts green("done")
|
48
|
-
|
49
|
-
print " Creating default #{configuration.preferred_yaprc_path} "
|
50
|
-
FileUtils.cp configuration.yaprc_template_path, configuration.yap_path
|
51
|
-
puts green("done")
|
52
|
-
puts
|
53
|
-
puts "To tweak yap take a look at #{configuration.preferred_yaprc_path}."
|
54
|
-
puts
|
55
|
-
puts "Reloading shell"
|
56
|
-
reload!
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
@editor = RawLine::Editor.create(dom: dom)
|
61
|
-
|
62
|
-
self.prompt = Yap::Shell::Prompt.new(text: DEFAULTS[:primary_prompt_text])
|
63
|
-
self.secondary_prompt = Yap::Shell::Prompt.new(text: DEFAULTS[:secondary_prompt_text])
|
64
|
-
|
65
|
-
@repl = Yap::Shell::Repl.new(world:self)
|
66
|
-
|
67
|
-
@addons_initialized = []
|
68
|
-
@addons = AddonHash.new(
|
69
|
-
self
|
70
|
-
)
|
71
|
-
|
72
|
-
addons.each do |addon|
|
73
|
-
if addon.yap_enabled?
|
74
|
-
@addons[addon.export_as] = addon
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
@addons.values.select(&:yap_enabled?).each do |addon|
|
79
|
-
initialize_addon(addon) unless addon_initialized?(addon)
|
80
|
-
addon
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def addon_initialized?(addon)
|
85
|
-
(@addons_initialized ||= []).include?(addon)
|
86
|
-
end
|
87
|
-
|
88
|
-
def initialize_addon(addon)
|
89
|
-
return unless addon
|
90
|
-
begin
|
91
|
-
addon.initialize_world(self)
|
92
|
-
(@addons_initialized ||= []) << addon
|
93
|
-
rescue Exception => ex
|
94
|
-
puts Term::ANSIColor.red(("The #{addon.addon_name} addon failed to initialize due to error:"))
|
95
|
-
puts ex.message
|
96
|
-
puts ex.backtrace[0..5]
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
class AddonHash < Hash
|
101
|
-
def initialize(world)
|
102
|
-
@world = world
|
103
|
-
end
|
104
|
-
|
105
|
-
def [](key)
|
106
|
-
addon = super
|
107
|
-
unless @world.addon_initialized?(addon)
|
108
|
-
@world.initialize_addon(addon)
|
109
|
-
end
|
110
|
-
addon
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def configuration
|
115
|
-
::Yap.configuration
|
116
|
-
end
|
117
|
-
|
118
|
-
def [](addon_name)
|
119
|
-
@addons.fetch(addon_name){ raise(ArgumentError, "No addon loaded registered as #{addon_name}") }
|
120
|
-
end
|
121
|
-
|
122
|
-
def aliases
|
123
|
-
Yap::Shell::Aliases.instance
|
124
|
-
end
|
125
|
-
|
126
|
-
def builtins
|
127
|
-
Yap::Shell::BuiltinCommand.builtins.keys.map(&:to_s)
|
128
|
-
end
|
129
|
-
|
130
|
-
def shell_commands
|
131
|
-
Yap::Shell::ShellCommand.registered_functions.keys.map(&:to_s)
|
132
|
-
end
|
133
|
-
|
134
|
-
def events
|
135
|
-
@editor.events
|
136
|
-
end
|
137
|
-
|
138
|
-
def bind(key, &blk)
|
139
|
-
@editor.bind(key) do
|
140
|
-
blk.call self
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def unbind(key)
|
145
|
-
@editor.unbind(key)
|
146
|
-
end
|
147
|
-
|
148
|
-
def reload!
|
149
|
-
exec configuration.yap_binpath
|
150
|
-
end
|
151
|
-
|
152
|
-
def func(name, &blk)
|
153
|
-
Yap::Shell::ShellCommand.define_shell_function(name, &blk)
|
154
|
-
end
|
155
|
-
|
156
|
-
def shell(statement)
|
157
|
-
context = Yap::Shell::Execution::Context.new(
|
158
|
-
stdin: $stdin,
|
159
|
-
stdout: $stdout,
|
160
|
-
stderr: $stderr
|
161
|
-
)
|
162
|
-
if statement.nil?
|
163
|
-
@last_result = Yap::Shell::Execution::Result.new(
|
164
|
-
status_code: 1,
|
165
|
-
directory: Dir.pwd,
|
166
|
-
n: 1,
|
167
|
-
of: 1
|
168
|
-
)
|
169
|
-
else
|
170
|
-
evaluation = Yap::Shell::Evaluation.new(stdin:$stdin, stdout:$stdout, stderr:$stderr, world:self)
|
171
|
-
evaluation.evaluate(statement) do |command, stdin, stdout, stderr, wait|
|
172
|
-
context.clear_commands
|
173
|
-
context.add_command_to_run command, stdin:stdin, stdout:stdout, stderr:stderr, wait:wait
|
174
|
-
@last_result = context.execute(world:self) || 0
|
175
|
-
if @last_result.is_a?(Integer)
|
176
|
-
Yap::Shell::Execution::Result.new(
|
177
|
-
status_code: @last_result,
|
178
|
-
directory: Dir.pwd,
|
179
|
-
n: 1,
|
180
|
-
of: 1
|
181
|
-
)
|
182
|
-
else
|
183
|
-
@last_result
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
@last_result
|
189
|
-
end
|
190
|
-
|
191
|
-
def foreground?
|
192
|
-
return unless STDIN.isatty
|
193
|
-
Process.getpgrp == Termios.tcgetpgrp($stdout)
|
194
|
-
end
|
195
|
-
|
196
|
-
def history
|
197
|
-
@editor.history
|
198
|
-
end
|
199
|
-
|
200
|
-
def interactive!
|
201
|
-
refresh_prompt
|
202
|
-
@editor.start
|
203
|
-
end
|
204
|
-
|
205
|
-
def prompt
|
206
|
-
@prompt
|
207
|
-
end
|
208
|
-
|
209
|
-
def prompt=(prompt=nil, &blk)
|
210
|
-
if prompt.is_a?(Yap::Shell::Prompt)
|
211
|
-
@prompt = prompt
|
212
|
-
elsif prompt.respond_to?(:call) # proc
|
213
|
-
@prompt = Yap::Shell::Prompt.new(text:prompt.call, &prompt)
|
214
|
-
else # text
|
215
|
-
@prompt = Yap::Shell::Prompt.new(text:prompt, &blk)
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
def secondary_prompt=(prompt=nil, &blk)
|
220
|
-
if prompt.is_a?(Yap::Shell::Prompt)
|
221
|
-
@secondary_prompt = prompt
|
222
|
-
elsif prompt.respond_to?(:call) # proc
|
223
|
-
@secondary_prompt = Yap::Shell::Prompt.new(text:prompt.call, &prompt)
|
224
|
-
else # text
|
225
|
-
@secondary_prompt = Yap::Shell::Prompt.new(text:prompt, &blk)
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
def refresh_prompt
|
230
|
-
@editor.prompt = @prompt.update.text
|
231
|
-
end
|
232
|
-
|
233
|
-
def right_prompt_text=(str)
|
234
|
-
@right_status_float.width = str.length
|
235
|
-
@right_status_box.content = str
|
236
|
-
end
|
237
|
-
|
238
|
-
def subscribe(*args, &blk)
|
239
|
-
@editor.subscribe(*args, &blk)
|
240
|
-
end
|
241
|
-
|
242
|
-
def build_editor_dom
|
243
|
-
@left_status_box = TerminalLayout::Box.new(content: "", style: {display: :inline})
|
244
|
-
@right_status_box = TerminalLayout::Box.new(content: "", style: {display: :inline})
|
245
|
-
@prompt_box = TerminalLayout::Box.new(content: "yap>", style: {display: :inline})
|
246
|
-
@input_box = TerminalLayout::InputBox.new(content: "", style: {display: :inline})
|
247
|
-
|
248
|
-
@content_box = TerminalLayout::Box.new(content: "", style: {display: :block})
|
249
|
-
@bottom_left_status_box = TerminalLayout::Box.new(content: "", style: {display: :inline})
|
250
|
-
@bottom_right_status_box = TerminalLayout::Box.new(content: "", style: {display: :inline})
|
251
|
-
|
252
|
-
@right_status_float = TerminalLayout::Box.new(style: {display: :float, float: :right, width: @right_status_box.content.length},
|
253
|
-
children: [
|
254
|
-
@right_status_box
|
255
|
-
]
|
256
|
-
)
|
257
|
-
|
258
|
-
RawLine::DomTree.new(
|
259
|
-
children:[
|
260
|
-
@right_status_float,
|
261
|
-
@left_status_box,
|
262
|
-
@prompt_box,
|
263
|
-
@input_box,
|
264
|
-
@content_box,
|
265
|
-
TerminalLayout::Box.new(style: {display: :float, float: :left, width: @bottom_left_status_box.content.length},
|
266
|
-
children: [
|
267
|
-
@bottom_left_status_box
|
268
|
-
]
|
269
|
-
),
|
270
|
-
TerminalLayout::Box.new(style: {display: :float, float: :right, width: @bottom_right_status_box.content.length},
|
271
|
-
children: [
|
272
|
-
@bottom_right_status_box
|
273
|
-
]
|
274
|
-
)
|
275
|
-
]
|
276
|
-
).tap do |dom|
|
277
|
-
dom.prompt_box = @prompt_box
|
278
|
-
dom.input_box = @input_box
|
279
|
-
dom.content_box = @content_box
|
280
|
-
@prompt_box.name = "prompt-box"
|
281
|
-
@input_box.name = "input-box"
|
282
|
-
@content_box.name = "content-box"
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|