vim-update-bundles 0.6
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.
- data/CHANGES +48 -0
- data/README.md +147 -0
- data/Rakefile +5 -0
- data/test.rb +660 -0
- data/vim-update-bundles +471 -0
- metadata +74 -0
data/vim-update-bundles
ADDED
@@ -0,0 +1,471 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Reads bundles to be installed from the .vimrc file then synchronizes
|
4
|
+
# .vim/bundles by downloading new repositories as needed. It also removes
|
5
|
+
# bundles that are no longer used.
|
6
|
+
#
|
7
|
+
# Specify a bundle in the .vimrc:
|
8
|
+
# " Bundle: https://git.wincent.com/command-t.git
|
9
|
+
# Specify a refspec (branch, tag, hash) at the end:
|
10
|
+
# " Bundle: https://github.com/vim-ruby/vim-ruby.git noisy
|
11
|
+
# " Bundle: https://github.com/bronson/vim-closebuffer.git 0.2
|
12
|
+
# " Bundle: https://github.com/tpope/vim-rails.git 42bb0699
|
13
|
+
#
|
14
|
+
# If the .vim folder is stored in a Git repository, add bundles as submodules
|
15
|
+
# by putting "submodule=true" in ~/.vim-update-bundles.conf.
|
16
|
+
|
17
|
+
require 'fileutils'
|
18
|
+
require 'open-uri'
|
19
|
+
|
20
|
+
Version = '0.6'
|
21
|
+
|
22
|
+
|
23
|
+
def dotvim config, *path
|
24
|
+
# Path to files inside the .vim directory, e.g., dotvim/autoload.
|
25
|
+
File.join config[:vimdir_path], *path
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def ensure_dir dir
|
30
|
+
Dir.mkdir dir unless test ?d, dir
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def download_file url, file
|
35
|
+
open(url) do |r|
|
36
|
+
File.open(file, 'w') do |w|
|
37
|
+
w.write(r.read)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def run *cmd
|
44
|
+
# Runs cmd, returns its stdout, and bails on error.
|
45
|
+
# Mostly a backport of Ruby 1.9's IO.popen for 1.8.
|
46
|
+
options = { :acceptable_exit_codes => [0] }
|
47
|
+
options.merge!(cmd.pop) if cmd.last.kind_of?(Hash)
|
48
|
+
puts "-> #{[cmd].join(" ")}" if $verbose
|
49
|
+
outr, outw = IO::pipe
|
50
|
+
pid = fork {
|
51
|
+
outr.close; STDOUT.reopen outw; outw.close
|
52
|
+
exec *cmd.flatten.map { |c| c.to_s }
|
53
|
+
}
|
54
|
+
outw.close
|
55
|
+
result = outr.read
|
56
|
+
outr.close
|
57
|
+
Process.waitpid pid
|
58
|
+
unless options[:acceptable_exit_codes].include?($?.exitstatus)
|
59
|
+
raise "'#{[cmd].join(" ")}' in #{Dir.pwd} exited with code #{$?.exitstatus}"
|
60
|
+
end
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def git *cmd
|
66
|
+
if !$verbose && %w{checkout clone fetch pull}.include?(cmd.first.to_s)
|
67
|
+
cmd.insert 1, '-q'
|
68
|
+
end
|
69
|
+
run :git, *cmd
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def describe_head dir
|
74
|
+
Dir.chdir(dir) do
|
75
|
+
# Don't want to use 'git describe --all' because branch names change too often.
|
76
|
+
# Use `` instead of git() so we don't error out if the git call fails.
|
77
|
+
# (might happen if there's a directory in .vim/bundle that isn't git-revisioned).
|
78
|
+
version = `git describe --tags 2>/dev/null`.chomp
|
79
|
+
version = `git rev-parse HEAD 2>/dev/null`[0..12] unless version =~ /\S/
|
80
|
+
version
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def current_date
|
86
|
+
# Ruby's Time.now.to_s just doesn't produce very good output
|
87
|
+
$current_date ||= run(:date).chomp
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def print_doc_header doc
|
92
|
+
doc.printf "%-34s %s\n\n\n", "*bundles* *bundles.txt*", "Installed Bundles"
|
93
|
+
doc.puts "Lists the currently installed bundles. Also see the |bundle-log|."
|
94
|
+
doc.puts "Last updated by vim-update-bundles on #{current_date}.\n\n"
|
95
|
+
doc.printf " %-32s %-22s %s\n", "PLUGIN", "VERSION", "RELEASE DATE"
|
96
|
+
doc.puts "-" * 72
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def print_doc_entry dir, doc
|
101
|
+
version = describe_head dir
|
102
|
+
date = Dir.chdir(dir) { git(:log, '-1', '--pretty=format:%ai').chomp }
|
103
|
+
doc.printf " %-32s %-22s %s\n", "|#{dir}|", version, date.split(' ').first
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def print_log_header log
|
108
|
+
log.printf "%-34s %s\n\n\n", "*bundle-log.txt*", "Bundle Install Log"
|
109
|
+
log.puts "Logs bundle install activity. Also see the list of installed |bundles|.\n\n"
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def print_log_entry log, action, dir, rev, notes=""
|
114
|
+
log.printf " %-3s %-26s %-18s %s\n", action, "|#{dir}|", rev, notes
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def ignore_doc_tags
|
119
|
+
exclude = File.read ".git/info/exclude"
|
120
|
+
if exclude !~ /doc\/tags/
|
121
|
+
File.open(".git/info/exclude", "w") { |f|
|
122
|
+
f.write exclude.chomp + "\ndoc/tags\n"
|
123
|
+
}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def in_git_root inpath=nil
|
129
|
+
# Submodules often require the CWD to be the Git root. If a path relative to
|
130
|
+
# the CWD is passed, the block receives it relative to the root.
|
131
|
+
path = File.join Dir.pwd, inpath if inpath
|
132
|
+
Dir.chdir("./" + git('rev-parse', '--show-cdup').chomp) do
|
133
|
+
path.sub! /^#{Dir.pwd}\/?/, '' if path
|
134
|
+
yield path
|
135
|
+
end rescue nil # Git deletes the bundle dir if it's empty.
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def clone_bundle config, dir, url, tagstr, log
|
140
|
+
unless config[:submodule]
|
141
|
+
puts "cloning #{dir} from #{url}#{tagstr}"
|
142
|
+
git :clone, url, dir
|
143
|
+
else
|
144
|
+
puts "adding submodule #{dir} from #{url}#{tagstr}"
|
145
|
+
in_git_root(dir) { |mod| git :submodule, :add, url, mod }
|
146
|
+
end
|
147
|
+
print_log_entry log, 'Add', dir, describe_head(dir), "#{url}#{tagstr}"
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def remove_bundle_to config, dir, destination
|
152
|
+
puts "Erasing #{dir}, find it in #{destination}"
|
153
|
+
FileUtils.mv dir, destination
|
154
|
+
if config[:submodule]
|
155
|
+
in_git_root(dir) do |mod|
|
156
|
+
git :rm, mod
|
157
|
+
['.gitmodules', '.git/config'].each do |filename|
|
158
|
+
begin
|
159
|
+
text = File.read filename
|
160
|
+
File.open(filename, 'w+') do |file|
|
161
|
+
file.puts text.gsub(/\[submodule "#{mod}"\][^\[]+/m,'')
|
162
|
+
end
|
163
|
+
rescue
|
164
|
+
puts " Could not delete submodule entries from .gitmodules and .git/config"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def remove_bundle config, dir, log
|
173
|
+
print_log_entry log, 'Del', dir, describe_head(dir)
|
174
|
+
trash_dir = dotvim(config, "Trashed-Bundles")
|
175
|
+
ensure_dir trash_dir
|
176
|
+
1.upto(100) do |i|
|
177
|
+
destination = "#{trash_dir}/#{dir}-#{'%02d' % i}"
|
178
|
+
unless test ?d, destination
|
179
|
+
remove_bundle_to config, dir, destination
|
180
|
+
return
|
181
|
+
end
|
182
|
+
end
|
183
|
+
raise "unable to remove #{dir}, please delete #{trash_dir}"
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
def update_bundle config, dir, tag, tagstr, previous_version, log
|
188
|
+
Dir.chdir(dir) do
|
189
|
+
git :checkout, tag || :master
|
190
|
+
if system 'git symbolic-ref HEAD -q >/dev/null'
|
191
|
+
# only pull if symbolic-ref is true, otherwise it's detached head
|
192
|
+
git :pull, '--ff-only', :origin, tag || :master
|
193
|
+
end
|
194
|
+
ignore_doc_tags
|
195
|
+
end
|
196
|
+
|
197
|
+
if previous_version
|
198
|
+
new_version = describe_head dir
|
199
|
+
if new_version != previous_version
|
200
|
+
print_log_entry log, 'up', dir, previous_version, "-> #{new_version}#{tagstr}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
in_git_root(dir) { |mod| git :add, mod } if config[:submodule]
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def download_bundle config, dir, url, tag, doc, log
|
209
|
+
tagstr = " at #{tag}" if tag
|
210
|
+
previous_version = nil
|
211
|
+
only_updating = false
|
212
|
+
|
213
|
+
# fetch bundle
|
214
|
+
if test ?d, dir
|
215
|
+
remote = Dir.chdir(dir) { git(:config, '--get', 'remote.origin.url').chomp }
|
216
|
+
if remote == url
|
217
|
+
only_updating = true
|
218
|
+
unless config[:no_updates]
|
219
|
+
previous_version = describe_head dir
|
220
|
+
puts "updating #{dir} from #{url}#{tagstr}"
|
221
|
+
Dir.chdir(dir) { git :fetch }
|
222
|
+
end
|
223
|
+
else
|
224
|
+
puts "repo has changed from #{remote} to #{url}"
|
225
|
+
remove_bundle config, dir, log
|
226
|
+
ensure_dir dotvim(config, 'bundle')
|
227
|
+
clone_bundle config, dir, url, tagstr, log
|
228
|
+
end
|
229
|
+
else
|
230
|
+
clone_bundle config, dir, url, tagstr, log
|
231
|
+
end
|
232
|
+
|
233
|
+
# pull bundle
|
234
|
+
unless only_updating && config[:no_updates]
|
235
|
+
update_bundle config, dir, tag, tagstr, previous_version, log
|
236
|
+
end
|
237
|
+
|
238
|
+
print_doc_entry dir, doc
|
239
|
+
only_updating
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
def read_vimrc config
|
244
|
+
File.open(config[:vimrc_path]) do |file|
|
245
|
+
file.each_line { |line| yield line }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
class BundleCommandError < RuntimeError
|
251
|
+
def exit_code; 47; end
|
252
|
+
end
|
253
|
+
|
254
|
+
def run_bundle_command dir, cmd
|
255
|
+
puts " running: #{cmd}"
|
256
|
+
status = Dir.chdir(dir) { system(cmd); $? }
|
257
|
+
unless status.success?
|
258
|
+
raise BundleCommandError.new("BundleCommand #{cmd} in #{Dir.pwd} failed!")
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
def update_bundles config, doc, log
|
264
|
+
existing_bundles = Dir['*']
|
265
|
+
|
266
|
+
# Ignore files in the bundle directory, e.g., READMEs.
|
267
|
+
existing_bundles.reject! { |path| FileTest.file? path }
|
268
|
+
|
269
|
+
dir = only_updating = nil
|
270
|
+
puts "# reading vimrc" if config[:verbose]
|
271
|
+
read_vimrc(config) do |line|
|
272
|
+
if line =~ /^\s*"\s*bundle:\s*(.*)$/i
|
273
|
+
url, tag = $1.split
|
274
|
+
puts "# processing '#{url}' at '#{tag}'" if config[:verbose]
|
275
|
+
dir = url.split('/').last.gsub(/^vim-|\.git$/, '')
|
276
|
+
if url.match /^[A-Za-z0-9-]+\/[A-Za-z0-9._-]+$/ # User/repository.
|
277
|
+
url = "https://github.com/#{url}.git"
|
278
|
+
end
|
279
|
+
if url.match /^[A-Za-z0-9._-]+$/ # Plain repository.
|
280
|
+
url = "https://github.com/vim-scripts/#{url}.git"
|
281
|
+
end
|
282
|
+
only_updating = download_bundle config, dir, url, tag, doc, log
|
283
|
+
existing_bundles.delete dir
|
284
|
+
elsif line =~ /^\s*"\s*bundle[ -]?command:\s*(.*)$/i
|
285
|
+
# Want BundleCommand but BUNDLE COMMAND and Bundle-Command used to be legal too
|
286
|
+
raise "BundleCommand must come after Bundle" if dir.nil?
|
287
|
+
run_bundle_command dir, $1 unless only_updating && config[:no_updates]
|
288
|
+
elsif line =~ /^\s*"\s*static:\s*(.*)$/i
|
289
|
+
dir = $1
|
290
|
+
puts " leaving #{dir} alone"
|
291
|
+
existing_bundles.delete dir
|
292
|
+
end
|
293
|
+
end
|
294
|
+
existing_bundles.each { |dir| remove_bundle config, dir, log }
|
295
|
+
|
296
|
+
if config[:submodule]
|
297
|
+
in_git_root do
|
298
|
+
puts " updating submodules"
|
299
|
+
git :submodule, :init
|
300
|
+
git :submodule, :update
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
def update_bundles_and_docs config
|
307
|
+
ensure_dir dotvim(config, 'doc')
|
308
|
+
bundle_dir = dotvim(config, 'bundle')
|
309
|
+
ensure_dir bundle_dir
|
310
|
+
|
311
|
+
File.open(dotvim(config, 'doc', 'bundles.txt'), "w") do |doc|
|
312
|
+
print_doc_header doc
|
313
|
+
logfile = dotvim(config, 'doc', 'bundle-log.txt')
|
314
|
+
log_already_exists = test ?f, logfile
|
315
|
+
File.open(logfile, 'a') do |log|
|
316
|
+
print_log_header log unless log_already_exists
|
317
|
+
log.puts "Updating on #{current_date}"
|
318
|
+
begin
|
319
|
+
Dir.chdir(bundle_dir) { update_bundles config, doc, log }
|
320
|
+
rescue Exception => e
|
321
|
+
message = e.is_a?(Interrupt) ? "Interrupted" : "Aborted: #{e.message}"
|
322
|
+
log.print " #{message}\n\n" # puts suppresses trailing newline
|
323
|
+
doc.puts message
|
324
|
+
STDERR.puts message
|
325
|
+
exit e.respond_to?(:exit_code) ? e.exit_code : 1
|
326
|
+
end
|
327
|
+
log.puts
|
328
|
+
end
|
329
|
+
doc.puts
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
def interpolate options, val, message, i
|
335
|
+
raise "Interpolation is now $#{$1} instead of ENV[#{$1}] #{message} #{i}" if val =~ /ENV\[['"]?([^\]]*)['"]?\]/
|
336
|
+
STDERR.puts "WARNING: quotes in a config item are probably a mistake #{message} #{i}" if val =~ /["']/
|
337
|
+
|
338
|
+
val.gsub(/\$([A-Za-z0-9_]+)/) { options[$1.to_sym] || ENV[$1] || raise("$#{$1} is not defined #{message} #{i}") }
|
339
|
+
end
|
340
|
+
|
341
|
+
|
342
|
+
def process_options options, args, message
|
343
|
+
args.each_with_index do |arg,i|
|
344
|
+
arg = arg.gsub /^\s*-?-?|\s*$/, '' # Leading dashes in front of options are optional.
|
345
|
+
return if arg == '' || arg =~ /^#/
|
346
|
+
|
347
|
+
k,v = arg.split /\s*=\s*/, 2
|
348
|
+
k = options[k.to_sym].to_s while options[k.to_sym].is_a? Symbol # expand 1-letter options, :v -> :verbose
|
349
|
+
k.gsub! '-', '_' # underscorize args, 'no-updates' -> 'no_updates'
|
350
|
+
|
351
|
+
unless options.has_key? k.to_sym
|
352
|
+
puts "Unknown option: #{k.inspect} #{message} #{i}"
|
353
|
+
puts "Usage: #{help}" if args.equal? ARGV
|
354
|
+
exit 1
|
355
|
+
end
|
356
|
+
|
357
|
+
v = options[k.to_sym].call(v) if options[k.to_sym].is_a? Proc
|
358
|
+
options[k.to_sym] = v ? interpolate(options,v,message,i).split("'").join("\\'") : true
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
|
363
|
+
# Returns the first path that exists or the last one if nothing exists.
|
364
|
+
def choose_file *paths
|
365
|
+
paths.find { |p| test ?f, p } || paths[-1]
|
366
|
+
end
|
367
|
+
|
368
|
+
|
369
|
+
def set_default_options opts
|
370
|
+
dotfiles = File.join(ENV['HOME'], '.dotfiles')
|
371
|
+
opts[:dotfiles_path] ||= dotfiles if test(?d, dotfiles)
|
372
|
+
|
373
|
+
if opts[:dotfiles_path]
|
374
|
+
raise "#{opts[:dotfiles_path]} doesn't exist!" unless test(?d, opts[:dotfiles_path])
|
375
|
+
opts[:vimdir_path] ||= File.join(opts[:dotfiles_path], 'vim')
|
376
|
+
opts[:vimrc_path] ||= choose_file(File.join([opts[:dotfiles_path], 'vim', 'vimrc']),
|
377
|
+
File.join([opts[:dotfiles_path], 'vimrc']))
|
378
|
+
else
|
379
|
+
opts[:vimdir_path] ||= File.join(ENV['HOME'], '.vim')
|
380
|
+
opts[:vimrc_path] ||= choose_file(File.join([ENV['HOME'], '.vim', 'vimrc']),
|
381
|
+
File.join([ENV['HOME'], '.vimrc']))
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
|
386
|
+
def ensure_vim_environment config
|
387
|
+
ensure_dir dotvim(config)
|
388
|
+
ensure_dir dotvim(config, 'autoload')
|
389
|
+
|
390
|
+
unless test ?f, dotvim(config, 'autoload', 'pathogen.vim')
|
391
|
+
puts "Downloading Pathogen..."
|
392
|
+
download_file config[:pathogen_url], dotvim(config, 'autoload', 'pathogen.vim')
|
393
|
+
end
|
394
|
+
|
395
|
+
unless test(?f, config[:vimrc_path])
|
396
|
+
puts "Downloading starter vimrc..."
|
397
|
+
download_file config[:starter_url], config[:vimrc_path]
|
398
|
+
end
|
399
|
+
|
400
|
+
run :ln, '-s', config[:vimdir_path], "#{ENV['HOME']}/.vim" unless test ?e, "#{ENV['HOME']}/.vim"
|
401
|
+
run :ln, '-s', config[:vimrc_path], "#{ENV['HOME']}/.vimrc" unless test ?e, "#{ENV['HOME']}/.vimrc"
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
def generate_helptags
|
406
|
+
puts "updating helptags..."
|
407
|
+
# Vim on a Mac often exits with 1, even when doing nothing.
|
408
|
+
run :vim, '-e', '-c', 'call pathogen#helptags()', '-c', 'q', :acceptable_exit_codes => [0, 1] unless ENV['TESTING']
|
409
|
+
end
|
410
|
+
|
411
|
+
|
412
|
+
def read_configuration config
|
413
|
+
conf_file = File.join ENV['HOME'], '.vim-update-bundles.conf'
|
414
|
+
process_options config, File.open(conf_file).readlines, "in #{conf_file} line" if test(?f, conf_file)
|
415
|
+
process_options config, ARGV, "in command line argument"
|
416
|
+
|
417
|
+
set_default_options config
|
418
|
+
|
419
|
+
config.keys.sort.each { |k| puts "# option #{k} = #{config[k].inspect}" } if config[:verbose]
|
420
|
+
config.delete :dotfiles_path # Ensure it is not used accidentally later.
|
421
|
+
end
|
422
|
+
|
423
|
+
|
424
|
+
def help
|
425
|
+
<<EOL
|
426
|
+
vim-update-bundles [options...]
|
427
|
+
Updates the installed Vim plugins.
|
428
|
+
-n --no-updates: don't update bundles, only add or delete (faster)
|
429
|
+
-h -? --help: print this message
|
430
|
+
-v --verbose: print exactly what's happening
|
431
|
+
optional configurations:
|
432
|
+
-s --submodule: store bundles as git submodules
|
433
|
+
--vimdir-path: path to ~/.vim directory
|
434
|
+
--vimrc-path: path to ~/.vimrc directory
|
435
|
+
--dotfiles-path: path to your dotfiles if different than $HOME.
|
436
|
+
EOL
|
437
|
+
end
|
438
|
+
|
439
|
+
|
440
|
+
config = {
|
441
|
+
:verbose => nil, # Git commands are quiet by default; set verbose=true to see everything.
|
442
|
+
:submodule => false, # If true then use Git submodules instead of cloning.
|
443
|
+
:no_updates => false, # If true then don't update repos, only add or delete.
|
444
|
+
|
445
|
+
:help => lambda { |v| puts help; exit },
|
446
|
+
:version => lambda { |v| puts "vim-update-bundles #{Version}"; exit },
|
447
|
+
|
448
|
+
# single-character aliases for command-line options
|
449
|
+
:v => :verbose, :s => :submodule, :n => :no_updates,
|
450
|
+
:h => :help, :'?' => :help, :V => :version,
|
451
|
+
|
452
|
+
:dotfiles_path => nil, # Full path to the dot files directory.
|
453
|
+
:vimdir_path => nil, # Full path to ~/.vim (creates symlink if not in $HOME).
|
454
|
+
:vimrc_path => nil, # Full path to ~/.vimrc (creates symlink if not in $HOME).
|
455
|
+
|
456
|
+
# Used when spinning up a new Vim environment.
|
457
|
+
:starter_url => "https://github.com/bronson/dotfiles/raw/master/.vimrc",
|
458
|
+
:pathogen_url => "https://github.com/tpope/vim-pathogen/raw/master/autoload/pathogen.vim",
|
459
|
+
}
|
460
|
+
|
461
|
+
|
462
|
+
unless $load_only # to read the version number
|
463
|
+
read_configuration config
|
464
|
+
$verbose = config[:verbose]
|
465
|
+
|
466
|
+
ensure_vim_environment config
|
467
|
+
update_bundles_and_docs config
|
468
|
+
generate_helptags
|
469
|
+
puts "done! Start Vim and type ':help bundles' to see what has been installed."
|
470
|
+
end
|
471
|
+
|