yap-shell 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +11 -0
  5. data/Gemfile.travis +8 -0
  6. data/README.md +9 -11
  7. data/addons/history/history.rb +1 -0
  8. data/addons/history_search/history_search.rb +21 -17
  9. data/bin/yap +47 -55
  10. data/bin/yap-dev +0 -1
  11. data/lib/yap/configuration.rb +25 -0
  12. data/lib/yap/shell/builtins/alias.rb +16 -0
  13. data/lib/yap/shell/commands.rb +15 -3
  14. data/lib/yap/shell/evaluation/shell_expansions.rb +10 -10
  15. data/lib/yap/shell/evaluation.rb +29 -4
  16. data/lib/yap/shell/execution/context.rb +3 -2
  17. data/lib/yap/shell/execution/file_system_command_execution.rb +9 -17
  18. data/lib/yap/shell/repl.rb +2 -5
  19. data/lib/yap/shell/version.rb +1 -1
  20. data/lib/yap/shell.rb +8 -2
  21. data/lib/yap/world.rb +23 -18
  22. data/lib/yap.rb +52 -7
  23. data/spec/features/aliases_spec.rb +78 -0
  24. data/spec/features/environment_variables_spec.rb +69 -0
  25. data/spec/features/filesystem_commands_spec.rb +61 -0
  26. data/spec/features/first_time_spec.rb +45 -0
  27. data/spec/features/grouping_spec.rb +81 -0
  28. data/spec/features/line_editing_spec.rb +166 -0
  29. data/spec/features/range_spec.rb +35 -0
  30. data/spec/features/redirection_spec.rb +225 -0
  31. data/spec/features/repetition_spec.rb +118 -0
  32. data/spec/features/shell_expansions_spec.rb +127 -0
  33. data/spec/spec_helper.rb +162 -0
  34. data/spec/support/matchers/have_not_printed.rb +30 -0
  35. data/spec/support/matchers/have_printed.rb +30 -0
  36. data/spec/support/very_soon.rb +9 -0
  37. data/spec/support/yap_spec_dsl.rb +240 -0
  38. data/yap-shell.gemspec +4 -2
  39. metadata +69 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 87a1ed22865947c8ef75b2a16007dc72270dab7a
4
- data.tar.gz: 38e78b4b608f6ec74c922472d6e965ef0fcccd1e
3
+ metadata.gz: 7186e70881e605ba5563cd30e9674030c55f4332
4
+ data.tar.gz: abbfa86bde0fabf472adca6a87843588e8760671
5
5
  SHA512:
