yap-shell 0.6.0 → 0.7.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.travis.lock +104 -0
  3. data/bin/yap +6 -0
  4. data/bin/yap-dev +37 -0
  5. data/lib/yap.rb +29 -39
  6. data/lib/yap/addon.rb +24 -0
  7. data/lib/yap/addon/base.rb +52 -0
  8. data/lib/yap/addon/export_as.rb +12 -0
  9. data/lib/yap/addon/loader.rb +84 -0
  10. data/lib/yap/addon/path.rb +56 -0
  11. data/lib/yap/addon/rc_file.rb +21 -0
  12. data/lib/yap/addon/reference.rb +22 -0
  13. data/lib/yap/cli.rb +4 -0
  14. data/lib/yap/cli/commands.rb +6 -0
  15. data/lib/yap/cli/commands/addon.rb +14 -0
  16. data/lib/yap/cli/commands/addon/disable.rb +35 -0
  17. data/lib/yap/cli/commands/addon/enable.rb +35 -0
  18. data/lib/yap/cli/commands/addon/list.rb +37 -0
  19. data/lib/yap/cli/commands/addon/search.rb +99 -0
  20. data/lib/yap/cli/commands/generate.rb +13 -0
  21. data/lib/yap/cli/commands/generate/addon.rb +258 -0
  22. data/lib/yap/cli/commands/generate/addonrb.template +22 -0
  23. data/lib/yap/cli/commands/generate/gemspec.template +25 -0
  24. data/lib/yap/cli/commands/generate/license.template +21 -0
  25. data/lib/yap/cli/commands/generate/rakefile.template +6 -0
  26. data/lib/yap/cli/commands/generate/readme.template +40 -0
  27. data/lib/yap/cli/options.rb +162 -0
  28. data/lib/yap/cli/options/addon.rb +64 -0
  29. data/lib/yap/cli/options/addon/disable.rb +62 -0
  30. data/lib/yap/cli/options/addon/enable.rb +63 -0
  31. data/lib/yap/cli/options/addon/list.rb +65 -0
  32. data/lib/yap/cli/options/addon/search.rb +76 -0
  33. data/lib/yap/cli/options/generate.rb +59 -0
  34. data/lib/yap/cli/options/generate/addon.rb +63 -0
  35. data/lib/yap/configuration.rb +10 -3
  36. data/lib/yap/gem_helper.rb +195 -0
  37. data/lib/yap/gem_tasks.rb +6 -0
  38. data/lib/yap/shell.rb +1 -1
  39. data/lib/yap/shell/repl.rb +1 -1
  40. data/lib/yap/shell/version.rb +1 -1
  41. data/lib/yap/world.rb +45 -7
  42. data/rcfiles/yaprc +90 -10
  43. data/spec/features/addons/generating_an_addon_spec.rb +55 -0
  44. data/spec/features/addons/using_an_addon_spec.rb +182 -0
  45. data/spec/features/aliases_spec.rb +6 -6
  46. data/spec/features/grouping_spec.rb +18 -18
  47. data/spec/features/line_editing_spec.rb +9 -1
  48. data/spec/features/redirection_spec.rb +12 -3
  49. data/spec/spec_helper.rb +21 -11
  50. data/spec/support/matchers/have_printed.rb +38 -0
  51. data/spec/support/yap_spec_dsl.rb +24 -6
  52. data/yap-shell.gemspec +6 -11
  53. metadata +51 -45
  54. data/addons/history/README.md +0 -16
  55. data/addons/history/history.rb +0 -58
  56. data/addons/history_search/history_search.rb +0 -197
  57. data/addons/keyboard_macros/keyboard_macros.rb +0 -425
  58. data/addons/keyboard_macros/lib/keyboard_macros/cycle.rb +0 -38
  59. data/addons/prompt/Gemfile +0 -1
  60. data/addons/prompt/right_prompt.rb +0 -17
  61. data/addons/prompt_updates/prompt_updates.rb +0 -28
  62. data/addons/tab_completion/Gemfile +0 -0
  63. data/addons/tab_completion/lib/tab_completion/basic_completion.rb +0 -151
  64. data/addons/tab_completion/lib/tab_completion/completer.rb +0 -62
  65. data/addons/tab_completion/lib/tab_completion/custom_completion.rb +0 -33
  66. data/addons/tab_completion/lib/tab_completion/dsl_methods.rb +0 -7
  67. data/addons/tab_completion/tab_completion.rb +0 -174
  68. data/lib/tasks/addons.rake +0 -97
  69. data/lib/yap/world/addons.rb +0 -181
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ require "rake/clean"
3
+ CLOBBER.include "pkg"
4
+
5
+ require "yap/gem_helper"
6
+ Yap::GemHelper.install_tasks
data/lib/yap/shell.rb CHANGED
@@ -105,7 +105,7 @@ module Yap
105
105
  if $stdout.isatty
