yap-shell 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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