6
- metadata.gz: 66989ed7684b9327859f04c655991b49bbeefeb83706bb14889395e32b75d08ad81a6858d780ac78ad75bb0fc1a35b3b99b8467eac1275d35a51bdca114a65a8
7
- data.tar.gz: ed9d4d26e65195606b817ac1f6c46d7ac490cf147cb6e74471e7ce192f41c7e5082ffba2e58e931b61fa83013dfc5f1bcc14b46c39389d24ef4d93159a2cd645
6
+ metadata.gz: 4498e667793d6009c6a988fd85b3977b7a01d79dee15565e71b9c6b10cc86a844263f9215205bf382e9ad152fde34589890d022a7bdb92d536204e3595df967f
7
+ data.tar.gz: fb6bff600285457f6aeea74c1fb5bf8d843dbba8b4a3b17c791b69ebd5dd93a5c1e2e0f7b7a660ed64afe468e49a3e2a73801a643d17fa811bfa6055ced9710f
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.2.3
1
+ 2.3.1
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.1
4
+ cache: bundler
5
+ gemfile: Gemfile.travis
6
+ script:
7
+ - bundle exec rspec -fd spec
8
+ notifications:
9
+ on_success: change
10
+ on_failure: always
11
+ on_start: false
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
- # Yap
2
-
3
- TODO: Write a gem description
1
+ [![Build Status](https://travis-ci.org/zdennis/yap-shell.svg?branch=master)](https://travis-ci.org/zdennis/yap-shell)
4
2
 
5
- ## Installation
3
+ # Yap
6
4
 
7
- Add this line to your application's Gemfile:
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
- gem 'yap-shell'
7
+ See the slide deck that [introduces Yap](http://slides.com/zdennis/yaptastic-1#/)
10
8
 
11
- And then execute:
9
+ ## Installation
12
10
 
13
- $ bundle
11
+ This should not go in your Gemfile, you should install from the command line:
14
12
 
15
- Or install it yourself as:
13
+ > gem install yap-shell
16
14
 
17
- $ gem install yap-shell
15
+ Then follow the on-screen installation instructions.
18
16
 
19
17
  ## Usage
20
18
 
21
- TODO: Write usage instructions here
19
+ See the Yap [shell reference](https://github.com/zdennis/yap-shell/wiki/ShellReference)
22
20
 
23
21
  ## Contributing
24
22
 
@@ -3,6 +3,7 @@ class History < Addon
3
3
  attr_reader :position
4
4
 
5
5
  def initialize_world(world)
6
+ return unless world.configuration.use_history?
6
7
  @world = world
7
8
 
8
9
  @file = world.configuration.path_for('history')
@@ -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
- # [1,2,3] => try 1,2,3 first ,then 1,2, then 1, then move on
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: 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
- if ENV['YAP_SHELL'] != 'active'
4
- # Start YAP with a pristine environment
5
- env2keep = {
6
- # DISPLAY needs to be inherited in order to launch GUI apps
7
- 'DISPLAY' => ENV['DISPLAY'],
8
- 'HOME' => ENV['HOME'],
9
- 'PATH' => ENV['PATH'],
10
-
11
- # For Yap's sanity
12
- 'RUBYOPT' => '-rubygems -EUTF-8',
13
- 'YAP_SHELL' => 'active',
14
-
15
- # Inherit the user's preferred shell
16
- 'SHELL' => ENV['SHELL'],
17
-
18
- # Necessary for ssh-agent
19
- 'SSH_AUTH_SOCK' => ENV['SSH_AUTH_SOCK'],
20
-
21
- # DEBUG/TREEFELL_OUT are for debuging yap itself
22
- 'DEBUG' => ENV['DEBUG'],
23
- 'TREEFELL_OUT' => ENV['TREEFELL_OUT']
24
- }
25
-
26
- ENV.clear
27
- env_params = env2keep.each_with_object([]) do |(key,value),arr|
28
- next if value.nil?
29
- arr << %|#{key}="#{value}"|
30
- end.join(' ')
31
-
32
- cmd = %|env -i #{env_params} #{File.expand_path(__FILE__)}|
33
- exec cmd
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
@@ -4,6 +4,5 @@ require 'bundler'
4
4
  Bundler.setup
5
5
  require 'pry'
6
6
 
7
- ENV['YAP_SHELL'] = 'active'
8
7
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
9
8
  load File.dirname(__FILE__) + '/yap'
@@ -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
@@ -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 then FileSystemCommand.new(world:world, str:command, args:args, heredoc:heredoc)
16
+ when FileSystemCommand then FileSystemCommand.new(world:world, str:command, args:args, heredoc:heredoc)
18
17
  else
19
- raise CommandUnknownError, "Don't know how to execute command: #{command}"
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
- str.gsub(/\$(\S+)/) do |match,*args|
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
- # Be compatible with Bash/Zsh which only do word-expansion if there
64
- # at least one comma listed. E.g. "a_{1,2}" => "a_1 a_2" whereas
65
- # "a_{1}" => "a_{1}"
66
- if expansions.length > 1
67
- expanded = expansions.map { |expansion| str.sub(/\{([^\}]+)\}/, expansion) }.tap do |expanded|
68
- Treefell['shell'].puts "shell-expansions expanding words in #{str} to #{expanded}"
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)
@@ -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(/\s+/)
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
- ["'#{node.lvalue}'"]
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
- stderr = :stdout
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
- stdout = :stderr
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 pid == Termios.tcgetpgrp(STDIN)
96
- Treefell['shell'].puts <<-DEBUG.gsub(/^\s*\|/, '')
97
- |restoring process group for STDIN to yap process with pid=#{Process.pid}
98
- DEBUG
99
- Process.setpgid Process.pid, Process.pid
100
- Termios.tcsetpgrp STDIN, Process.pid
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
@@ -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)
@@ -1,5 +1,5 @@
1
1
  module Yap
2
2
  module Shell
3
- VERSION = "0.5.2"
3
+ VERSION = "0.6.0"
4
4
  end
5
5
  end
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
- binding.pry unless ex.is_a?(SystemExit)
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
- puts
37
- puts yellow("Yap directory not found: #{configuration.yap_path}")
38
- puts
39
- puts "Initializing yap for the first time:"
40
- puts
41
-
42
- print " Creating #{configuration.yap_path} "
43
- FileUtils.mkdir_p configuration.yap_path
44
- puts green("done")
45
-
46
- print " Creating default #{configuration.preferred_yaprc_path} "
47
- FileUtils.cp configuration.yaprc_template_path, configuration.yap_path
48
- puts green("done")
49
- puts
50
- puts "To tweak yap take a look at #{configuration.preferred_yaprc_path}."
51
- puts
52
- puts "Reloading shell"
53
- reload!
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