yap-shell 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,24 +9,30 @@ module Yap::Shell
9
9
  end
10
10
 
11
11
  def expand_aliases_in(input)
12
+ Treefell['shell'].puts "shell-expansions expand aliases in: #{input.inspect}"
12
13
  head, *tail = input.split(/\s/, 2).first
13
- if new_head=aliases.fetch_alias(head)
14
+ expanded = if aliases.has_key?(head)
15
+ new_head=aliases.fetch_alias(head)
14
16
  [new_head].concat(tail).join(" ")
15
17
  else
16
18
  input
17
19
  end
20
+ expanded
18
21
  end
19
22
 
20
23
  def expand_words_in(input, escape_directory_expansions: true)
21
- [input].flatten.inject([]) do |results,str|
24
+ Treefell['shell'].puts "shell-expansions expand words in: #{input.inspect}"
25
+ expanded = [input].flatten.inject([]) do |results,str|
22
26
  results << process_expansions(
23
27
  word_expand(str),
24
28
  escape_directory_expansions: escape_directory_expansions
25
29
  )
26
30
  end.flatten
31
+ expanded
27
32
  end
28
33
 
29
34
  def expand_variables_in(input)
35
+ Treefell['shell'].puts "shell-expansions expand variables in: #{input.inspect}"
30
36
  env_expand(input)
31
37
  end
32
38
 
@@ -35,11 +41,16 @@ module Yap::Shell
35
41
  def env_expand(str)
36
42
  str.gsub(/\$(\S+)/) do |match,*args|
37
43
  var_name = match[1..-1]
38
- case var_name
39
- when "?"
40
- world.last_result ? world.last_result.status_code.to_s : '0'
44
+ if var_name == '?'
45
+ (world.last_result ? world.last_result.status_code.to_s : '0').tap do |expanded|
46
+ Treefell['shell'].puts "shell-expansions expanding env var #{match} to #{expanded}"
47
+ end
48
+ elsif world.env.has_key?(var_name)
49
+ world.env.fetch(var_name).tap do |expanded|
50
+ Treefell['shell'].puts "shell-expansions expanding env var #{match} to #{expanded}"
51
+ end
41
52
  else
42
- world.env.fetch(var_name){ match }
53
+ match
43
54
  end
44
55
  end
45
56
  end
@@ -53,10 +64,13 @@ module Yap::Shell
53
64
  # at least one comma listed. E.g. "a_{1,2}" => "a_1 a_2" whereas
54
65
  # "a_{1}" => "a_{1}"
55
66
  if expansions.length > 1
56
- return expansions.map { |expansion| str.sub(/\{([^\}]+)\}/, expansion) }
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
57
71
  end
58
72
  end
59
- return [str]
73
+ [str]
60
74
  end
61
75
 
62
76
  def process_expansions(expansions, escape_directory_expansions: true)
@@ -3,13 +3,17 @@ module Yap::Shell::Execution
3
3
 
4
4
  class BuiltinCommandExecution < CommandExecution
5
5
  on_execute do |command:, n:, of:, wait:|
6
+ Treefell['shell'].puts "builtin command executing: #{command}"
6
7
  status_code = command.execute(stdin:@stdin, stdout:@stdout, stderr:@stderr)
7
8
  if status_code == :resume
9
+ Treefell['shell'].puts "builtin command execution resumed: #{command}"
8
10
  ResumeExecution.new(status_code:0, directory:Dir.pwd, n:n, of:of)
9
11
  else
10
12
  @stdout.close if @stdout != $stdout && !@stdout.closed?
11
13
  @stderr.close if @stderr != $stderr && !@stderr.closed?
12
- Result.new(status_code:status_code, directory:Dir.pwd, n:n, of:of)
14
+ Result.new(status_code:status_code, directory:Dir.pwd, n:n, of:of).tap do |result|
15
+ Treefell['shell'].puts "builtin command execution done with result=#{result.inspect}"
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -60,12 +60,15 @@ module Yap::Shell::Execution
60
60
  )
61
61
 
62
62
  @saved_tty_attrs = Termios.tcgetattr(STDIN)
63
+ Treefell['shell'].puts "firing :before_execute for #{command}"
63
64
  self.class.fire :before_execute, world, command: command
64
65
 
65
66
  begin
66
67
  result = execution_context.execute(command:command, n:i, of:of, wait:wait)
67
68
  rescue Exception => ex
68
69
  raise(ex) if ex.is_a?(SystemExit)
70
+
71
+ Treefell['shell'].puts "rescued unexpected error=#{ex} with message=#{ex.message.inspect}"
69
72
  puts <<-ERROR.gsub(/^\s*\|/, '')
70
73
  |******************************
