vim-flavor 0.0.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vim-flavor.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Kana Natsuno
2
+
3
+ So-called MIT/X License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.asciidoc ADDED
@@ -0,0 +1,142 @@
1
+ = vim-flavor: a tool to manage your favorite Vim plugins
2
+
3
+
4
+
5
+
6
+ == Set up the utility for a new-style plugin management
7
+
8
+ === Requirements
9
+
10
+ * Git 1.7.9 or later
11
+ * Ruby 1.9.2 or later
12
+ * Vim 7.3 or later
13
+
14
+
15
+ === Supported platforms
16
+
17
+ * Unix-like environments such as Linux, Mac OS X, etc.
18
+ * Though Microsoft Windows is not directly supported,
19
+ it is possible to manage Vim plugins via Cygwin or other environments.
20
+
21
+
22
+ === Installation steps
23
+
24
+ ----
25
+ gem install vim-flavor
26
+ ----
27
+
28
+
29
+
30
+
31
+ == Start using vim-flavor
32
+
33
+ ----
34
+ cd $YOUR_REPOSITORY_FOR_DOTFILES
35
+
36
+ cat >VimFlavor <<'END'
37
+ # * Declare using git://github.com/kana/vim-textobj-indent.git
38
+ # * vim-flavor fetches a plugin from git://github.com/$USER/$REPO.git
39
+ # if the argument is written in '$USER/$REPO' format.
40
+ # * kana/vim-textobj-indent requires kana/vim-textobj-user.
41
+ # Such dependencies are automatically installed
42
+ # if the flavored plugin declares its dependencies with VimFlavor file.
43
+ # (FIXME: Resolving dependencies will be implemented later.)
44
+ flavor 'kana/vim-textobj-indent'
45
+
46
+ # * Declare using git://github.com/vim-scripts/fakeclip.git
47
+ # * vim-flavor fetches a plugin from git://github.com/vim-scripts/$REPO.git
48
+ # if the argument is written in '$REPO' format.
49
+ flavor 'fakeclip'
50
+
51
+ # * Declare using git://github.com/kana/vim-altr.git
52
+ # * vim-flavor fetches a plugin from the URI
53
+ # if the argument seems to be a URI.
54
+ flavor 'git://github.com/kana/vim-altr.git'
55
+
56
+ # * Declare using kana/vim-smartchr 0.1.0 or later and older than 0.2.0.
57
+ flavor 'kana/vim-smartchr', '~> 0.1.0'
58
+
59
+ # * Declare using kana/vim-smartword 0.1 or later and older than 1.0.
60
+ flavor 'kana/vim-smartword', '~> 0.1'
61
+
62
+ # * Declare using kana/vim-smarttill 0.1.0 or later.
63
+ flavor 'kana/vim-smarttill', '>= 0.1.0'
64
+ END
65
+
66
+ # Fetch the plugins declared in the VimFlavor,
67
+ # create VimFlavor.lock for a snapshot of all plugins and versions,
68
+ # then install the plugins and a bootstrap script into ~/.vim etc.
69
+ vim-flavor install
70
+
71
+ # Add the following line into the first line of your vimrc:
72
+ #
73
+ # runtime flavors/bootstrap.vim
74
+ vim vimrc
75
+
76
+ git add VimFlavor VimFlavor.lock vimrc
77
+ git commit -m 'Use vim-flavor to manage my favorite Vim plugins'
78
+ ----
79
+
80
+
81
+
82
+
83
+ == Upgrade all plugins to the latest version
84
+
85
+ ----
86
+ vim-flavor upgrade
87
+
88
+ git add VimFlavor.lock
89
+ git commit -m 'Upgrade my favorite Vim plugins'
90
+ ----
91
+
92
+
93
+
94
+
95
+ == Add more plugins into your dotfile repository
96
+
97
+ ----
98
+ cat >>VimFlavor <<'END'
99
+
100
+ flavor 'kana/vim-operator-replace'
101
+
102
+ END
103
+
104
+ # Fetch newly added plugins,
105
+ # update VimFlavor.lock for the plugins,
106
+ # then install the plugins into ~/.vim etc.
107
+ vim-flavor install
108
+
109
+ git add VimFlavor VimFlavor.lock
110
+ git commit -m 'Use kana/vim-operator-replace'
111
+ ----
112
+
113
+
114
+
115
+
116
+ == Remove plugins from your dotfile repository
117
+
118
+ ----
119
+ # Remove declarations of unused plugins from VimFlavor.
120
+ sed -i~ -e '/vim-smartchr/d' VimFlavor
121
+
122
+ # Update VimFlavor.lock for the removed plugins,
123
+ # then clean up the plugins from ~/.vim etc.
124
+ vim-flavor install
125
+
126
+ git add VimFlavor VimFlavor.lock
127
+ git commit -m 'Farewell kana/vim-smartchr'
128
+ ----
129
+
130
+
131
+
132
+
133
+ == Install plugins into a non-standard directory
134
+
135
+ ----
136
+ vim-flavor install --vimfiles-path=/cygdrive/c/Users/kana/vimfiles
137
+ ----
138
+
139
+
140
+
141
+
142
+ // vim: filetype=asciidoc
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/vim-flavor ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'vim-flavor'
3
+ Vim::Flavor::CLI.start()
data/lib/vim-flavor.rb ADDED
@@ -0,0 +1,471 @@
1
+ require 'bundler/setup'
2
+ require 'fileutils'
3
+ require 'thor'
4
+ require 'vim-flavor/version'
5
+ require 'yaml'
6
+
7
+ module Vim
8
+ module Flavor
9
+ module StringExtension
10
+ def to_flavors_path()
11
+ "#{self}/flavors"
12
+ end
13
+ end
14
+
15
+ class ::String
16
+ include StringExtension
17
+ end
18
+
19
+ class VersionConstraint
20
+ attr_reader :base_version, :operator
21
+
22
+ def initialize(s)
23
+ @base_version, @operator = parse(s)
24
+ end
25
+
26
+ def to_s()
27
+ "#{@operator} #{@base_version}"
28
+ end
29
+
30
+ def ==(other)
31
+ self.base_version == other.base_version &&
32
+ self.operator == other.operator
33
+ end
34
+
35
+ def parse(s)
36
+ m = /^\s*(>=|~>)\s+(\S+)$/.match(s)
37
+ if m then
38
+ [Gem::Version.create(m[2]), m[1]]
39
+ else
40
+ raise "Invalid version constraint: #{s.inspect}"
41
+ end
42
+ end
43
+
44
+ def compatible?(other_version_or_s)
45
+ v = Gem::Version.create(other_version_or_s)
46
+ if @operator == '~>' then
47
+ self.base_version.bump() > v and v >= self.base_version
48
+ elsif @operator == '>=' then
49
+ v >= self.base_version
50
+ else
51
+ raise NotImplementedError
52
+ end
53
+ end
54
+
55
+ def find_the_best_version(versions)
56
+ versions.
57
+ select {|v| compatible?(v)}.
58
+ sort().
59
+ reverse().
60
+ first
61
+ end
62
+ end
63
+
64
+ DOT_PATH = "#{Dir.getwd()}/.vim-flavor"
65
+ CACHED_REPOS_PATH = "#{DOT_PATH}/repos"
66
+
67
+ class Flavor
68
+ @@properties = [
69
+ :groups,
70
+ :locked_version,
71
+ :repo_name,
72
+ :repo_uri,
73
+ :version_contraint,
74
+ ]
75
+
76
+ @@properties.each do |p|
77
+ attr_accessor p
78
+ end
79
+
80
+ def initialize()
81
+ @groups = []
82
+ end
83
+
84
+ def ==(other)
85
+ return false if self.class != other.class
86
+ @@properties.all? do |p|
87
+ self.send(p) == other.send(p)
88
+ end
89
+ end
90
+
91
+ def zapped_repo_dir_name
92
+ @repo_name.gsub(/[^A-Za-z0-9._-]/, '_')
93
+ end
94
+
95
+ def cached_repo_path
96
+ @cached_repo_path ||=
97
+ "#{CACHED_REPOS_PATH}/#{zapped_repo_dir_name}"
98
+ end
99
+
100
+ def make_deploy_path(vimfiles_path)
101
+ "#{vimfiles_path.to_flavors_path()}/#{zapped_repo_dir_name}"
102
+ end
103
+
104
+ def clone()
105
+ message = %x[
106
+ {
107
+ git clone '#{@repo_uri}' '#{cached_repo_path}'
108
+ } 2>&1
109
+ ]
110
+ if $? != 0 then
111
+ raise RuntimeError, message
112
+ end
113
+ true
114
+ end
115
+
116
+ def fetch()
117
+ message = %x[
118
+ {
119
+ cd #{cached_repo_path.inspect} &&
120
+ git fetch origin
121
+ } 2>&1
122
+ ]
123
+ if $? != 0 then
124
+ raise RuntimeError, message
125
+ end
126
+ end
127
+
128
+ def deploy(vimfiles_path)
129
+ deploy_path = make_deploy_path(vimfiles_path)
130
+ message = %x[
131
+ {
132
+ cd '#{cached_repo_path}' &&
133
+ git checkout -f #{locked_version.inspect} &&
134
+ git checkout-index -a -f --prefix='#{deploy_path}/' &&
135
+ vim -u NONE -i NONE -n -N -e -c 'helptags #{deploy_path}/doc | qall!'
136
+ } 2>&1
137
+ ]
138
+ if $? != 0 then
139
+ raise RuntimeError, message
140
+ end
141
+ end
142
+
143
+ def undeploy(vimfiles_path)
144
+ deploy_path = make_deploy_path(vimfiles_path)
145
+ message = %x[
146
+ {
147
+ rm -fr '#{deploy_path}'
148
+ } 2>&1
149
+ ]
150
+ if $? != 0 then
151
+ raise RuntimeError, message
152
+ end
153
+ end
154
+
155
+ def list_versions()
156
+ tags = %x[
157
+ {
158
+ cd '#{cached_repo_path}' &&
159
+ git tag
160
+ } 2>&1
161
+ ]
162
+ if $? != 0 then
163
+ raise RuntimeError, message
164
+ end
165
+
166
+ tags.
167
+ split(/[\r\n]/).
168
+ select {|t| t != ''}.
169
+ map {|t| Gem::Version.create(t)}
170
+ end
171
+
172
+ def update_locked_version()
173
+ @locked_version =
174
+ version_contraint.find_the_best_version(list_versions())
175
+ end
176
+ end
177
+
178
+ class FlavorFile
179
+ attr_reader :flavors
180
+
181
+ def initialize()
182
+ @flavors = {}
183
+ @default_groups = [:default]
184
+ end
185
+
186
+ def interpret(&block)
187
+ instance_eval(&block)
188
+ end
189
+
190
+ def eval_flavorfile(flavorfile_path)
191
+ content = File.open(flavorfile_path, 'rb') do |f|
192
+ f.read()
193
+ end
194
+ interpret do
195
+ instance_eval(content)
196
+ end
197
+ end
198
+
199
+ def repo_uri_from_repo_name(repo_name)
200
+ if /^([^\/]+)$/.match(repo_name) then
201
+ m = Regexp.last_match
202
+ "git://github.com/vim-scripts/#{m[1]}.git"
203
+ elsif /^([A-Za-z0-9_-]+)\/(.*)$/.match(repo_name) then
204
+ m = Regexp.last_match
205
+ "git://github.com/#{m[1]}/#{m[2]}.git"
206
+ elsif /^[a-z]+:\/\/.*$/.match(repo_name) then
207
+ repo_name
208
+ else
209
+ raise "repo_name is written in invalid format: #{repo_name.inspect}"
210
+ end
211
+ end
212
+
213
+ def flavor(repo_name, *args)
214
+ options = Hash === args.last ? args.pop : {}
215
+ options[:groups] ||= []
216
+ version_contraint = VersionConstraint.new(args.last || '>= 0')
217
+
218
+ f = Flavor.new()
219
+ f.repo_name = repo_name
220
+ f.repo_uri = repo_uri_from_repo_name(repo_name)
221
+ f.version_contraint = version_contraint
222
+ f.groups = @default_groups + options[:groups]
223
+
224
+ @flavors[f.repo_uri] = f
225
+ end
226
+
227
+ def group(*group_names, &block)
228
+ @default_groups.concat(group_names)
229
+ yield
230
+ ensure
231
+ group_names.each do
232
+ @default_groups.pop()
233
+ end
234
+ end
235
+ end
236
+
237
+ class LockFile
238
+ # TODO: Resolve dependencies recursively.
239
+
240
+ attr_reader :flavors, :path
241
+
242
+ def initialize(path)
243
+ @flavors = {} # repo_uri => flavor
244
+ @path = path
245
+ end
246
+
247
+ def load()
248
+ h = File.open(@path, 'rb') do |f|
249
+ YAML.load(f.read())
250
+ end
251
+
252
+ @flavors = self.class.flavors_from_poro(h[:flavors])
253
+ end
254
+
255
+ def save()
256
+ h = {}
257
+
258
+ h[:flavors] = self.class.poro_from_flavors(@flavors)
259
+
260
+ File.open(@path, 'wb') do |f|
261
+ YAML.dump(h, f)
262
+ end
263
+ end
264
+
265
+ def self.poro_from_flavors(flavors)
266
+ Hash[
267
+ flavors.values.map {|f|
268
+ [
269
+ f.repo_uri,
270
+ {
271
+ :groups => f.groups,
272
+ :locked_version => f.locked_version.to_s(),
273
+ :repo_name => f.repo_name,
274
+ :version_contraint => f.version_contraint.to_s(),
275
+ }
276
+ ]
277
+ }
278
+ ]
279
+ end
280
+
281
+ def self.flavors_from_poro(poro)
282
+ Hash[
283
+ poro.to_a().map {|repo_uri, h|
284
+ f = Flavor.new()
285
+ f.groups = h[:groups]
286
+ f.locked_version = Gem::Version.create(h[:locked_version])
287
+ f.repo_name = h[:repo_name]
288
+ f.repo_uri = repo_uri
289
+ f.version_contraint = VersionConstraint.new(h[:version_contraint])
290
+ [f.repo_uri, f]
291
+ }
292
+ ]
293
+ end
294
+ end
295
+
296
+ class Facade
297
+ attr_reader :flavorfile
298
+ attr_accessor :flavorfile_path
299
+ attr_reader :lockfile
300
+ attr_accessor :lockfile_path
301
+ attr_accessor :traced
302
+
303
+ def initialize()
304
+ @flavorfile = nil # FlavorFile
305
+ @flavorfile_path = "#{Dir.getwd()}/VimFlavor"
306
+ @lockfile = nil # LockFile
307
+ @lockfile_path = "#{Dir.getwd()}/VimFlavor.lock"
308
+ @traced = false
309
+ end
310
+
311
+ def trace(message)
312
+ print(message) if @traced
313
+ end
314
+
315
+ def load()
316
+ @flavorfile = FlavorFile.new()
317
+ @flavorfile.eval_flavorfile(@flavorfile_path)
318
+
319
+ @lockfile = LockFile.new(@lockfile_path)
320
+ @lockfile.load() if File.exists?(@lockfile_path)
321
+ end
322
+
323
+ def make_new_flavors(current_flavors, locked_flavors, mode)
324
+ new_flavors = {}
325
+
326
+ current_flavors.each do |repo_uri, cf|
327
+ lf = locked_flavors[repo_uri]
328
+ nf = cf.dup()
329
+
330
+ nf.locked_version =
331
+ if (not lf) or
332
+ cf.version_contraint != lf.version_contraint or
333
+ mode == :update then
334
+ cf.locked_version
335
+ else
336
+ lf.locked_version
337
+ end
338
+
339
+ new_flavors[repo_uri] = nf
340
+ end
341
+
342
+ new_flavors
343
+ end
344
+
345
+ def create_vim_script_for_bootstrap(vimfiles_path)
346
+ bootstrap_path = "#{vimfiles_path.to_flavors_path()}/bootstrap.vim"
347
+ FileUtils.mkdir_p(File.dirname(bootstrap_path))
348
+ File.open(bootstrap_path, 'w') do |f|
349
+ f.write(<<-'END')
350
+ function! s:bootstrap()
351
+ let current_rtp = &runtimepath
352
+ let current_rtps = split(current_rtp, ',')
353
+ set runtimepath&
354
+ let default_rtp = &runtimepath
355
+ let default_rtps = split(default_rtp, ',')
356
+ let user_dir = default_rtps[0]
357
+ let user_after_dir = default_rtps[-1]
358
+ let base_rtps =
359
+ \ filter(copy(current_rtps),
360
+ \ 'v:val !=# user_dir && v:val !=# user_after_dir')
361
+ let flavor_dirs =
362
+ \ filter(split(glob(user_dir . '/flavors/*'), '\n'),
363
+ \ 'isdirectory(v:val)')
364
+ let new_rtps =
365
+ \ []
366
+ \ + [user_dir]
367
+ \ + flavor_dirs
368
+ \ + base_rtps
369
+ \ + map(reverse(copy(flavor_dirs)), 'v:val . "/after"')
370
+ \ + [user_after_dir]
371
+ let &runtimepath = join(new_rtps, ',')
372
+ endfunction
373
+
374
+ call s:bootstrap()
375
+ END
376
+ end
377
+ end
378
+
379
+ def deploy_flavors(flavor_list, vimfiles_path)
380
+ FileUtils.rm_rf(
381
+ ["#{vimfiles_path.to_flavors_path()}"],
382
+ :secure => true
383
+ )
384
+
385
+ create_vim_script_for_bootstrap(vimfiles_path)
386
+ flavor_list.each do |f|
387
+ trace("Deploying #{f.repo_name} (#{f.locked_version})\n")
388
+ f.deploy(vimfiles_path)
389
+ end
390
+ end
391
+
392
+ def save_lockfile()
393
+ @lockfile.save()
394
+ end
395
+
396
+ def complete_locked_flavors(mode)
397
+ nfs = {}
398
+ @flavorfile.flavors.each do |repo_uri, cf|
399
+ nf = cf.dup()
400
+ lf = @lockfile.flavors[repo_uri]
401
+
402
+ trace("Using #{nf.repo_name} ... ")
403
+ begin
404
+ if not File.exists?(nf.cached_repo_path)
405
+ nf.clone()
406
+ end
407
+
408
+ if mode == :upgrade_all or
409
+ (not lf) or
410
+ nf.version_contraint != lf.version_contraint then
411
+ nf.fetch()
412
+ nf.update_locked_version()
413
+ else
414
+ nf.locked_version = lf.locked_version
415
+ end
416
+ end
417
+ trace("(#{nf.locked_version})\n")
418
+
419
+ nfs[repo_uri] = nf
420
+ end
421
+
422
+ @lockfile.instance_eval do
423
+ @flavors = nfs
424
+ end
425
+ end
426
+
427
+ def get_default_vimfiles_path()
428
+ # FIXME: Compute more appropriate value.
429
+ "#{ENV['HOME']}/.vim"
430
+ end
431
+
432
+ def install(vimfiles_path)
433
+ load()
434
+ complete_locked_flavors(:upgrade_if_necessary)
435
+ save_lockfile()
436
+ deploy_flavors(lockfile.flavors.values, vimfiles_path)
437
+ end
438
+
439
+ def upgrade(vimfiles_path)
440
+ load()
441
+ complete_locked_flavors(:upgrade_all)
442
+ save_lockfile()
443
+ deploy_flavors(lockfile.flavors.values, vimfiles_path)
444
+ end
445
+ end
446
+
447
+ class CLI < Thor
448
+ desc 'install', 'Install Vim plugins according to VimFlavor file.'
449
+ method_option :vimfiles_path,
450
+ :desc => 'A path to your vimfiles directory.'
451
+ def install()
452
+ facade = Facade.new()
453
+ facade.traced = true
454
+ facade.install(
455
+ options[:vimfiles_path] || facade.get_default_vimfiles_path()
456
+ )
457
+ end
458
+
459
+ desc 'upgrade', 'Upgrade Vim plugins according to VimFlavor file.'
460
+ method_option :vimfiles_path,
461
+ :desc => 'A path to your vimfiles directory.'
462
+ def upgrade()
463
+ facade = Facade.new()
464
+ facade.traced = true
465
+ facade.upgrade(
466
+ options[:vimfiles_path] || facade.get_default_vimfiles_path()
467
+ )
468
+ end
469
+ end
470
+ end
471
+ end