106
106
  binding.pry
107
107
  else
108
- $stdout.puts ex.message
108
+ $stdout.puts ex.class.name
109
109
  raise ex
110
110
  end
111
111
  end
@@ -195,7 +195,7 @@ module Yap::Shell
195
195
  end
196
196
 
197
197
  def install_default_tab_completion_proc
198
- editor.completion_proc = lambda do |word|
198
+ editor.completion_proc = lambda do |word, line, word_index|
199
199
  Dir["#{word}*"].map{ |str| str.gsub(/ /, '\ ')}
200
200
  end
201
201
  end
@@ -1,5 +1,5 @@
1
1
  module Yap
2
2
  module Shell
3
- VERSION = "0.6.0"
3
+ VERSION = "0.7.0"
4
4
  end
5
5
  end
data/lib/yap/world.rb CHANGED
@@ -5,9 +5,9 @@ require 'rawline'
5
5
  require 'termios'
6
6
 
7
7
  module Yap
8
+ require 'yap/addon'
8
9
  require 'yap/shell/execution'
9
10
  require 'yap/shell/prompt'
10
- require 'yap/world/addons'
11
11
 
12
12
  class World
13
13
  include Term::ANSIColor
@@ -64,13 +64,51 @@ module Yap
64
64
 
65
65
  @repl = Yap::Shell::Repl.new(world:self)
66
66
 
67
- @addons = addons.reduce(Hash.new) do |hsh, addon|
68
- hsh[addon.addon_name] = addon
69
- hsh
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
70
81
  end
82
+ end
83
+
84
+ def addon_initialized?(addon)
85
+ (@addons_initialized ||= []).include?(addon)
86
+ end
71
87
 
72
- # initialize after they are all loaded in case they reference each other.
73
- addons.each { |addon| addon.initialize_world(self) }
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
74
112
  end
75
113
 
76
114
  def configuration
@@ -108,7 +146,7 @@ module Yap
108
146
  end
109
147
 
110
148
  def reload!
111
- exec File.expand_path($0)
149
+ exec configuration.yap_binpath
112
150
  end
113
151
 
114
152
  def func(name, &blk)
