yap-shell 0.4.0 → 0.4.1

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.
@@ -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])