yap-shell 0.5.2 → 0.6.0
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/.rspec +2 -0
- data/.ruby-version +1 -1
- data/.travis.yml +11 -0
- data/Gemfile.travis +8 -0
- data/README.md +9 -11
- data/addons/history/history.rb +1 -0
- data/addons/history_search/history_search.rb +21 -17
- data/bin/yap +47 -55
- data/bin/yap-dev +0 -1
- data/lib/yap/configuration.rb +25 -0
- data/lib/yap/shell/builtins/alias.rb +16 -0
- data/lib/yap/shell/commands.rb +15 -3
- data/lib/yap/shell/evaluation/shell_expansions.rb +10 -10
- data/lib/yap/shell/evaluation.rb +29 -4
- data/lib/yap/shell/execution/context.rb +3 -2
- data/lib/yap/shell/execution/file_system_command_execution.rb +9 -17
- data/lib/yap/shell/repl.rb +2 -5
- data/lib/yap/shell/version.rb +1 -1
- data/lib/yap/shell.rb +8 -2
- data/lib/yap/world.rb +23 -18
- data/lib/yap.rb +52 -7
- data/spec/features/aliases_spec.rb +78 -0
- data/spec/features/environment_variables_spec.rb +69 -0
- data/spec/features/filesystem_commands_spec.rb +61 -0
- data/spec/features/first_time_spec.rb +45 -0
- data/spec/features/grouping_spec.rb +81 -0
- data/spec/features/line_editing_spec.rb +166 -0
- data/spec/features/range_spec.rb +35 -0
- data/spec/features/redirection_spec.rb +225 -0
- data/spec/features/repetition_spec.rb +118 -0
- data/spec/features/shell_expansions_spec.rb +127 -0
- data/spec/spec_helper.rb +162 -0
- data/spec/support/matchers/have_not_printed.rb +30 -0
- data/spec/support/matchers/have_printed.rb +30 -0
- data/spec/support/very_soon.rb +9 -0
- data/spec/support/yap_spec_dsl.rb +240 -0
- data/yap-shell.gemspec +4 -2
- metadata +69 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7186e70881e605ba5563cd30e9674030c55f4332
|
4
|
+
data.tar.gz: abbfa86bde0fabf472adca6a87843588e8760671
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4498e667793d6009c6a988fd85b3977b7a01d79dee15565e71b9c6b10cc86a844263f9215205bf382e9ad152fde34589890d022a7bdb92d536204e3595df967f
|
7
|
+
data.tar.gz: fb6bff600285457f6aeea74c1fb5bf8d843dbba8b4a3b17c791b69ebd5dd93a5c1e2e0f7b7a660ed64afe468e49a3e2a73801a643d17fa811bfa6055ced9710f
|
data/.rspec
ADDED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3.1
|
data/.travis.yml
ADDED
data/Gemfile.travis
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'treefell', git: 'https://github.com/zdennis/treefell.git'
|
6
|
+
gem 'yap-shell-parser', git: 'https://github.com/zdennis/yap-shell-parser.git'
|
7
|
+
gem 'terminal-layout', git: 'https://github.com/zdennis/terminal-layout.git'
|
8
|
+
gem 'yap-rawline', git: 'https://github.com/zdennis/rawline.git'
|
data/README.md
CHANGED
@@ -1,24 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
TODO: Write a gem description
|
1
|
+
[](https://travis-ci.org/zdennis/yap-shell)
|
4
2
|
|
5
|
-
|
3
|
+
# Yap
|
6
4
|
|
7
|
-
|
5
|
+
Yap is a modern, developer shell. Inspired by shells like bash and zsh it's a modern implementation keeping the good parts and leaving out the rest.
|
8
6
|
|
9
|
-
|
7
|
+
See the slide deck that [introduces Yap](http://slides.com/zdennis/yaptastic-1#/)
|
10
8
|
|
11
|
-
|
9
|
+
## Installation
|
12
10
|
|
13
|
-
|
11
|
+
This should not go in your Gemfile, you should install from the command line:
|
14
12
|
|
15
|
-
|
13
|
+
> gem install yap-shell
|
16
14
|
|
17
|
-
|
15
|
+
Then follow the on-screen installation instructions.
|
18
16
|
|
19
17
|
## Usage
|
20
18
|
|
21
|
-
|
19
|
+
See the Yap [shell reference](https://github.com/zdennis/yap-shell/wiki/ShellReference)
|
22
20
|
|
23
21
|
## Contributing
|
24
22
|
|
data/addons/history/history.rb
CHANGED
@@ -80,22 +80,7 @@ class HistorySearch < Addon
|
|
80
80
|
if bytes.any?
|
81
81
|
Treefell['shell'].puts "history search found bytes: #{bytes.inspect}"
|
82
82
|
|
83
|
-
|
84
|
-
nbytes = bytes.dup
|
85
|
-
search_bytes = []
|
86
|
-
loop do
|
87
|
-
if nbytes.empty?
|
88
|
-
break
|
89
|
-
elsif @keys[nbytes]
|
90
|
-
Treefell['shell'].puts "history search found key-binding for bytes=#{nbytes.inspect}"
|
91
|
-
@keys[nbytes].call
|
92
|
-
nbytes = search_bytes
|
93
|
-
search_bytes = []
|
94
|
-
else
|
95
|
-
search_bytes.unshift nbytes[-1]
|
96
|
-
nbytes = nbytes[0..-2]
|
97
|
-
end
|
98
|
-
end
|
83
|
+
search_bytes = process_bytes(bytes)
|
99
84
|
|
100
85
|
if search_bytes.any?
|
101
86
|
Treefell['shell'].puts "history searching with bytes=#{bytes.inspect}"
|
@@ -106,12 +91,31 @@ class HistorySearch < Addon
|
|
106
91
|
|
107
92
|
private
|
108
93
|
|
94
|
+
# given [1,2,3] first try [1,2,3]. If no key binding
|
95
|
+
# matches, then try [1,2]. If no keybinding matches try [1].
|
96
|
+
# Execution should flow in left to right, positional order.
|
97
|
+
# This returns an array of left over bytes in the order they
|
98
|
+
# appeared that did not match any keybinding
|
99
|
+
def process_bytes bytes, leftover=[]
|
100
|
+
if bytes.empty?
|
101
|
+
leftover
|
102
|
+
elsif @keys[bytes]
|
103
|
+
@keys[bytes].call
|
104
|
+
leftover
|
105
|
+
else
|
106
|
+
process_bytes(
|
107
|
+
bytes[0..-2],
|
108
|
+
[bytes[-1]].concat(leftover)
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
109
113
|
def accept
|
110
114
|
@done_proc.call(execute: false, result: result)
|
111
115
|
end
|
112
116
|
|
113
117
|
def cancel
|
114
|
-
@done_proc.call(execute: false, result:
|
118
|
+
@done_proc.call(execute: false, result: nil)
|
115
119
|
end
|
116
120
|
|
117
121
|
def execute
|
data/bin/yap
CHANGED
@@ -1,58 +1,50 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
34
|
-
else
|
35
|
-
file = __FILE__
|
36
|
-
if File.symlink?(file)
|
37
|
-
file = File.readlink(file)
|
38
|
-
end
|
39
|
-
|
40
|
-
trap "SIGTSTP", "IGNORE"
|
41
|
-
trap "SIGINT", "IGNORE"
|
42
|
-
trap "SIGTTIN", "IGNORE"
|
43
|
-
trap "SIGTTOU", "IGNORE"
|
44
|
-
|
45
|
-
$LOAD_PATH.unshift File.dirname(file) + '/../lib'
|
46
|
-
|
47
|
-
ENV['TERM'] ||= 'linux'
|
48
|
-
ENV['YAP_SHELL'] = 'active'
|
49
|
-
|
50
|
-
require "term/ansicolor"
|
51
|
-
require "tins"
|
52
|
-
require "byebug"
|
53
|
-
require "pry"
|
54
|
-
require "yap"
|
55
|
-
|
56
|
-
STDOUT.sync = true
|
57
|
-
Yap.run_shell
|
3
|
+
# Start YAP with a pristine environment
|
4
|
+
env2keep = {
|
5
|
+
# DISPLAY needs to be inherited in order to launch GUI apps
|
6
|
+
'DISPLAY' => ENV['DISPLAY'],
|
7
|
+
'HOME' => ENV['HOME'],
|
8
|
+
'PATH' => ENV['PATH'],
|
9
|
+
|
10
|
+
# For Yap's sanity
|
11
|
+
'RUBYOPT' => '-rubygems -EUTF-8',
|
12
|
+
|
13
|
+
# Inherit the user's preferred shell
|
14
|
+
'SHELL' => ENV['SHELL'],
|
15
|
+
|
16
|
+
# Necessary for ssh-agent
|
17
|
+
'SSH_AUTH_SOCK' => ENV['SSH_AUTH_SOCK'],
|
18
|
+
|
19
|
+
# DEBUG/TREEFELL_OUT are for debuging yap itself
|
20
|
+
'DEBUG' => ENV['DEBUG'],
|
21
|
+
'TREEFELL_OUT' => ENV['TREEFELL_OUT']
|
22
|
+
}
|
23
|
+
|
24
|
+
ENV.clear
|
25
|
+
env_params = env2keep.each_with_object([]) do |(key,value),arr|
|
26
|
+
# next if value.nil?
|
27
|
+
arr << %|#{key}="#{value}"|
|
28
|
+
ENV[key] = value
|
29
|
+
end.join(' ')
|
30
|
+
|
31
|
+
file = __FILE__
|
32
|
+
if File.symlink?(file)
|
33
|
+
file = File.readlink(file)
|
58
34
|
end
|
35
|
+
|
36
|
+
trap "SIGTSTP", "IGNORE"
|
37
|
+
trap "SIGINT", "IGNORE"
|
38
|
+
trap "SIGTTIN", "IGNORE"
|
39
|
+
trap "SIGTTOU", "IGNORE"
|
40
|
+
|
41
|
+
$LOAD_PATH.unshift File.dirname(file) + '/../lib'
|
42
|
+
|
43
|
+
ENV['TERM'] ||= 'linux'
|
44
|
+
|
45
|
+
require "yap"
|
46
|
+
|
47
|
+
STDOUT.sync = true
|
48
|
+
STDERR.sync = true
|
49
|
+
|
50
|
+
Yap.run_shell(ARGV)
|
data/bin/yap-dev
CHANGED
data/lib/yap/configuration.rb
CHANGED
@@ -11,6 +11,31 @@ module Yap
|
|
11
11
|
attr_accessor :addon_paths
|
12
12
|
attr_accessor :rcfiles
|
13
13
|
|
14
|
+
def self.option(name, type=nil, default: nil)
|
15
|
+
reader_method = name.to_s
|
16
|
+
define_method(reader_method) do
|
17
|
+
return default unless instance_variable_defined?("@#{name}")
|
18
|
+
value = instance_variable_get("@#{name}")
|
19
|
+
return !!value if type == :boolean
|
20
|
+
value
|
21
|
+
end
|
22
|
+
|
23
|
+
writer_method = "#{reader_method}="
|
24
|
+
define_method(writer_method) do |value|
|
25
|
+
instance_variable_set("@#{name}", value)
|
26
|
+
end
|
27
|
+
|
28
|
+
if type == :boolean
|
29
|
+
query_method = "#{reader_method}?"
|
30
|
+
alias_method query_method, reader_method
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
option :skip_first_time, :boolean, default: false
|
35
|
+
option :use_addons, :boolean, default: true
|
36
|
+
option :use_history, :boolean, default: true
|
37
|
+
option :use_rcfiles, :boolean, default: true
|
38
|
+
|
14
39
|
def initialize
|
15
40
|
@addon_paths = [
|
16
41
|
"#{File.dirname(__FILE__)}/../../addons",
|
@@ -4,6 +4,8 @@ module Yap::Shell
|
|
4
4
|
require 'yap/shell/aliases'
|
5
5
|
|
6
6
|
module Builtins
|
7
|
+
Color = ::Term::ANSIColor
|
8
|
+
|
7
9
|
builtin :alias do |args:, stdout:, **kwargs|
|
8
10
|
output = []
|
9
11
|
if args.empty?
|
@@ -18,9 +20,23 @@ module Yap::Shell
|
|
18
20
|
else
|
19
21
|
name_eq_value = args.map(&:shellsplit).join(' ')
|
20
22
|
name, command = name_eq_value.scan(/^(.*?)\s*=\s*(.*)$/).flatten
|
23
|
+
output << "Setting alias #{name} #{Color.green('done')}"
|
21
24
|
Yap::Shell::Aliases.instance.set_alias name, command
|
22
25
|
end
|
23
26
|
stdout.puts output.join("\n")
|
24
27
|
end
|
28
|
+
|
29
|
+
builtin :unalias do |args:, stdout:, **kwargs|
|
30
|
+
output = []
|
31
|
+
if args.empty?
|
32
|
+
output << "Usage: unalias <aliasname>"
|
33
|
+
else
|
34
|
+
args.each do |alias_name|
|
35
|
+
Yap::Shell::Aliases.instance.unset_alias alias_name
|
36
|
+
output << "Removing alias #{alias_name} #{Color.green('done')}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
stdout.puts output.join("\n")
|
40
|
+
end
|
25
41
|
end
|
26
42
|
end
|
data/lib/yap/shell/commands.rb
CHANGED
@@ -5,7 +5,6 @@ module Yap::Shell
|
|
5
5
|
require 'yap/shell/execution/result'
|
6
6
|
|
7
7
|
class CommandError < StandardError ; end
|
8
|
-
class CommandUnknownError < CommandError ; end
|
9
8
|
|
10
9
|
class CommandFactory
|
11
10
|
def self.build_command_for(world:, command:, args:, heredoc:, internally_evaluate:, line:)
|
@@ -14,9 +13,9 @@ module Yap::Shell
|
|
14
13
|
case command
|
15
14
|
when ShellCommand then ShellCommand.new(world:world, str:command, args:args, heredoc:heredoc, line:line)
|
16
15
|
when BuiltinCommand then BuiltinCommand.new(world:world, str:command, args:args, heredoc:heredoc)
|
17
|
-
when FileSystemCommand
|
16
|
+
when FileSystemCommand then FileSystemCommand.new(world:world, str:command, args:args, heredoc:heredoc)
|
18
17
|
else
|
19
|
-
|
18
|
+
UnknownCommand.new(world:world, str:command, args:args, heredoc:heredoc)
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
@@ -75,6 +74,19 @@ module Yap::Shell
|
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
77
|
+
class UnknownCommand < Command
|
78
|
+
EXIT_CODE = 127
|
79
|
+
|
80
|
+
def execute(stdin:, stdout:, stderr:)
|
81
|
+
stderr.puts "yap: command not found: #{str}"
|
82
|
+
EXIT_CODE
|
83
|
+
end
|
84
|
+
|
85
|
+
def type
|
86
|
+
:BuiltinCommand
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
78
90
|
class FileSystemCommand < Command
|
79
91
|
def self.world
|
80
92
|
::Yap::World.instance
|
@@ -39,7 +39,8 @@ module Yap::Shell
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def env_expand(str)
|
42
|
-
|
42
|
+
# match "$a", "$a $b", "$a$b", "$cat$dog $foo"
|
43
|
+
str.gsub(/\$([^\$\s]+)/) do |match,*args|
|
43
44
|
var_name = match[1..-1]
|
44
45
|
if var_name == '?'
|
45
46
|
(world.last_result ? world.last_result.status_code.to_s : '0').tap do |expanded|
|
@@ -60,17 +61,16 @@ module Yap::Shell
|
|
60
61
|
if content
|
61
62
|
expansions = content.split(",", -1)
|
62
63
|
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
return expanded
|
64
|
+
# Break compatibility with Bash/Zsh. They do not expand words
|
65
|
+
# when there is only one word supplied (e.g. "a_{word}"), but this
|
66
|
+
# is counter-intuitive to me. It should behave the same regardless
|
67
|
+
# of the number of words provided to expand.
|
68
|
+
expanded = expansions.map { |expansion| str.sub(/\{([^\}]+)\}/, expansion) }.tap do |expanded|
|
69
|
+
Treefell['shell'].puts "shell-expansions expanding words in #{str} to #{expanded}"
|
71
70
|
end
|
71
|
+
else
|
72
|
+
[str]
|
72
73
|
end
|
73
|
-
[str]
|
74
74
|
end
|
75
75
|
|
76
76
|
def process_expansions(expansions, escape_directory_expansions: true)
|
data/lib/yap/shell/evaluation.rb
CHANGED
@@ -158,7 +158,7 @@ module Yap::Shell
|
|
158
158
|
values = []
|
159
159
|
if node.head
|
160
160
|
node.head.accept(self)
|
161
|
-
values = stdin.read.split(/\
|
161
|
+
values = stdin.read.split(/\n|\0/)
|
162
162
|
else
|
163
163
|
# assume range for now
|
164
164
|
values = @current_range_values
|
@@ -327,10 +327,13 @@ module Yap::Shell
|
|
327
327
|
def process_ArgumentNode(node)
|
328
328
|
if node.single_quoted?
|
329
329
|
debug_visit(node, 'single quoted argument, performing no expansion')
|
330
|
-
["
|
330
|
+
["#{node.lvalue}"]
|
331
331
|
elsif node.double_quoted?
|
332
332
|
debug_visit(node, 'double quoted argument, performing variable expansion')
|
333
333
|
[variable_expand(node.lvalue)]
|
334
|
+
elsif node.escaped?
|
335
|
+
debug_visit(node, 'escaped argument, skipping variable expansion')
|
336
|
+
[node.lvalue]
|
334
337
|
else
|
335
338
|
debug_visit(node, 'unquoted argument, performing shell expansion')
|
336
339
|
shell_expand(node.lvalue, escape_directory_expansions: false)
|
@@ -388,16 +391,38 @@ module Yap::Shell
|
|
388
391
|
case redirect.kind
|
389
392
|
when "<"
|
390
393
|
stdin = redirect.target
|
394
|
+
stdin = File.open(stdin, "rb") if stdin.is_a?(String)
|
391
395
|
when ">", "1>"
|
392
396
|
stdout = redirect.target
|
397
|
+
stdout = File.open(stdout, "wb") if stdout.is_a?(String)
|
393
398
|
when "1>&2"
|
394
|
-
|
399
|
+
stdout = redirect.target
|
400
|
+
stdout = File.open(stdout, "wb") if stdout.is_a?(String)
|
401
|
+
stderr = stdout
|
395
402
|
when "2>"
|
396
403
|
stderr = redirect.target
|
404
|
+
stderr = File.open(stderr, "wb") if stderr.is_a?(String)
|
397
405
|
when "2>&1"
|
398
|
-
|
406
|
+
stderr = redirect.target
|
407
|
+
stderr = File.open(stderr, "wb") if stderr.is_a?(String)
|
408
|
+
stdout = stderr
|
409
|
+
when "1>>", ">>"
|
410
|
+
stdout = redirect.target
|
411
|
+
stdout = File.open(stdout, "ab") if stdout.is_a?(String)
|
412
|
+
when "2>>"
|
413
|
+
stderr = redirect.target
|
414
|
+
stderr = File.open(stderr, "ab") if stderr.is_a?(String)
|
415
|
+
when "&>"
|
416
|
+
stdout = redirect.target
|
417
|
+
stdout = File.open(stdout, "wb") if stdout.is_a?(String)
|
418
|
+
stderr = stdout
|
419
|
+
when "&>>"
|
420
|
+
stdout = redirect.target
|
421
|
+
stdout = File.open(stdout, "ab") if stdout.is_a?(String)
|
422
|
+
stderr = stdout
|
399
423
|
end
|
400
424
|
end
|
425
|
+
|
401
426
|
[stdin, stdout, stderr]
|
402
427
|
end
|
403
428
|
|
@@ -59,7 +59,8 @@ module Yap::Shell::Execution
|
|
59
59
|
world: world
|
60
60
|
)
|
61
61
|
|
62
|
-
@saved_tty_attrs = Termios.tcgetattr(STDIN)
|
62
|
+
@saved_tty_attrs = Termios.tcgetattr(STDIN) if STDIN.isatty
|
63
|
+
|
63
64
|
Treefell['shell'].puts "firing :before_execute for #{command}"
|
64
65
|
self.class.fire :before_execute, world, command: command
|
65
66
|
|
@@ -90,7 +91,7 @@ module Yap::Shell::Execution
|
|
90
91
|
self.class.fire :after_execute, world, command: command, result: result
|
91
92
|
|
92
93
|
results << process_execution_result(execution_context:execution_context, result:result)
|
93
|
-
Termios.tcsetattr(STDIN, Termios::TCSANOW, @saved_tty_attrs)
|
94
|
+
Termios.tcsetattr(STDIN, Termios::TCSANOW, @saved_tty_attrs) if STDIN.isatty
|
94
95
|
end
|
95
96
|
end
|
96
97
|
|
@@ -27,16 +27,6 @@ module Yap::Shell::Execution
|
|
27
27
|
# Set the process group of the forked to child to that of the
|
28
28
|
Process.setpgrp
|
29
29
|
|
30
|
-
# Start a new process group as the session leader. Now we are
|
31
|
-
# responsible for sending signals that would have otherwise
|
32
|
-
# been propagated to the process, e.g. SIGINT, SIGSTOP, SIGCONT, etc.
|
33
|
-
stdin = File.open(stdin, "rb") if stdin.is_a?(String)
|
34
|
-
stdout = File.open(stdout, "wb") if stdout.is_a?(String)
|
35
|
-
stderr = File.open(stderr, "wb") if stderr.is_a?(String)
|
36
|
-
|
37
|
-
stdout = stderr if stdout == :stderr
|
38
|
-
stderr = stdout if stderr == :stdout
|
39
|
-
|
40
30
|
$stdin.reopen stdin
|
41
31
|
$stdout.reopen stdout
|
42
32
|
$stderr.reopen stderr
|
@@ -72,7 +62,7 @@ module Yap::Shell::Execution
|
|
72
62
|
end
|
73
63
|
|
74
64
|
# Set terminal's process group to that of the child process
|
75
|
-
Termios.tcsetpgrp STDIN, pid
|
65
|
+
Termios.tcsetpgrp STDIN, pid if STDIN.isatty
|
76
66
|
pid, status = Process.wait2(pid, Process::WUNTRACED) if wait
|
77
67
|
|
78
68
|
# If we're not printing to the terminal then close in/out/err. This
|
@@ -92,12 +82,14 @@ module Yap::Shell::Execution
|
|
92
82
|
# if the pid that just stopped was the process group owner then
|
93
83
|
# give it back to the us so we can become the foreground process
|
94
84
|
# in the terminal
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
85
|
+
if STDIN.isatty
|
86
|
+
if pid == Termios.tcgetpgrp(STDIN)
|
87
|
+
Treefell['shell'].puts <<-DEBUG.gsub(/^\s*\|/, '')
|
88
|
+
|restoring process group for STDIN to yap process with pid=#{Process.pid}
|
89
|
+
DEBUG
|
90
|
+
Process.setpgid Process.pid, Process.pid
|
91
|
+
Termios.tcsetpgrp STDIN, Process.pid
|
92
|
+
end
|
101
93
|
end
|
102
94
|
|
103
95
|
# if the reason we stopped is from being suspended
|
data/lib/yap/shell/repl.rb
CHANGED
@@ -38,14 +38,9 @@ module Yap::Shell
|
|
38
38
|
line << more_input
|
39
39
|
retry
|
40
40
|
end
|
41
|
-
rescue ::Yap::Shell::CommandUnknownError => ex
|
42
|
-
Treefell['shell'].puts "rescued #{ex}, telling user"
|
43
|
-
puts " CommandError: #{ex.message}"
|
44
41
|
rescue ::Yap::Shell::Parser::ParseError => ex
|
45
42
|
Treefell['shell'].puts "rescued #{ex}, telling user"
|
46
43
|
puts " Parse error: #{ex.message}"
|
47
|
-
ensure
|
48
|
-
@world.editor.reset_line
|
49
44
|
end
|
50
45
|
|
51
46
|
ensure_process_group_controls_the_tty
|
@@ -209,6 +204,8 @@ module Yap::Shell
|
|
209
204
|
# if we haven't been made the process group controlling the TTY that we
|
210
205
|
# become so. This method intentionally blocks.
|
211
206
|
def ensure_process_group_controls_the_tty
|
207
|
+
return unless STDIN.isatty
|
208
|
+
|
212
209
|
tty_pgrp = Termios.tcgetpgrp(STDIN)
|
213
210
|
while ![Process.pid, Process.getpgrp].include?(tty_pgrp)
|
214
211
|
Termios.tcsetpgrp(STDIN, Process.pid)
|
data/lib/yap/shell/version.rb
CHANGED
data/lib/yap/shell.rb
CHANGED
@@ -11,7 +11,6 @@ module Yap
|
|
11
11
|
|
12
12
|
autoload :CommandFactory, "yap/shell/commands"
|
13
13
|
autoload :CommandError, "yap/shell/commands"
|
14
|
-
autoload :CommandUnknownError, "yap/shell/commands"
|
15
14
|
autoload :BuiltinCommand, "yap/shell/commands"
|
16
15
|
autoload :FileSystemCommand, "yap/shell/commands"
|
17
16
|
autoload :RubyCommand, "yap/shell/commands"
|
@@ -102,7 +101,14 @@ module Yap
|
|
102
101
|
@world.editor.puts "^C"
|
103
102
|
retry
|
104
103
|
rescue Exception => ex
|
105
|
-
|
104
|
+
if !ex.is_a?(SystemExit)
|
105
|
+
if $stdout.isatty
|
106
|
+
binding.pry
|
107
|
+
else
|
108
|
+
$stdout.puts ex.message
|
109
|
+
raise ex
|
110
|
+
end
|
111
|
+
end
|
106
112
|
end
|
107
113
|
end
|
108
114
|
end
|
data/lib/yap/world.rb
CHANGED
@@ -33,24 +33,28 @@ module Yap
|
|
33
33
|
|
34
34
|
# ensure yap directory exists
|
35
35
|
if !File.exists?(configuration.yap_path)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
54
58
|
end
|
55
59
|
|
56
60
|
@editor = RawLine::Editor.create(dom: dom)
|
@@ -147,6 +151,7 @@ module Yap
|
|
147
151
|
end
|
148
152
|
|
149
153
|
def foreground?
|
154
|
+
return unless STDIN.isatty
|
150
155
|
Process.getpgrp == Termios.tcgetpgrp($stdout)
|
151
156
|
end
|
152
157
|
|