71
74
  |\e[31mWhoops! An unexpected error has occurred\e[0m
@@ -83,6 +86,7 @@ module Yap::Shell::Execution
83
86
  ERROR
84
87
  end
85
88
 
89
+ Treefell['shell'].puts "firing :after_execute for #{command}"
86
90
  self.class.fire :after_execute, world, command: command, result: result
87
91
 
88
92
  results << process_execution_result(execution_context:execution_context, result:result)
@@ -100,15 +104,19 @@ module Yap::Shell::Execution
100
104
  def process_execution_result(execution_context:, result:)
101
105
  case result
102
106
  when SuspendExecution
107
+ Treefell['shell'].puts "suspending execution context"
103
108
  @suspended_execution_contexts.push execution_context
104
109
  return result
105
110
 
106
111
  when ResumeExecution
112
+ Treefell['shell'].puts "resuming suspended execution context"
107
113
  execution_context = @suspended_execution_contexts.pop
108
114
  if execution_context
109
115
  nresult = execution_context.resume
116
+ Treefell['shell'].puts "resuming suspended execution context success"
110
117
  return process_execution_result execution_context: execution_context, result: nresult
111
118
  else
119
+ Treefell['shell'].puts "error: cannot resume execution when there is nothing suspended"
112
120
  @stderr.puts "fg: No such job"
113
121
  end
114
122
  else
@@ -49,11 +49,13 @@ module Yap::Shell::Execution
49
49
  ENV.replace(before)
50
50
  end
51
51
  end
52
+ Treefell['shell'].puts "forked child process pid=#{pid} to execute #{command}"
52
53
 
53
54
  # Put the child process into a process group of its own
54
55
  Process.setpgid pid, pid
55
56
 
56
57
  if command.heredoc
58
+ Treefell['shell'].puts "command has heredoc, wriing to stdin"
57
59
  w.write command.heredoc
58
60
  w.close
59
61
  end
@@ -67,9 +69,11 @@ module Yap::Shell::Execution
67
69
  # is so the next command in the pipeline can complete and don't hang waiting for
68
70
  # stdin after the command that's writing to its stdin has completed.
69
71
  if stdout != $stdout && stdout.is_a?(IO) && !stdout.closed? then
72
+ Treefell['shell'].puts "closing stdout for child process with pid=#{pid}"
70
73
  stdout.close
71
74
  end
72
75
  if stderr != $stderr && stderr.is_a?(IO) && !stderr.closed? then
76
+ Treefell['shell'].puts "closing stderr for child process with pid=#{pid}"
73
77
  stderr.close
74
78
  end
75
79
  # if stdin != $stdin && !stdin.closed? then stdin.close end
@@ -79,17 +83,24 @@ module Yap::Shell::Execution
79
83
  # give it back to the us so we can become the foreground process
80
84
  # in the terminal
81
85
  if pid == Termios.tcgetpgrp(STDIN)
86
+ Treefell['shell'].puts <<-DEBUG.gsub(/^\s*\|/, '')
87
+ |restoring process group for STDIN to yap process with pid=#{Process.pid}
88
+ DEBUG
82
89
  Process.setpgid Process.pid, Process.pid
83
90
  Termios.tcsetpgrp STDIN, Process.pid
84
91
  end
85
92
 
86
93
  # if the reason we stopped is from being suspended
87
- if status && status.stopsig == Signal.list["TSTP"]
88
- puts "Process (#{pid}) suspended: #{status.stopsig}" if ENV["DEBUG"]
94
+ sigtstp = Signal.list["TSTP"]
95
+ if status && status.stopsig == sigtstp
96
+ Treefell['shell'].puts "process pid=#{pid} suspended by signal=#{status.stopsig.inspect}"
97
+ Treefell['shell'].puts "$?: #{$?.inspect}"
89
98
  suspended(command:command, n:n, of:of, pid: pid)
90
99
  result = Yap::Shell::Execution::SuspendExecution.new(status_code:nil, directory:Dir.pwd, n:n, of:of)
91
100
  else
92
- puts "Process (#{pid}) not suspended? #{status.stopsig}" if ENV["DEBUG"]
101
+ caused_by_signal = status ? (status.termsig || status.stopsig) : nil
102
+ Treefell['shell'].puts "process pid=#{pid} stopped by signal=#{caused_by_signal.inspect}"
103
+ Treefell['shell'].puts "$?: #{$?.inspect}"
93
104
  # if a signal killed or stopped the process (such as SIGINT or SIGTSTP) $? is nil.
94
105
  exitstatus = $? ? $?.exitstatus : nil
95
106
  result = Yap::Shell::Execution::Result.new(status_code:exitstatus, directory:Dir.pwd, n:n, of:of)
@@ -99,18 +110,20 @@ module Yap::Shell::Execution
99
110
  def resume
100
111
  args = @suspended
101
112
  @suspended = nil
113
+ pid = args[:pid]
114
+ sigcont = Signal.list["CONT"]
102
115
 
103
- puts "Resuming: #{args[:pid]}" if ENV["DEBUG"]
116
+ Treefell['shell'].puts "resuming suspended process pid=#{pid} by sending it signal=#{sigcont}"
104
117
  resume_blk = lambda do
105
- Process.kill "SIGCONT", args[:pid]
106
- args[:pid]
118
+ Process.kill sigcont, pid
119
+ pid
107
120
  end
108
121
 
109
122
  self.instance_exec command:args[:command], n:args[:n], of:args[:of], resume_blk:resume_blk, wait:true, &self.class.on_execute
110
123
  end
111
124
 
112
125
  def suspended(command:, n:, of:, pid:)
113
- puts "Suspending: #{pid}" if ENV["DEBUG"]
126
+ Treefell['shell'].puts "process pid=#{pid} suspended"
114
127
  @suspended = {
115
128
  command: command,
116
129
  n: n,
@@ -3,83 +3,78 @@ module Yap::Shell::Execution
3
3
  on_execute do |command:, n:, of:, wait:|
4
4
  result = nil
5
5
  stdin, stdout, stderr, world = @stdin, @stdout, @stderr, @world
6
- # t = Thread.new {
7
- exit_code = 0
8
- first_command = n == 1
9
6
 
10
- f = nil
11
- ruby_result = nil
12
- begin
13
- ruby_command = command.to_executable_str
7
+ exit_code = 0
8
+ first_command = n == 1
14
9
 
15
- contents = if stdin.is_a?(String)
16
- puts "READ: stdin as a String: #{stdin.inspect}" if ENV["DEBUG"]
17
- f = File.open stdin
18
- f.read
19
- elsif stdin != $stdin
20
- puts "READ: stdin is not $stdin: #{stdin.inspect}" if ENV["DEBUG"]
21
- stdin.read
22
- else
23
- puts "READ: contents is: #{contents.inspect}" if ENV["DEBUG"]
24
- end
10
+ f = nil
11
+ ruby_result = nil
12
+ begin
13
+ ruby_command = command.to_executable_str
25
14
 
26
- puts "READ: #{contents.length} bytes from #{stdin}" if ENV["DEBUG"] && contents
27
- world.contents = contents
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
28
25
 
29
- method = ruby_command.scan(/^(\w+(?:[!?]|\s*=)?)/).flatten.first.gsub(/\s/, '')
30
- puts "method: #{method}" if ENV["DEBUG"]
26
+ method = ruby_command.scan(/^(\w+(?:[!?]|\s*=)?)/).flatten.first.gsub(/\s/, '')
27
+ Treefell['shell'].puts "ruby execution: method=#{method.inspect}"
31
28
 
32
- obj = if first_command
33
- world
34
- elsif contents.respond_to?(method)
35
- contents
36
- else
37
- world
38
- end
29
+ obj = if first_command
30
+ world
31
+ elsif contents.respond_to?(method)
32
+ contents
33
+ else
34
+ world
35
+ end
39
36
 
40
- if ruby_command =~ /^[A-Z0-9]|::/
41
- puts "Evaluating #{ruby_command.inspect} globally" if ENV["DEBUG"]
42
- ruby_result = eval ruby_command
43
- else
44
- ruby_command = "self.#{ruby_command}"
45
- puts "Evaluating #{ruby_command.inspect} on #{obj.inspect}" if ENV["DEBUG"]
46
- ruby_result = obj.instance_eval ruby_command
47
- end
48
- rescue Exception => ex
49
- ruby_result = <<-EOT.gsub(/^\s*\S/, '')
50
- |Failed processing ruby: #{ruby_command}
51
- |#{ex}
52
- |#{ex.backtrace.join("\n")}
53
- EOT
54
- exit_code = 1
55
- ensure
56
- f.close if f && !f.closed?
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
57
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
58
55
 
59
- # The next line causes issues sometimes?
60
- # puts "WRITING #{ruby_result.length} bytes" if ENV["DEBUG"]
61
- ruby_result = ruby_result.to_s
62
- ruby_result << "\n" unless ruby_result.end_with?("\n")
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")
63
60
 
64
- stdout.write ruby_result
65
- stdout.flush
66
- stderr.flush
61
+ stdout.write ruby_result
62
+ stdout.flush
63
+ stderr.flush
67
64
 
68
- stdout.close if stdout != $stdout && !stdout.closed?
69
- stderr.close if stderr != $stderr && !stderr.closed?
65
+ stdout.close if stdout != $stdout && !stdout.closed?
66
+ stderr.close if stderr != $stderr && !stderr.closed?
70
67
 
71
- # Pass current execution to give any other threads a chance
72
- # to be scheduled before we send back our status code. This could
73
- # probably use a more elaborate signal or message passing scheme,
74
- # but that's for another day.
75
- # Thread.pass
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
76
73
 
77
- # Make up an exit code
78
- result = Result.new(status_code:exit_code, directory:Dir.pwd, n:n, of:of)
79
- # }
80
- # t.abort_on_exception = true
81
- # t.join
82
- result
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
83
78
  end
84
79
  end
85
80
  end
@@ -1,6 +1,8 @@
1
1
  module Yap::Shell::Execution
2
2
  class ShellCommandExecution < CommandExecution
3
3
  on_execute do |command:, n:, of:, wait:|
4
+ Treefell['shell'].puts "shell command execution: #{command}"
5
+
4
6
  possible_parameters = {
5
7
  command: command.str,
6
8
  args: command.args,
@@ -17,6 +19,7 @@ module Yap::Shell::Execution
17
19
  h
18
20
  end
19
21
 
22
+ Treefell['shell'].puts "shell command executing with params: #{params.inspect}"
20
23
  command_result = func.call(**params)
21
24
  @stdout.close if @stdout != $stdout && !@stdout.closed?
22
25
  @stderr.close if @stderr != $stderr && !@stderr.closed?
@@ -12,7 +12,11 @@ module Yap::Shell
12
12
  def initialize(world:nil)
13
13
  @world = world
14
14
  @editor= world.editor
15
+
16
+ Treefell['shell'].puts "installing default keybindings"
15
17
  install_default_keybindings
18
+
19
+ Treefell['shell'].puts "installing default tab completion"
16
20
  install_default_tab_completion_proc
17
21
  end
18
22
 
@@ -20,17 +24,23 @@ module Yap::Shell
20
24
  @blk = blk
21
25
 
22
26
  @world.editor.on_read_line do |event|
27
+ line_read = event[:payload][:line]
28
+ Treefell['shell'].puts "editor line read: #{line_read.inspect}"
23
29
  # editor.history = true?
24
- line = event[:payload][:line] << "\n"
30
+ line = line_read << "\n"
25
31
  begin
26
32
  @blk.call(line)
27
33
  @world.editor.redraw_prompt
28
- rescue Yap::Shell::Parser::Lexer::NonterminatedString, Yap::Shell::Parser::Lexer::LineContinuationFound
34
+ rescue Yap::Shell::Parser::Lexer::NonterminatedString,
35
+ Yap::Shell::Parser::Lexer::LineContinuationFound => ex
36
+ Treefell['shell'].puts "rescued #{ex}, asking user for more input"
29
37
  line << read_another_line_of_input
30
38
  retry
31
39
  rescue ::Yap::Shell::CommandUnknownError => ex
40
+ Treefell['shell'].puts "rescued #{ex}, telling user"
32
41
  puts " CommandError: #{ex.message}"
33
42
  rescue ::Yap::Shell::Parser::ParseError => ex
43
+ Treefell['shell'].puts "rescued #{ex}, telling user"
34
44
  puts " Parse error: #{ex.message}"
35
45
  ensure
36
46
  @world.editor.reset_line
@@ -213,13 +223,12 @@ module Yap::Shell
213
223
  return _input
214
224
  end
215
225
 
216
- puts "Beginning heredoc" if ENV["DEBUG"]
217
-
226
+ Treefell['shell'].puts "asking for heredoc input with @world.secondary_prompt"
218
227
  loop do
219
228
  str = editor.read(@world.secondary_prompt.update.text, false)
220
229
  input << "#{str}\n"
221
230
  if str =~ /^#{Regexp.escape(marker)}$/
222
- puts "Ending heredoc" if ENV["DEBUG"]
231
+ Treefell['shell'].puts "done asking for heredoc input"
223
232
  break
224
233
  end
225
234
  end
@@ -1,5 +1,5 @@
1
1
  module Yap
2
2
  module Shell
3
- VERSION = "0.4.0"
3
+ VERSION = "0.4.1"
4
4
  end
5
5
  end
data/lib/yap/world.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'term/ansicolor'
2
+ require 'fileutils'
2
3
  require 'forwardable'
3
4
  require 'rawline'
4
5
  require 'termios'
@@ -30,6 +31,9 @@ module Yap
30
31
  @env = ENV.to_h.dup
31
32
  dom = build_editor_dom
32
33
 
34
+ # ensure yap directory exists
35
+ FileUtils.mkdir_p configuration.yap_path
36
+
33
37
  @editor = RawLine::Editor.create(dom: dom)
34
38
 
35
39
  self.prompt = Yap::Shell::Prompt.new(text: DEFAULTS[:primary_prompt_text])