/
install.vim
903 lines (805 loc) · 32.3 KB
/
install.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
" vam#install contains code which is used when install plugins only
exec vam#DefineAndBind('s:c','g:vim_addon_manager','{}')
let s:c.change_to_unix_ff = get(s:c, 'change_to_unix_ff', (g:os=~#'unix'))
let s:c.do_diff = get(s:c, 'do_diff', 1)
let s:c.known = get(s:c, 'known', 'vim-pi')
let s:c.MergeSources = get(s:c, 'MergeSources', 'vam_known_repositories#MergeSources')
let s:c.pool_fun = get(s:c, 'pool_fun', 'vam#install#Pool')
let s:c.name_rewriting = get(s:c, 'name_rewriting', {})
let s:c.pre_update_hook_functions = get(s:c, 'pre_update_hook_functions', ['vam#install#CreatePatch'])
let s:c.post_update_hook_functions = get(s:c, 'post_update_hook_functions', ['vam#install#ApplyPatch'])
let s:c.post_scms_update_hook_functions = get(s:c, 'post_scms_update_hook_functions', ['vam#install#ShowShortLog'])
let s:c.pool_item_check_funs = get(s:c, 'pool_item_check_funs', ['vam#install#CheckPoolItem'])
call extend(s:c.name_rewriting, {'99git+github': 'vam#install#RewriteName'})
fun! s:confirm(msg, ...)
if getchar(1)
let char = getchar()
if type(char) == type(0)
let char = nr2char(char)
endif
let char = tolower(char)
if a:0
if type(a:1)==type("")
let choices = tolower(substitute(a:1, '\v\&@<!.', '', 'g'))
let idx = stridx(choices, char)+1
return idx ? idx : get(a:000, 1, 1)
else
return char is# 's'
endif
else
return char isnot# 'n'
endif
elseif a:0 && type(a:1) == type("")
return call("confirm", [a:msg]+a:000)
else
" Don't allow [y] with additional argument intentionally: it is too easy to
" overlook the dialog. So force users typing [s] instead
return confirm(a:msg, a:0 ? "&No\nYe&s" : "&Yes\n&No") == 1+a:0
endif
endfun
fun! vam#install#confirm(...)
return call('s:confirm', a:000)
endfun
fun! vam#install#CheckPoolItem(key, i)
let type = get(a:i, 'type', '')
let url = get(a:i, 'url', '')
if type == 'git' && url =~ ':\/\/github.com\/'
if url =~ '^http' | call vam#Log("pool item ".url.": should not be using http:// for consistency. If you need http see MyGitCheckout example") | endif
if url =~ '\.git$' | call vam#Log('pool item '.url.': github urls also work without .git suffix. Drop if for copy paste and consistency reasons') | endif
endif
endfun
fun! vam#install#RewriteName(name)
let match = matchlist(a:name, '^\(github\|git\|darcs\|hg\):\(.*\)$')
if empty(match)
return 0
endif
if match[1] ==# 'github'
" github:{Name} {"type": "git", "url": "git://github.com/{Name}/vim-addon-{Name}}
" github:{N}/{Repo} {"type": "git", "url": "git://github.com/{N}/{Repo}"}
return {'type' : 'git', 'url' : 'git://github.com/'.(match[2] =~ '/' ? match[2] : match[2].'/vim-addon-'.match[2])}
else
" hg:{URL}
" svn:{URL}
" darcs:{URL}
" -> {"type" : "hg/svn/darcs", url : URL}
return {'type' : match[1], 'url' : match[2]}
endif
endfun
fun! vam#install#CompleteRepoData(repository, opts)
if has_key(a:repository, 'type')
" looks like repository is already complete ..
return a:repository
endif
" add missing information by
" 1) lookup in pool
" 2) try turning name into source by s:c.name_rewriting
let name = a:repository.name
if name isnot# s:c.known | call vam#install#LoadPool() |endif
let repository = get(s:c.plugin_sources, name, get(get(a:opts, 'plugin_sources', {}), name, 0))
if repository is 0
unlet repository
for key in sort(keys(s:c.name_rewriting))
let repository=call(s:c.name_rewriting[key], [name], {})
if type(repository) == type({})
break
endif
unlet repository
endfor
if exists('repository')
echom 'Name '.name.' expanded to :'.string(repository)
else
" try to find typos and renamings. Tell user about failure
let maybe_fixes = []
let name_ = vam#utils#TypoFix(name)
for x in keys(s:c.plugin_sources)
if vam#utils#TypoFix(x) == name_
call add(maybe_fixes, name.' might be a typo, did you mean: '.x.' ?')
endif
endfor
" try finding new name (vim-pi only)
try
" using try because pool implementations other then vim-pi could be
" used
call extend(maybe_fixes, vimpi#SuggestNewName(name))
catch /Vim(call):E117:/
" If vim-pi activation policy is never, then the above will yield
" unknown function error
endtry
call vam#Log(join(["No repository location info known for plugin ".name."."] + maybe_fixes,"\n"))
return 0
endif
endif
call extend(repository, a:repository)
return repository
endfun
" Install let's you install plugins by passing the url of a addon-info file
" This preprocessor replaces the urls by the plugin-names putting the
" repository information into the global dict
"
" TODO: Does anybody use this? Is it worth keeping?
fun! vam#install#ReplaceAndFetchUrls(list)
let l = a:list
let idx = 0
for idx in range(0, len(l)-1)
let n = l[idx]
" assume n is either an url or a path
if n =~ '\m^https\?://' && s:confirm('Fetch plugin info from URL '.n.'?')
let t = tempname()
call vam#utils#Download(n, t)
elseif n =~ '[/\\]' && filereadable(n)
let t = n
endif
if exists('t')
let info = vam#ReadAddonInfo(t)
unlet t
if !has_key(info, 'name') || !has_key(info, 'repository')
call vam#Log( n." is no valid addon-info file. It must contain both keys: name and repository")
continue
endif
let s:c.plugin_sources[info.name] = info.repository
let l[idx] = info.name
endif
endfor
endfun
fun! vam#install#RunHook(hook, info, repository, pluginDir, opts)
let hkey=tr(a:hook, '-', '_').'_hook_functions'
if has_key(s:c, hkey)
let d={}
for d.hook in s:c[hkey]
call call(d.hook, [a:info, a:repository, a:pluginDir, a:opts], {})
endfor
endif
let hkey=a:hook.'-hook'
if has_key(a:info, hkey)
execute substitute(substitute(substitute(substitute(a:info[hkey],
\'%d', 'a:pluginDir', 'g'),
\'%r', 'a:repository', 'g'),
\'%i', 'a:info', 'g'),
\'%o', 'a:opts', 'g')
endif
endfun
" opts: same as ActivateAddons
fun! vam#install#Install(toBeInstalledList, ...)
let toBeInstalledList = a:toBeInstalledList
call vam#PreprocessScriptIdentifier(toBeInstalledList)
call vam#install#ReplaceAndFetchUrls(map(copy(a:toBeInstalledList),'v:val.name'))
let opts = a:0 == 0 ? {} : a:1
let auto_install = get(opts, 'auto_install', s:c.auto_install)
let installed = []
for to_install in filter(copy(toBeInstalledList), '!vam#IsPluginInstalled(v:val.name)')
let repository = vam#install#CompleteRepoData(to_install, opts)
let name = repository['name']
" make sure all sources are known
if repository is 0
continue
endif
let confirmed = 0
let origin = get(repository,'type','').' '.get(repository,'url','')
" tell user about target directory. Some users don't get it the first time..
let pluginDir = vam#PluginDirFromName(name)
" call vam#DisplayAddonInfo(name), can't use due to plugin_sources
call vam#Log(join(vam#DisplayAddonInfoLines(name, repository),"\n"), 'None')
call vam#Log('Target: '.pluginDir, 'None')
if (has_key(opts, 'requested_by'))
call vam#Log('Dependency chain: '.join([name]+opts.requested_by,' < '))
endif
let d = get(repository, 'deprecated', '')
if type(d) == type('') && d != ''
echom "!> Deprecation warning package ".name. ":"
echom d
" even for auto_install make user confirm the deprecation case
if !vam#Log('Origin: '.origin ,"None")
\ && s:confirm('Plugin '.name.' is deprecated, see warning above. Install it?', 1)
let confirmed = 1
else
continue
endif
endif
" ask user for to confirm installation unless he set auto_install
if auto_install
\ || confirmed
\ || (!vam#Log('Origin: '.origin ,"None")
\ && s:confirm("Install plugin `".name."'?"))
call vam#install#Checkout(pluginDir, repository)
let infoFile = vam#AddonInfoFile(pluginDir, name)
if has_key(repository, 'addon-info') && !filereadable(infoFile)
call writefile([string(repository['addon-info'])], infoFile)
endif
" install dependencies
let info = vam#ReadAddonInfo(infoFile)
let dependencies = get(info, 'dependencies', {})
" install dependencies merging opts with given repository sources
" sources given in opts will win
call vam#install#Install(keys(dependencies),
\ extend(copy(opts), {
\ 'plugin_sources' : extend(copy(dependencies), get(opts, 'plugin_sources',{})),
\ 'requested_by' : [name] + get(opts, 'requested_by', [])
\ }))
call vam#install#HelpTags(name)
if get(opts, 'run_install_hooks', 0)
call vam#install#RunHook('post-install', info, repository, pluginDir, {})
endif
endif
let installed += [name]
endfor
return installed
endfun
fun! vam#install#CreatePatch(info, repository, pluginDir, hook_opts)
let a:hook_opts.diff_do_diff=(s:c.do_diff && executable('diff'))
" try creating diff by checking out old version again
if a:hook_opts.diff_do_diff
" move plugin to backup destination:
let pluginDirBackup = a:pluginDir.'-'.a:hook_opts.oldVersion
if isdirectory(pluginDirBackup) || filereadable(pluginDirBackup)
if s:confirm('Remove old plugin backup directory ('.pluginDirBackup.')?')
call vam#utils#RmFR(pluginDirBackup)
else
throw 'User abort: remove '.pluginDirBackup.' manually'
endif
endif
call rename(a:pluginDir, pluginDirBackup)
" can be romved. old version is encoded in tmp dir. Removing makes
" diffing easier
silent! call delete(pluginDirBackup.'/version')
let a:hook_opts.diff_backup_dir=pluginDirBackup
let diff_file = a:pluginDir.'-'.a:hook_opts.oldVersion.'.diff'
let a:hook_opts.diff_file=diff_file
" try to create a diff
let archiveName = vam#install#ArchiveNameFromDict(a:repository)
let archiveFileBackup = pluginDirBackup.'/archive/'.archiveName
if !filereadable(archiveFileBackup)
call vam#Log("Old archive file ".archiveFileBackup." is gone, can't try to create diff.")
else
let archiveFile = a:pluginDir.'/archive/'.archiveName
call mkdir(a:pluginDir.'/archive', 'p')
let rep_copy = deepcopy(a:repository)
let rep_copy.url = 'file://'.expand(archiveFileBackup, 1)
call vam#install#Checkout(a:pluginDir, rep_copy)
silent! call delete(a:pluginDir.'/version')
try
call vam#utils#ExecInDir(fnamemodify(a:pluginDir, ':h'), 'diff -U3 -r -a --binary $p $p > $p', fnamemodify(a:pluginDir,':t'), fnamemodify(pluginDirBackup,':t'), diff_file)
silent! call delete(diff_file)
let a:hook_opts.diff_do_diff=0
catch /.*/
" :-( this is expected. diff returns non zero exit status. This is hacky
let a:hook_opts.diff_do_diff=1
endtry
call vam#utils#RmFR(a:pluginDir)
endif
endif
endfun
fun! vam#install#ApplyPatch(info, repository, pluginDir, hook_opts)
" try applying patch
if a:hook_opts.diff_do_diff
if executable('patch')
let diff_file=a:hook_opts.diff_file
let pluginDirBackup=a:hook_opts.diff_backup_dir
try
call vam#utils#ExecInDir(a:pluginDir, 'patch --binary -p1 --input=$p', diff_file)
call vam#Log('Patching suceeded', 'None')
call delete(diff_file)
call vam#utils#RmFR(pluginDirBackup)
catch /.*/
call vam#Log('Failed to apply patch '.diff_file.' ('.v:exception.'), original files may be found in '.pluginDirBackup)
endtry
else
call vam#Log('Failed trying to apply diff: “patch” executable not found')
endif
endif
endfun
" this function will be refactored slightly soon by either me or ZyX.
" returns
" "up-to-date" if addon is up to date
" "updated" if addon was updated succesfully
" "failed" if addon could not be updated. Error is logged in this case
fun! vam#install#UpdateAddon(name)
call vam#Log( "Considering ".a:name." for update" ,'type','unknown')
let pluginDir = vam#PluginDirFromName(a:name)
if !isdirectory(pluginDir)
return 'missing'
endif
" First, try updating using VCS. Return 1 if everything is ok, 0 if exception
" is thrown
try
let [r, oldVersion, newVersion, sdescr] = vam#vcs#Update(pluginDir)
let hook_opts={'oldVersion': oldVersion, 'newVersion': newVersion, 'sdescr': sdescr}
if r isnot# 'unknown'
if r is# 'updated'
call vam#install#RunHook('post-scms-update', vam#ReadAddonInfo(vam#AddonInfoFile(pluginDir, a:name)), {}, pluginDir, hook_opts)
endif
return r
endif
catch /.*/
call vam#Log(v:exception)
return 'failed'
endtry
"Next, try updating plugin by archive
" we have to find out whether there is a new version:
call vam#install#LoadPool()
let repository = get(s:c.plugin_sources, a:name, {})
if empty(repository)
call vam#Log("Don't know how to update ".a:name." because it is not contained in plugin_sources")
return 'failed'
endif
let newVersion = get(repository, 'version', '?')
if a:name is# 'vim-addon-manager'
" load utils before the file is moved below
runtime autoload/vam/util.vim
endif
if get(repository, 'type', 0) isnot# 'archive'
call vam#Log("Not updating ".a:name." because the repository description suggests using VCS ".get(repository, 'type', 'unknown').'.'
\ ."\nYour install seems to be of type archive/manual/www.vim.org/unknown."
\ ."\nIf you want to update ".a:name." remove ".pluginDir." and let VAM check it out again."
\ )
return 'failed'
endif
let versionFile = pluginDir.'/version'
let oldVersion = filereadable(versionFile) ? readfile(versionFile, 'b')[0] : "?"
if oldVersion !=# newVersion || newVersion == '?'
" update plugin
echom "Updating plugin ".a:name." because ".(newVersion == '?' ? 'version is unknown' : 'there is a different version')
let hook_opts={'oldVersion': oldVersion, 'newVersion': newVersion}
call vam#install#RunHook('pre-update', vam#ReadAddonInfo(vam#AddonInfoFile(pluginDir, a:name)), repository, pluginDir, hook_opts)
" checkout new version (checkout into empty location - same as installing):
if isdirectory(pluginDir)
call vam#utils#RmFR(pluginDir)
endif
call vam#install#Checkout(pluginDir, repository)
" addon info file name could have changed here ..
call vam#install#RunHook('post-update', vam#ReadAddonInfo(vam#AddonInfoFile(pluginDir, a:name)), repository, pluginDir, hook_opts)
return 'updated'
elseif oldVersion == newVersion
call vam#Log( "Not updating plugin ".a:name.", ".newVersion." is current")
return 'up-to-date'
else
call vam#Log( "Not updating plugin ".a:name." because there is no version according to version key")
endif
return 'up-to-date'
endfun
fun! vam#install#Update(list)
let list = a:list
if s:c.known isnot 0
if vam#install#UpdateAddon(s:c.known)
call vam#install#HelpTags(s:c.known)
endif
endif
" refresh sources:
call vam#install#LoadPool(1)
if empty(list) && s:confirm('Update all loaded plugins?')
let list = keys(s:c.activated_plugins)
endif
let by_reply = {}
for p in list
let r = vam#install#UpdateAddon(p)
if r is# 'updated'
call vam#install#HelpTags(p)
endif
if (!has_key(by_reply,r))
let by_reply[r] = []
endif
call add(by_reply[r], p)
endfor
let labels = {'updated': 'Updated:', 'failed': 'Failed to update:',
\ 'up-to-date': 'Already up to date:',
\ 'possibly updated': 'Don’t know how to determine update state (possibly updated):',
\ 'missing': 'Plugin not installed:',}
for [k,v] in items(by_reply)
call vam#Log(get(labels,k,k).' '.string(by_reply[k]).".", k is# 'failed' ? 'WarningMsg' : 'Type')
endfor
endfun
" completion {{{
fun! vam#install#KnownAddons(type)
call vam#install#LoadPool()
let k = {}
" from pool
call extend(k, s:c.plugin_sources)
" Disk completion using default plugin dir location.
" Don’t do this if plugin_root_dir contains newline on outdated vim:
" split(glob) does not work properly in this case. Also don’t do this if we
" require notinstalled plugins.
if a:type isnot# 'notinstalled' &&
\s:c.plugin_dir_by_name is# 'vam#DefaultPluginDirFromName'
for dir in [s:c.plugin_root_dir]+s:c.additional_addon_dirs
for key in map(vam#GlobInDir(dir, '*/'), 'fnamemodify(v:val[:-2], ":t")')
" We don’t care about values: so no need to make it complex
let k[key] = 1
endfor
endfor
endif
return sort(keys(k))
endfun
" Filters:
" 1. Start of the name must be the same as completed name
" 2. Part of the name must be the same as completed name
" 3. User correctly entered start of the string, but punctuation characters
" inside were mistaken. Also takes globs as '*'=~'[[:punct:]]'.
" 4. There may be missing characters somewhere at the word boundary, but user
" correctly entered start of the word
" 5. There may be missing characters somewhere at the word boundary, but first
" character is correct
" 6. Name has completely wrong order of characters, but not the first character
" 7. Punctuation characters inside string were mistaken.
" Also takes globs as '*'=~'[[:punct:]]'.
" 8. There may be missing characters somewhere at the word boundary
" 9. There may be some missing charaters inside a word
let s:smartfilters=[
\['1', '"v:val[:".(len(a:str)-1)."]==?".string(a:str)'],
\['1', '"stridx(tolower(v:val), ".string(tolower(a:str)).")!=-1"'],
\['haspunct', '"v:val=~?".string("\\v^".regnopunct)'],
\['haspunct', '"v:val=~?".string("\\v^".regboundary)'],
\['lstr>1', '"v:val=~?".string("\\v^".reginside)'],
\['lstr>1', '"v:val=~?".string(regwrongorder)'],
\['haspunct', '"v:val=~?".string(regnopunct)'],
\['haspunct', '"v:val=~?".string(regboundary)'],
\['lstr>1', '"v:val=~?".string(reginside)'],
\]
fun! vam#install#GetFilters(str)
if empty(a:str)
return ['1']
endif
let lstr=len(a:str)
let haspunct=(match(a:str, "[[:punct:]]")!=-1)
let regnopunct='\V'.substitute(a:str, '\v[[:punct:]]+', '\\.\\*', 'g')
let estrchars=map(split(a:str,'\v\_.@='), 'escape(v:val, "\\")')
let regwrongorder='\V\^'.estrchars[0].'\%(\.\*'.join(estrchars[1:], '\)\@=\%(\.\*').'\)\@='
let regboundary='\V'.join(map(split(a:str, '\v[[:punct:]]@<=|[[:punct:]]@='), 'escape(v:val, "\\")'), '\.\*')
let reginside='\V'.join(estrchars, '\.\*')
return map(filter(copy(s:smartfilters), 'eval(v:val[0])'), 'eval(v:val[1])')
endfun
fun! vam#install#FilterVariants(str, variants)
let list=copy(a:variants)
let r=[]
for filter in vam#install#GetFilters(a:str)
let newlist=[]
call map(list, '('.filter.')?(add(r, v:val)):(add(newlist, v:val))')
" We already have enough results to show, so stop thinking that user needs
" more variants
if len(r)>8
break
endif
let list=newlist
endfor
return r
endfun
fun! vam#install#CompleteAddonName(findstart, base)
if a:findstart
let match_text=matchstr(getline('.')[:col('.')-2], "[^'\"()[\\]{}\t ]*$")
return col('.')-len(match_text)-1
else
call map(vam#install#FilterVariants(a:base, vam#install#KnownAddons(0)),
\ 'complete_add({"word": v:val, '.
\ '"menu": "Script id: ".'.
\ 'get(get(s:c.plugin_sources, v:val, {}), '.
\ '"vim_script_nr", "NA")})')
return []
endif
endfun
let s:postfilters={
\'installed': 'isdirectory(vam#PluginDirFromName(v:val))',
\'notloaded': '(!has_key(s:c.activated_plugins, v:val)) && '.
\ 'isdirectory(vam#PluginDirFromName(v:val))',
\'notinstalled': '!isdirectory(vam#PluginDirFromName(v:val))',
\}
fun! vam#install#DoCompletion(A, L, P, ...)
let list=vam#install#KnownAddons(get(a:000, 0, 0))
let str=matchstr(a:L[:a:P-1], '\S*$')
let r=vam#install#FilterVariants(str, list)
if a:0
call filter(r, s:postfilters[a:1])
endif
return r
endfun
fun! vam#install#AddonCompletion(...)
return call('vam#install#DoCompletion',a:000)
endfun
fun! vam#install#NotInstalledAddonCompletion(...)
return call('vam#install#DoCompletion',a:000+["notinstalled"])
endfun
fun! vam#install#InstalledAddonCompletion(...)
return call('vam#install#DoCompletion',a:000+["installed"])
endfun
fun! vam#install#UninstallCompletion(...)
return call('vam#install#DoCompletion',a:000+["notloaded"])
endfun
fun! vam#install#UpdateCompletion(...)
return call('vam#install#DoCompletion',a:000+["installed"])
endfun
"}}}
fun! vam#install#UninstallAddons(list)
let list = a:list
if list == []
echo "No plugins selected. If you ran UninstallNotLoadedAddons use <tab> or <c-d> to get a list of not loaded plugins."
return
endif
call map(list, 'vam#PluginDirFromName(v:val)')
if s:confirm('Will now remove '.join(list, ', ').'. Confirm?')
call map(list, 'vam#utils#RmFR(v:val)')
endif
endfun
fun! vam#install#HelpTags(name)
let pluginDir = vam#PluginDirFromName(a:name)
let info = vam#ReadAddonInfo(vam#AddonInfoFile(pluginDir, a:name))
let d=vam#PluginRuntimePath(pluginDir, info).'/doc'
if isdirectory(d) | exec 'helptags '.fnameescape(d) | endif
endfun
" basename of url. if archive_name is given use that instead
fun! vam#install#ArchiveNameFromDict(repository)
let archiveName = fnamemodify(substitute(get(a:repository,'archive_name',''), '\.\@<=VIM$', 'vim', ''),':t')
if empty(archiveName)
let archiveName = fnamemodify(a:repository.url,':t')
endif
return archiveName
endfun
" may throw EXCEPTION_UNPACK
fun! vam#install#Checkout(targetDir, repository) abort
if get(a:repository, 'script-type') is 'patch'
call vam#Log(
\ "This plugin requires patching and recompiling vim.\n"
\ ."VAM could not do this, so you have to apply patch\n"
\ ."manually."
\ )
endif
if vam#vcs#Checkout(a:targetDir, a:repository)
" Successfully checked out a repository. Leaving a comment here to indicate
" that an if condition has a side effect of checking out a repository.
else
" archive based repositories - no VCS
if !isdirectory(a:targetDir) | call mkdir(a:targetDir.'/archive','p') | endif
" basename VIM -> vim
let archiveName = vam#install#ArchiveNameFromDict(a:repository)
" archive will be downloaded to this location
let archiveFile = a:targetDir.'/archive/'.archiveName
call vam#utils#Download(a:repository.url, archiveFile)
let opts = {'strip-components': get(a:repository,'strip-components',-1),
\ 'script-type': tolower(get(a:repository, 'script-type', 'plugin')),
\ 'unix_ff': get(a:repository, 'unix_ff', get(s:c, 'change_to_unix_ff')) }
if (has_key(a:repository, 'target_dir'))
let opts.target_dir = a:repository.target_dir
endif
call vam#utils#Unpack(archiveFile, a:targetDir, opts)
call writefile([get(a:repository, 'version', '?')], a:targetDir.'/version', 'b')
endif
endfun
" is there a library providing an OS abstraction? This breaks Winndows
" xcopy or copy should be used there..
fun! vam#install#Copy(f,t)
if g:is_win
call vam#utils#RunShell('xcopy /e /i $ $', a:f, a:t)
else
call vam#utils#RunShell('cp -r $ $', a:f, a:t)
endif
endfun
fun! vam#install#MergeTarget()
return split(&runtimepath,",")[0].'/after/plugin/vim-addon-manager-merged.vim'
endfun
" if you machine is under IO load starting up Vim can take some time
" This function tries to optimize this by reading all the plugin/*.vim
" files joining them to one vim file.
"
" 1) rename plugin to plugin-merged (so that they are no longer sourced by Vim)
" 2) read plugin/*.vim_merged files
" 3) replace clashing s:name vars by uniq names
" 4) rewrite the guards (everything containing finish)
" 5) write final merged file to ~/.vim/after/plugin/vim-addon-manager-merged.vim
" so that its sourced automatically
"
" TODO: take after plugins int account?
fun! vam#install#MergePluginFiles(plugins, skip_pattern)
if !filereadable('/bin/sh')
throw "you should be using Linux.. This code is likely to break on other operating systems!"
endif
let target = vam#install#MergeTarget()
for r in a:plugins
if !has_key(s:c.activated_plugins, r)
throw "JoinPluginFiles: all plugins must be activated (which ensures that they have been installed). This plugin is not active: ".r
endif
endfor
let runtimepaths = map(copy(a:plugins), 'vam#PluginRuntimePath(vam#PluginDirFromName(v:val), vam#ReadAddonInfo(vam#AddonInfoFile(vam#PluginDirFromName(v:val), v:val)))')
" 1)
for r in runtimepaths
if (isdirectory(r.'/plugin'))
call vam#utils#RunShell('mv $ $', r.'/plugin', r.'/plugin-merged')
endif
endfor
" 2)
let file_local_vars = {}
let uniq = 1
let all_contents = ""
for r in runtimepaths
for file in vam#GlobInDir(r, 'plugin-merged/*.vim')
if file =~ a:skip_pattern
let all_contents .= "\" ignoring ".file."\n"
continue
endif
let names_this_file = {}
let contents =join(readfile(file, 'b'),"\n")
for l in split("s:abc s:foobar",'\ze\<s:')[1:]
let names_this_file[matchstr(l,'^s:\zs[^ [(=)\]]*')] = 1
endfor
" handle duplicate local vars: 3)
for k in keys(names_this_file)
if has_key(file_local_vars, k)
let new = 'uniq_'.uniq
let uniq += 1
let file_local_vars[new] = 1
let contents = "\" replaced: ".k." by ".new."\n".substitute(contents, 's:'.k,'s:'.new,'g')
else
let file_local_vars[k] = 1
endif
endfor
" find finish which start at the end of a line.
" They are often used to separated Vim code from additional info such as
" history (eg tlib is using it). Comment remaining lines
let lines = split(contents,"\n")
let comment = 0
for i in range(0,len(lines)-1)
if lines[i] =~ '^finish'
let comment = 1
endif
if comment
let lines[i] = "\" ".lines[i]
endif
endfor
let contents = join(lines,"\n")
" guards 4)
" find guards replace them by if .. endif blocks
let lines = split(contents,"\n")
for i in range(2,len(lines)-1)
if lines[i] =~ '^\s*finish' && lines[i-1] =~ '^\s*if\s'
" found a guard
" negate if, remove triple { if present (I don't care)
let lines[i-1] = 'if !('.matchstr(substitute(lines[i-1],'"[^"]*{\{3}.*','',''),'if\s*\zs.*').')'
let j = i+1
while j < len(lines) && lines[j] !~ '^\s*endif'
let lines[j] = ''
let j = j+1
endwhile
let lines[j] = ''
" guards are never longer than 10 lines
if j - i > 10
throw "something probably has gone wrong while removing guard for file".file." start at line: ".i
endif
call add(lines,'endif')
let contents = join(lines,"\n")
break
endif
endfor
" comment remaining finish lines. This does not catch if .. | finish | endif and such
" :-(
" adding additional \n because not all scripts have a final \n..
let contents = substitute(contents, '\<finish\>','" finish','g')
let all_contents .= "\n"
\ ."\"merged: ".file."\n"
\ .contents
\ ."\n"
\ ."\"merged: ".file." end\n"
endfor
endfor
let d =fnamemodify(target,':h')
if !isdirectory(d) | call mkdir(d,'p') | endif
call writefile(split(all_contents,"\n"), target)
endfun
fun! vam#install#UnmergePluginFiles()
let path = fnamemodify(vam#PluginRuntimePath('vim-addon-manager', {}),':h')
for merged in vam#GlobInDir(path, '{,*/}*/plugin-merged')
echo "unmerging ".merged
call rename(merged, substitute(merged,'-merged$','',''))
endfor
call delete(vam#install#MergeTarget())
endfun
"{{{1 Pool
" VAM does call this function for you when using {Activate/Install}Addon() or
" one of those commands. Read doc/vim-addon-manager.txt to learn about the
" pool of plugin sources. Also see option "known_repos_activation_policy"
fun! vam#install#LoadKnownRepos()
let known = s:c.known
let reason = a:0 > 0 ? a:1 : 'get more plugin sources'
if get(s:c.activated_plugins, known, 0)
return 1
else
let policy=get(s:c, 'known_repos_activation_policy', 'autoload')
if policy is? 'ask'
let reply = s:confirm('Activate plugin '.known.' to '.reason."?", "&Yes\n&No\nN&ever (during session)")
elseif policy is? 'never'
let reply=2
else
let reply=1
endif
if reply == 3
let s:c.known_repos_activation_policy = 'never'
return 0
elseif reply == 1
" don't pass opts so that new_runtime_paths is not set which will
" trigger topLevel adding -known-repos to rtp immediately
call vam#ActivateAddons([known], {})
return 1
else
return 0
endif
endif
endfun
" The default pool of know plugins for VAM: vim-pi
fun! vam#install#Pool()
if vam#install#LoadKnownRepos()
return vam_known_repositories#Pool()
else
return s:c.plugin_sources
endif
endfun
" (re)loads pool of known plugins
fun! vam#install#LoadPool(...)
let force = a:0 > 0 ? a:1 : 0
if force || !has_key(s:c, 'pool_loaded')
" update plugin_sources
let s:c.plugin_sources = call(s:c.pool_fun, [], {})
for F in s:c.pool_item_check_funs
for k_v in items(s:c.plugin_sources)
call call(F, k_v)
endfor
endfor
let s:c.pool_loaded = 1
endif
endfun
"}}}1
if g:is_win
fun! vam#install#FetchAdditionalWindowsTools() abort
if !isdirectory(s:c.binary_utils.'\dist')
call mkdir(s:c.binary_utils.'\dist','p')
endif
" we have curl, so we can fetch remaining deps using Download and Unpack
let tools = {
\ 'gzip': ['mirror://sourceforge/gnuwin32/gzip/1.3.12-1/', "gzip-1.3.12-1-bin.zip", ["gzip", "7z"]],
\ 'bzip2':['mirror://sourceforge/gnuwin32/bzip2/1.0.5/', "bzip2-1.0.5-bin.zip", ["bzip2", "7z"] ],
\ 'tar': ['mirror://sourceforge/gnuwin32/tar/1.13-1/',"tar-1.13-1-bin.zip", ["tar", "7z"] ],
\ 'zip': ['mirror://sourceforge/gnuwin32/unzip/5.51-1/', "unzip-5.51-1-bin.zip", ["unzip","7z"] ],
\ 'diffutils': ['mirror://sourceforge/gnuwin32/diffutils/2.8.7-1/',"diffutils-2.8.7-1-bin.zip", "diff"],
\ 'patch': [ 'mirror://sourceforge/gnuwin32/patch/2.5.9-7/',"patch-2.5.9-7-bin.zip", "patch"]
\ }
for v in values(tools)
echo "downloading ".v[1]
for ex in v[2]
if executable(ex) | continue | endif
endfor
if !filereadable(s:c.binary_utils.'\'.v[1])
call vam#utils#DownloadFromMirrors(v[0].v[1], s:c.binary_utils)
endif
endfor
if !executable('unzip')
" colorize this?
echo "__ its your turn: __"
echom "__ move all files of the zip directory into ".s:c.binary_utils.'/dist . Close the Explorer window and the shell window to continue. Press any key'
call getchar()
exec "!".expand(s:c.binary_utils.'/'. tools.zip[1], 1)
let $PATH=$PATH.';'.s:c.binary_utils_bin
if !executable('unzip')
throw "can't execute unzip. Something failed!"
endif
endif
" now we have unzip and can do rest
for k in ["gzip","bzip2","tar","diffutils","patch"]
if !executable(tools[k][2])
call vam#utils#Unpack(s:c.binary_utils.'\'.tools[k][1], s:c.binary_utils.'\dist')
endif
endfor
"if executable("7z")
"echo "you already have 7z in PATH. Nothing to be done"
"return
"endif
"let _7zurl = 'mirror://sourceforge/sevenzip/7-Zip/4.65/7z465.exe'
"call vam#utils#DownloadFromMirrors(_7zurl, s:c.binary_utils.'/7z.exe')
endfun
endif
fun! vam#install#ShowShortLog(info, repository, pluginDir, hook_opts)
if has_key(a:hook_opts.sdescr, 'log')
let c=a:hook_opts.sdescr.log
call vam#Log('Changes', 'PreProc')
" XXX wdrev() functions return result with trailing newline hence matchstr()
" \S also matches newline hence double quotes and collection
call vam#Log(call(c[0], get(c, 1, [])+[
\a:pluginDir,
\matchstr(a:hook_opts.oldVersion, "[^ \t\r\n]*"),
\matchstr(a:hook_opts.newVersion, "[^ \t\r\n]*")], get(c, 2, {})),
\'Normal')
endif
endfun
" vim: et ts=8 sts=2 sw=2