yap-shell 0.7.1 → 0.7.2
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 +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
|