data/rcfiles/yaprc CHANGED
@@ -1,5 +1,81 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
+ old_world_envs = []
4
+ parse_env_vars_from_string = -> (str) {
5
+ keys_and_values = str.split("\n").map(&:chomp).map do |line|
6
+ next if line =~ /^\s*#/
7
+ line.split('=', 2)
8
+ end.compact
9
+
10
+ keys_and_values.map! do |(key, value)|
11
+ if value =~ /^['"]/
12
+ [key, value.scan(/^['"](.*)['"]\s*$/).flatten.first]
13
+ else
14
+ [key, value]
15
+ end
16
+ end
17
+ }
18
+
19
+ load_dotenv_file = -> (file) {
20
+ dotenv_file = Dir[file].first
21
+ if dotenv_file
22
+ old_world_envs.push world.env.dup
23
+ Treefell['shell'].puts "loading dotenv file #{dotenv_file}"
24
+ keys_and_values = parse_env_vars_from_string.call(IO.read(dotenv_file))
25
+
26
+ keys_and_values.each do |(key, value)|
27
+ next unless key
28
+ next if key == 'RAILS_ENV'
29
+ world.env[key] = value
30
+ end
31
+ Treefell['shell'].puts "done loading dotenv"
32
+ end
33
+ }
34
+
35
+
36
+ Yap::Shell::Execution::Context.on(:after_execute) do |world, command:, **kwargs|
37
+ if command.str == "nvm"
38
+ old_nvm_bin = world.env["NVM_BIN"]
39
+ Dir[ world.configuration.path_for("env/nvm.env") ].each do |file|
40
+ if old_nvm_bin
41
+ world.env["PATH"].sub!(/#{Regexp.escape(old_nvm_bin)}:/, '')
42
+ end
43
+
44
+ key_and_values = parse_env_vars_from_string.call(IO.read(file))
45
+ key_and_values.each do |(key, value)|
46
+ world.env[key] = value
47
+ end
48
+
49
+ nvm_bin = world.env["NVM_BIN"]
50
+ if nvm_bin
51
+ world.env["PATH"] = nvm_bin + ":" + world.env["PATH"]
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ Yap::Shell::Execution::Context.on(:before_execute) do |world, command:|
58
+ if command.str == "cd"
59
+ if old_world_envs.any?
60
+ world.env.replace(old_world_envs.first)
61
+ old_world_envs.clear
62
+ end
63
+ end
64
+ end
65
+
66
+ Yap::Shell::Execution::Context.on(:after_execute) do |world, command:, result:|
67
+ if command.str == "cd"
68
+ load_dotenv_file.call('.env')
69
+ load_dotenv_file.call('.env.local')
70
+ rails_env = world.env['RAILS_ENV']
71
+ if rails_env
72
+ load_dotenv_file.call(".env.#{rails_env}")
73
+ else
74
+ load_dotenv_file.call(".env.development")
75
+ end
76
+ end
77
+ end
78
+
3
79
  ###############################################################################
4
80
  # ENVIRONMENT VARIABLES
5
81
  #------------------------------------------------------------------------------
@@ -87,6 +163,7 @@ world.prompt = -> do
87
163
  # Example: ~/source/playground/yap master ➜
88
164
  "#{yellow(pwd)} #{git_branch}#{red('➜')} "
89
165
  end
166
+ # world.prompt = world.addons[:zztop].prompt
90
167
 
91
168
  ###############################################################################
92
169
  # SECONDARY PROMPT
@@ -120,7 +197,7 @@ self.secondary_prompt = '> '
120
197
  #
121
198
  # == Example
122
199
  #
123
- # world.addons[:keyboard_macros].configure(trigger_key: :ctrl_g) do |macros|
200
+ # world.addons[:'keyboard-macros'].configure(trigger_key: :ctrl_g) do |macros|
124
201
  # macros.define :z, 'git open-pull'
125
202
  # macros.define 'l', "git log -n1\n"
126
203
  # end
@@ -133,20 +210,20 @@ self.secondary_prompt = '> '
133
210
  ###############################################################################
134
211
 
135
212
  # Sets the default trigger key for all keyboard macros
136
- world.addons[:keyboard_macros].trigger_key = ?\C-g
213
+ world.addons[:'keyboard-macros'].trigger_key = ?\C-g
137
214
 
138
215
  # Sets the default cancel key (space) for all keyboard macros
139
- world.addons[:keyboard_macros].cancel_key = " "
216
+ world.addons[:'keyboard-macros'].cancel_key = " "
140
217
 
141
218
  # Sets the default timeout for macros. When set to nil you will have to
142
219
  # use the cancel key to exit out of macros.
143
- world.addons[:keyboard_macros].timeout_in_ms = nil
220
+ world.addons[:'keyboard-macros'].timeout_in_ms = nil
144
221
 
145
222
  # Forgiveness-mode: Automatically cancel if the sequence is unknown. When
146
223
  # set to false you can keep attempting to type in your macro.
147
- world.addons[:keyboard_macros].cancel_on_unknown_sequences = true
224
+ world.addons[:'keyboard-macros'].cancel_on_unknown_sequences = true
148
225
 
149
- keyboard_macros = world.addons[:keyboard_macros]
226
+ keyboard_macros = world.addons[:'keyboard-macros']
150
227
  keyboard_macros.cycle(:recent_git_branches) { `git recent`.lines.map(&:chomp) }
151
228
  world.editor.bind(:alt_up_arrow) do
152
229
  keyboard_macros.cycle(:recent_git_branches).next
@@ -156,12 +233,15 @@ world.editor.bind(:alt_down_arrow) do
156
233
  end
157
234
 
158
235
  world.editor.bind(:ctrl_r) do
159
- world.addons[:history_search].prompt_user_to_search
236
+ world.addons[:'history-search'].prompt_user_to_search
160
237
  end
161
238
 
239
+ world.editor.bind(:up_arrow) { world.addons[:history].back }
240
+ world.editor.bind(:down_arrow) { world.addons[:history].forward }
241
+
162
242
  # Or, you can set the trigger key for a particular set of macros
163
243
  # by specifying it when you call .configure(...).
164
- world.addons[:keyboard_macros].configure(trigger_key: ?\C-g) do |macro|
244
+ world.addons[:'keyboard-macros'].configure(trigger_key: ?\C-g) do |macro|
165
245
  macro.start do
166
246
  # TODO: FUTURE
167
247
  # world.editor.content_box.children = [
@@ -205,7 +285,7 @@ end
205
285
 
206
286
  # The below macro shows that you can have macros start with a different
207
287
  # trigger keys.
208
- # world.addons[:keyboard_macros].configure(trigger_key: :ctrl_h) do |macros|
288
+ # world.addons[:'keyboard-macros'].configure(trigger_key: :ctrl_h) do |macros|
209
289
  # macros.define 'h123', -> {
210
290
  # box = TerminalLayout::Box.new(content: "Right?", style: { display: :block, float: :right, height: 1, width: 50 })
211
291
  # world.editor.content_box.children = [box]
@@ -267,7 +347,7 @@ func :'run-modified-specs' do |stdin:, stdout:|
267
347
  specs.concat Dir["spec/**/*#{filename_without_extension}_spec.rb"]
268
348
  end
269
349
 
270
- cmd = "bundle exec rspec #{specs.join(' ')}"
350
+ cmd = "bundle exec rspec #{specs.sort.uniq.join(' ')}"
271
351
  stdout.puts cmd
272
352
  shell cmd
273
353
  end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Generating an addon', type: :feature, repl: false do
4
+ let(:addons_path) { tmp_dir.join('addons/') }
5
+ let(:yaprc_path) { tmp_dir.join('yaprc') }
6
+ let(:yaprc_contents) { '' }
7
+
8
+ let(:yap_cli_args) do
9
+ [
10
+ 'generate', 'addon', 'foo-bar'
11
+ ]
12
+ end
13
+
14
+ before do
15
+ set_yap_command_line_arguments yap_cli_args
16
+
17
+ turn_on_debug_log(debug: 'editor')
18
+
19
+ reinitialize_shell
20
+ end
21
+
22
+ it 'generates an addon in the current working directory' do
23
+ # foo-addon is a shell function added by the foo-addon defined above;
24
+ expect { output }.to have_printed_lines <<-TEXT.gsub(/^\s*\|/, '')
25
+ |Creating addon foo-bar in yap-shell-addon-foo-bar/
26
+ |
27
+ |Create directory: yap-shell-addon-foo-bar done
28
+ |Create directory: lib done
29
+ |Creating file: Gemfile done
30
+ |Creating file: yap-shell-addon-foo-bar.gemspec done
31
+ |Creating file: LICENSE.txt done
32
+ |Creating file: Rakefile done
33
+ |Creating file: README.md done
34
+ |Creating file: lib/yap-shell-addon-foo-bar.rb done
35
+ |Create directory: lib/yap-shell-addon-foo-bar done
36
+ |Creating file: lib/yap-shell-addon-foo-bar/version.rb done
37
+ |
38
+ |Creating file: .gitignore done
39
+ |git init . && git add . && git commit -m 'initial commit of foo-bar' done
40
+ |
41
+ |Yap addon generated! A few helpful things to note:
42
+ |
43
+ | * The foo-bar addon has been generated in yap-shell-addon-foo-bar/
44
+ | * It is a standard rubygem, has its own gemspec, and is named yap-shell-addon-foo-bar
45
+ | * Yap loads the YapShellAddonFooBar, found in lib/yap-shell-addon-foo-bar.rb (start there)
46
+ | * Share your addon with others by building a gem and pushing it to rubygems
47
+
48
+ |For more information see https://github.com/zdennis/yap-shell/wiki/Addons
49
+ |
50
+ |Now, to get started:
51
+ |
52
+ | cd yap-shell-addon-foo-bar
53
+ TEXT
54
+ end
55
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Using an addon', type: :feature do
4
+ let(:addons_path) { tmp_dir.join('addons/') }
5
+ let(:yaprc_path) { tmp_dir.join('yaprc') }
6
+ let(:yaprc_contents) { '' }
7
+
8
+ let(:foo_addon_path) { addons_path.join('yap-shell-addon-foo-0.1.0') }
9
+ let(:foo_lib_addon_path) { foo_addon_path.join('lib') }
10
+ let(:foo_addon_rb_path) { foo_lib_addon_path.join('yap-shell-addon-foo.rb') }
11
+ let(:create_foo_addon) do
12
+ mkdir_p foo_lib_addon_path.to_s
13
+ write_file foo_addon_rb_path.to_s, <<-RUBY.strip_heredoc
14
+ |module YapShellAddonFoo
15
+ | class Addon < ::Yap::Addon::Base
16
+ | self.export_as :foo
17
+ |
18
+ | def initialize_world(world)
19
+ | world.func('foo-addon') do |args:, stdout:|
20
+ | stdout.puts \"You passed \#\{args.inspect\} to foo-addon\"
21
+ | end
22
+ | end
23
+ | end
24
+ |end
25
+ RUBY
26
+ end
27
+
28
+ let(:bar_addon_path) { addons_path.join('yap-shell-addon-bar-0.1.0') }
29
+ let(:bar_lib_addon_path) { bar_addon_path.join('lib') }
30
+ let(:bar_addon_rb_path) { bar_lib_addon_path.join('yap-shell-addon-bar.rb') }
31
+ let(:create_bar_addon) do
32
+ mkdir_p bar_lib_addon_path.to_s
33
+ write_file bar_addon_rb_path.to_s, <<-RUBY.strip_heredoc
34
+ |module YapShellAddonBar
35
+ | class Addon < ::Yap::Addon::Base
36
+ | self.export_as :bar
37
+ |
38
+ | def initialize_world(world)
39
+ | world.func('bar-addon') do |args:, stdout:|
40
+ | stdout.puts \"You passed \#\{args.inspect\} to bar-addon\"
41
+ | end
42
+ | end
43
+ | end
44
+ |end
45
+ RUBY
46
+ end
47
+
48
+ let(:create_yaprc_file) do
49
+ write_file yaprc_path.to_s, <<-RUBY.strip_heredoc
50
+ |#{yaprc_contents}
51
+ RUBY
52
+ end
53
+
54
+ let(:yap_cli_args) do
55
+ [
56
+ '--addon-paths', addons_path.to_s,
57
+ '--rcfiles', yaprc_path.to_s,
58
+ '--no-history',
59
+ '--no-rcfiles',
60
+ '--skip-first-time'
61
+ ]
62
+ end
63
+
64
+ before do
65
+ set_yap_command_line_arguments yap_cli_args
66
+
67
+ create_foo_addon
68
+ create_bar_addon
69
+ create_yaprc_file
70
+
71
+ turn_on_debug_log(debug: 'editor')
72
+ reinitialize_shell
73
+ end
74
+
75
+ it 'loads addons it finds in addon paths' do
76
+ # foo-addon is a shell function added by the foo-addon defined above
77
+ type 'foo-addon hello world'
78
+ enter
79
+
80
+ expect { output }.to have_printed("You passed [\"hello\", \"world\"] to foo-addon")
81
+ end
82
+
83
+ it 'makes addons available thru its export_as name' do
84
+ type '!addons.keys.include?(:foo)'
85
+ enter
86
+ expect { output }.to have_printed('true')
87
+ clear_all_output
88
+
89
+ type '!addons.keys.include?(:non_existent_addon)'
90
+ enter
91
+ expect { output }.to have_printed('false')
92
+ end
93
+
94
+ describe 'disabling an addon', repl: false do
95
+ let(:yap_cli_args) do
96
+ [
97
+ '--addon-paths', addons_path.to_s,
98
+ 'addon', 'disable', 'foo'
99
+ ]
100
+ end
101
+
102
+ it 'writes to disk that the addon has been disabled' do
103
+ expect { output }.to have_printed("Addon foo has been disabled")
104
+
105
+ expect(File.exists?(tmp_dir.join('.yap/addons.yml'))).to be(true)
106
+
107
+ addons_config_hsh = YAML.load_file(tmp_dir.join('.yap/addons.yml'))
108
+ expect(addons_config_hsh[:foo]).to include(disabled: true)
109
+ end
110
+ end
111
+
112
+ describe 'enabling an addon', repl: false do
113
+ let(:yap_cli_args) do
114
+ [
115
+ '--addon-paths', addons_path.to_s,
116
+ 'addon', 'enable', 'foo'
117
+ ]
118
+ end
119
+
120
+ it 'writes to disk that the addon has been enabled' do
121
+ expect { output }.to have_printed("Addon foo has been enabled")
122
+
123
+ expect(File.exists?(tmp_dir.join('.yap/addons.yml'))).to be(true)
124
+
125
+ addons_config_hsh = YAML.load_file(tmp_dir.join('.yap/addons.yml'))
126
+ expect(addons_config_hsh[:foo]).to include(disabled: false)
127
+ end
128
+ end
129
+
130
+ describe 'listing addons', repl: false do
131
+ let(:yap_cli_args) do
132
+ [
133
+ '--addon-paths', addons_path.to_s,
134
+ 'addon', 'list'
135
+ ]
136
+ end
137
+
138
+ before do
139
+ mkdir tmp_dir.join('.yap')
140
+ write_file(
141
+ tmp_dir.join('.yap/addons.yml'),
142
+ {
143
+ bar: { disabled: true },
144
+ foo: { disabled: false }
145
+ }.to_yaml
146
+ )
147
+ reinitialize_shell
148
+ end
149
+
150
+ it 'lists all addons found in the addon-paths' do
151
+ expect { output }.to have_printed(/bar.*foo/m)
152
+ end
153
+
154
+ describe 'enabled' do
155
+ let(:yap_cli_args) do
156
+ [
157
+ '--addon-paths', addons_path.to_s,
158
+ 'addon', 'list', '--enabled'
159
+ ]
160
+ end
161
+
162
+ it 'lists enabled addons' do
163
+ expect { output }.to have_printed(/foo/m)
164
+ expect { output }.to have_not_printed(/bar/m)
165
+ end
166
+ end
167
+
168
+ describe 'disabled' do
169
+ let(:yap_cli_args) do
170
+ [
171
+ '--addon-paths', addons_path.to_s,
172
+ 'addon', 'list', '--disabled'
173
+ ]
174
+ end
175
+
176
+ it 'lists disabled addons' do
177
+ expect { output }.to have_printed(/bar/m)
178
+ expect { output }.to have_not_printed(/foo/m)
179
+ end
180
+ end
181
+ end
182
+ end