plug.vim 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  1. " vim-plug: Vim plugin manager
  2. " ============================
  3. "
  4. " Download plug.vim and put it in ~/.vim/autoload
  5. "
  6. " mkdir -p ~/.vim/autoload
  7. " curl -fLo ~/.vim/autoload/plug.vim \
  8. " https://raw.github.com/junegunn/vim-plug/master/plug.vim
  9. "
  10. " Edit your .vimrc
  11. "
  12. " call plug#begin('~/.vim/plugged')
  13. "
  14. " " Make sure you use single quotes
  15. " Plug 'junegunn/seoul256.vim'
  16. " Plug 'junegunn/vim-easy-align'
  17. "
  18. " " On-demand loading
  19. " Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
  20. " Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
  21. "
  22. " " Using git URL
  23. " Plug 'https://github.com/junegunn/vim-github-dashboard.git'
  24. "
  25. " " Plugin options
  26. " Plug 'nsf/gocode', { 'tag': 'go.weekly.2012-03-13', 'rtp': 'vim' }
  27. "
  28. " " Locally-managed plugin
  29. " Plug '~/.fzf'
  30. "
  31. " call plug#end()
  32. "
  33. " Then :PlugInstall to install plugins. (default: ~/.vim/plugged)
  34. " You can change the location of the plugins with plug#begin(path) call.
  35. "
  36. "
  37. " Copyright (c) 2014 Junegunn Choi
  38. "
  39. " MIT License
  40. "
  41. " Permission is hereby granted, free of charge, to any person obtaining
  42. " a copy of this software and associated documentation files (the
  43. " "Software"), to deal in the Software without restriction, including
  44. " without limitation the rights to use, copy, modify, merge, publish,
  45. " distribute, sublicense, and/or sell copies of the Software, and to
  46. " permit persons to whom the Software is furnished to do so, subject to
  47. " the following conditions:
  48. "
  49. " The above copyright notice and this permission notice shall be
  50. " included in all copies or substantial portions of the Software.
  51. "
  52. " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  53. " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  54. " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  55. " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  56. " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  57. " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  58. " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  59. if exists('g:loaded_plug')
  60. finish
  61. endif
  62. let g:loaded_plug = 1
  63. let s:cpo_save = &cpo
  64. set cpo&vim
  65. let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
  66. let s:plug_file = 'Plugfile'
  67. let s:plug_buf = -1
  68. let s:mac_gui = has('gui_macvim') && has('gui_running')
  69. let s:is_win = has('win32') || has('win64')
  70. let s:me = expand('<sfile>:p')
  71. function! plug#begin(...)
  72. if a:0 > 0
  73. let home = s:path(fnamemodify(a:1, ':p'))
  74. elseif exists('g:plug_home')
  75. let home = s:path(g:plug_home)
  76. elseif !empty(&rtp)
  77. let home = s:path(split(&rtp, ',')[0]) . '/plugged'
  78. else
  79. echoerr "Unable to determine plug home. Try calling plug#begin() with a path argument."
  80. return 0
  81. endif
  82. if !isdirectory(home)
  83. try
  84. call mkdir(home, 'p')
  85. catch
  86. echoerr 'Invalid plug directory: '. home
  87. return 0
  88. endtry
  89. endif
  90. if !executable('git')
  91. echoerr "`git' executable not found. vim-plug requires git."
  92. return 0
  93. endif
  94. let g:plug_home = home
  95. let g:plugs = {}
  96. " we want to keep track of the order plugins where registered.
  97. let g:plugs_order = []
  98. command! -nargs=+ -bar Plug call s:add(1, <args>)
  99. command! -nargs=* -complete=customlist,s:names PlugInstall call s:install(<f-args>)
  100. command! -nargs=* -complete=customlist,s:names PlugUpdate call s:update(<f-args>)
  101. command! -nargs=0 -bang PlugClean call s:clean('<bang>' == '!')
  102. command! -nargs=0 PlugUpgrade if s:upgrade() | execute "source ". s:me | endif
  103. command! -nargs=0 PlugStatus call s:status()
  104. command! -nargs=0 PlugDiff call s:diff()
  105. return 1
  106. endfunction
  107. function! s:to_a(v)
  108. return type(a:v) == 3 ? a:v : [a:v]
  109. endfunction
  110. function! plug#end()
  111. if !exists('g:plugs')
  112. echoerr 'Call plug#begin() first'
  113. return
  114. endif
  115. let keys = keys(g:plugs)
  116. while !empty(keys)
  117. let keys = keys(s:extend(keys))
  118. endwhile
  119. if exists('#PlugLOD')
  120. augroup PlugLOD
  121. autocmd!
  122. augroup END
  123. augroup! PlugLOD
  124. endif
  125. let lod = {}
  126. filetype off
  127. " we want to make sure the plugin directories are added to rtp in the same
  128. " order that they are registered with the Plug command. since the s:add_rtp
  129. " function uses ^= to add plugin directories to the front of the rtp, we
  130. " need to loop through the plugins in reverse
  131. for name in reverse(copy(g:plugs_order))
  132. let plug = g:plugs[name]
  133. if !has_key(plug, 'on') && !has_key(plug, 'for')
  134. call s:add_rtp(s:rtp(plug))
  135. continue
  136. endif
  137. if has_key(plug, 'on')
  138. let commands = s:to_a(plug.on)
  139. for cmd in commands
  140. if cmd =~ '^<Plug>.\+'
  141. if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
  142. for [mode, map_prefix, key_prefix] in
  143. \ [['i', "<C-O>", ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
  144. execute printf(
  145. \ "%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, '%s')<CR>",
  146. \ mode, cmd, map_prefix, string(cmd), string(name), key_prefix)
  147. endfor
  148. endif
  149. elseif !exists(':'.cmd)
  150. execute printf(
  151. \ "command! -nargs=* -range -bang %s call s:lod_cmd(%s, '<bang>', <line1>, <line2>, <q-args>, %s)",
  152. \ cmd, string(cmd), string(name))
  153. endif
  154. endfor
  155. endif
  156. if has_key(plug, 'for')
  157. for vim in split(globpath(s:rtp(plug), 'ftdetect/**/*.vim'), '\n')
  158. execute 'source '.vim
  159. endfor
  160. for key in s:to_a(plug.for)
  161. if !has_key(lod, key)
  162. let lod[key] = []
  163. endif
  164. call add(lod[key], name)
  165. endfor
  166. endif
  167. endfor
  168. for [key, names] in items(lod)
  169. augroup PlugLOD
  170. execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
  171. \ key, string(key), string(reverse(names)))
  172. augroup END
  173. endfor
  174. call s:reorg_rtp()
  175. filetype plugin indent on
  176. syntax on
  177. endfunction
  178. if s:is_win
  179. function! s:rtp(spec)
  180. let rtp = s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  181. return substitute(rtp, '\\*$', '', '')
  182. endfunction
  183. function! s:path(path)
  184. return substitute(substitute(a:path, '/', '\', 'g'), '[/\\]*$', '', '')
  185. endfunction
  186. function! s:dirpath(path)
  187. return s:path(a:path) . '\'
  188. endfunction
  189. else
  190. function! s:rtp(spec)
  191. return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  192. endfunction
  193. function! s:path(path)
  194. return substitute(a:path, '[/\\]*$', '', '')
  195. endfunction
  196. function! s:dirpath(path)
  197. return s:path(a:path) . '/'
  198. endfunction
  199. endif
  200. function! s:esc(path)
  201. return substitute(a:path, ' ', '\\ ', 'g')
  202. endfunction
  203. function! s:add_rtp(rtp)
  204. execute "set rtp^=".s:esc(a:rtp)
  205. let after = globpath(a:rtp, 'after')
  206. if isdirectory(after)
  207. execute "set rtp+=".s:esc(after)
  208. endif
  209. endfunction
  210. function! s:reorg_rtp()
  211. if !empty(s:first_rtp)
  212. execute 'set rtp-='.s:first_rtp
  213. execute 'set rtp^='.s:first_rtp
  214. endif
  215. if s:last_rtp !=# s:first_rtp
  216. execute 'set rtp-='.s:last_rtp
  217. execute 'set rtp+='.s:last_rtp
  218. endif
  219. endfunction
  220. function! s:lod(plug, types)
  221. let rtp = s:rtp(a:plug)
  222. call s:add_rtp(rtp)
  223. for dir in a:types
  224. for vim in split(globpath(rtp, dir.'/**/*.vim'), '\n')
  225. execute 'source '.vim
  226. endfor
  227. endfor
  228. endfunction
  229. function! s:lod_ft(pat, names)
  230. for name in a:names
  231. call s:lod(g:plugs[name], ['plugin', 'after'])
  232. endfor
  233. call s:reorg_rtp()
  234. execute 'autocmd! PlugLOD FileType ' . a:pat
  235. silent! doautocmd filetypeplugin FileType
  236. endfunction
  237. function! s:lod_cmd(cmd, bang, l1, l2, args, name)
  238. execute 'delc '.a:cmd
  239. call s:lod(g:plugs[a:name], ['plugin', 'ftdetect', 'after'])
  240. call s:reorg_rtp()
  241. execute printf("%s%s%s %s", (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
  242. endfunction
  243. function! s:lod_map(map, name, prefix)
  244. execute 'unmap '.a:map
  245. execute 'iunmap '.a:map
  246. call s:lod(g:plugs[a:name], ['plugin', 'ftdetect', 'after'])
  247. call s:reorg_rtp()
  248. let extra = ''
  249. while 1
  250. let c = getchar(0)
  251. if c == 0
  252. break
  253. endif
  254. let extra .= nr2char(c)
  255. endwhile
  256. call feedkeys(a:prefix . substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
  257. endfunction
  258. function! s:add(force, ...)
  259. let opts = { 'branch': 'master', 'frozen': 0 }
  260. if a:0 == 1
  261. let plugin = a:1
  262. elseif a:0 == 2
  263. let plugin = a:1
  264. if type(a:2) == 1
  265. let opts.branch = a:2
  266. elseif type(a:2) == 4
  267. call extend(opts, a:2)
  268. if has_key(opts, 'tag')
  269. let opts.branch = remove(opts, 'tag')
  270. endif
  271. else
  272. echoerr "Invalid argument type (expected: string or dictionary)"
  273. return
  274. endif
  275. else
  276. echoerr "Invalid number of arguments (1..2)"
  277. return
  278. endif
  279. let plugin = substitute(plugin, '[/\\]*$', '', '')
  280. let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
  281. if !a:force && has_key(g:plugs, name)
  282. let s:extended[name] = g:plugs[name]
  283. return
  284. endif
  285. if plugin[0] =~ '[/$~]' || plugin =~? '^[a-z]:'
  286. let spec = extend(opts, { 'dir': s:dirpath(expand(plugin)) })
  287. else
  288. if plugin =~ ':'
  289. let uri = plugin
  290. else
  291. if plugin !~ '/'
  292. let plugin = 'vim-scripts/'. plugin
  293. endif
  294. let uri = 'https://git:@github.com/' . plugin . '.git'
  295. endif
  296. let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') )
  297. let spec = extend(opts, { 'dir': dir, 'uri': uri })
  298. endif
  299. let g:plugs[name] = spec
  300. if !a:force
  301. let s:extended[name] = spec
  302. endif
  303. let g:plugs_order += [name]
  304. endfunction
  305. function! s:install(...)
  306. call s:update_impl(0, a:000)
  307. endfunction
  308. function! s:update(...)
  309. call s:update_impl(1, a:000)
  310. endfunction
  311. function! s:apply()
  312. for spec in values(g:plugs)
  313. let docd = join([spec.dir, 'doc'], '/')
  314. if isdirectory(docd)
  315. silent! execute "helptags ". join([spec.dir, 'doc'], '/')
  316. endif
  317. endfor
  318. runtime! plugin/*.vim
  319. runtime! after/*.vim
  320. silent! source $MYVIMRC
  321. endfunction
  322. function! s:syntax()
  323. syntax clear
  324. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
  325. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
  326. syn match plugNumber /[0-9]\+[0-9.]*/ contained
  327. syn match plugBracket /[[\]]/ contained
  328. syn match plugX /x/ contained
  329. syn match plugDash /^-/
  330. syn match plugPlus /^+/
  331. syn match plugStar /^*/
  332. syn match plugMessage /\(^- \)\@<=.*/
  333. syn match plugName /\(^- \)\@<=[^ ]*:/
  334. syn match plugInstall /\(^+ \)\@<=[^:]*/
  335. syn match plugUpdate /\(^* \)\@<=[^:]*/
  336. syn match plugCommit /^ [0-9a-z]\{7} .*/ contains=plugRelDate,plugSha
  337. syn match plugSha /\(^ \)\@<=[0-9a-z]\{7}/ contained
  338. syn match plugRelDate /([^)]*)$/ contained
  339. syn match plugError /^x.*/
  340. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  341. hi def link plug1 Title
  342. hi def link plug2 Repeat
  343. hi def link plugX Exception
  344. hi def link plugBracket Structure
  345. hi def link plugNumber Number
  346. hi def link plugDash Special
  347. hi def link plugPlus Constant
  348. hi def link plugStar Boolean
  349. hi def link plugMessage Function
  350. hi def link plugName Label
  351. hi def link plugInstall Function
  352. hi def link plugUpdate Type
  353. hi def link plugError Error
  354. hi def link plugRelDate Comment
  355. hi def link plugSha Identifier
  356. endfunction
  357. function! s:lpad(str, len)
  358. return a:str . repeat(' ', a:len - len(a:str))
  359. endfunction
  360. function! s:lastline(msg)
  361. let lines = split(a:msg, '\n')
  362. return get(lines, -1, '')
  363. endfunction
  364. function! s:prepare()
  365. if bufexists(s:plug_buf)
  366. let winnr = bufwinnr(s:plug_buf)
  367. if winnr < 0
  368. vertical topleft new
  369. execute 'buffer ' . s:plug_buf
  370. else
  371. execute winnr . 'wincmd w'
  372. endif
  373. silent %d _
  374. else
  375. vertical topleft new
  376. nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>q<cr>
  377. nnoremap <silent> <buffer> R :silent! call <SID>retry()<cr>
  378. nnoremap <silent> <buffer> D :PlugDiff<cr>
  379. nnoremap <silent> <buffer> S :PlugStatus<cr>
  380. nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
  381. nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
  382. let b:plug_preview = -1
  383. let s:plug_buf = winbufnr(0)
  384. call s:assign_name()
  385. endif
  386. silent! unmap <buffer> <cr>
  387. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  388. setf vim-plug
  389. call s:syntax()
  390. endfunction
  391. function! s:assign_name()
  392. " Assign buffer name
  393. let prefix = '[Plugins]'
  394. let name = prefix
  395. let idx = 2
  396. while bufexists(name)
  397. let name = printf("%s (%s)", prefix, idx)
  398. let idx = idx + 1
  399. endwhile
  400. silent! execute "f ".fnameescape(name)
  401. endfunction
  402. function! s:do(pull, todo)
  403. for [name, spec] in items(a:todo)
  404. execute 'cd '.s:esc(spec.dir)
  405. if has_key(s:prev_update.new, name) || (a:pull &&
  406. \ !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"')))
  407. call append(3, '- Post-update hook for '. name .' ... ')
  408. let type = type(spec.do)
  409. if type == 1 " String
  410. call system(spec.do)
  411. let result = v:shell_error ? ('Exit status: '.v:shell_error) : 'Done!'
  412. elseif type == 2 " Funcref
  413. try
  414. call spec.do()
  415. let result = 'Done!'
  416. catch
  417. let result = 'Error: ' . v:exception
  418. endtry
  419. else
  420. let result = 'Error: Invalid type!'
  421. endif
  422. call setline(4, getline(4) . result)
  423. endif
  424. cd -
  425. endfor
  426. endfunction
  427. function! s:finish(pull)
  428. call append(3, '- Finishing ... ')
  429. redraw
  430. call s:apply()
  431. call s:syntax()
  432. call setline(4, getline(4) . 'Done!')
  433. normal! gg
  434. redraw
  435. let msgs = []
  436. if !empty(s:prev_update.errors)
  437. call add(msgs, "Press 'R' to retry.")
  438. endif
  439. if a:pull
  440. call add(msgs, "Press 'D' to see the updated changes.")
  441. endif
  442. echo join(msgs, ' ')
  443. endfunction
  444. function! s:retry()
  445. if empty(s:prev_update.errors)
  446. return
  447. endif
  448. call s:update_impl(s:prev_update.pull,
  449. \ extend(copy(s:prev_update.errors), [s:prev_update.threads]))
  450. endfunction
  451. function! s:is_managed(name)
  452. return has_key(g:plugs[a:name], 'uri')
  453. endfunction
  454. function! s:names(...)
  455. return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')
  456. endfunction
  457. function! s:update_impl(pull, args) abort
  458. let args = copy(a:args)
  459. let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
  460. \ remove(args, -1) : get(g:, 'plug_threads', 16)
  461. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  462. let todo = empty(args) ? filter(managed, '!get(v:val, "frozen", 0)') :
  463. \ filter(managed, 'index(args, v:key) >= 0')
  464. if empty(todo)
  465. echohl WarningMsg
  466. echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.'
  467. echohl None
  468. return
  469. endif
  470. call s:prepare()
  471. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  472. call append(1, '['. s:lpad('', len(todo)) .']')
  473. normal! 2G
  474. redraw
  475. if !isdirectory(g:plug_home)
  476. call mkdir(g:plug_home, 'p')
  477. endif
  478. let len = len(g:plugs)
  479. let s:prev_update = { 'errors': [], 'pull': a:pull, 'new': {}, 'threads': threads }
  480. if has('ruby') && threads > 1
  481. try
  482. let imd = &imd
  483. if s:mac_gui
  484. set noimd
  485. endif
  486. call s:update_parallel(a:pull, todo, threads)
  487. catch
  488. let lines = getline(4, '$')
  489. let printed = {}
  490. silent 4,$d
  491. for line in lines
  492. let name = get(matchlist(line, '^. \([^:]\+\):'), 1, '')
  493. if empty(name) || !has_key(printed, name)
  494. call append('$', line)
  495. if !empty(name)
  496. let printed[name] = 1
  497. if line[0] == 'x' && index(s:prev_update.errors, name) < 0
  498. call add(s:prev_update.errors, name)
  499. end
  500. endif
  501. endif
  502. endfor
  503. finally
  504. let &imd = imd
  505. endtry
  506. else
  507. call s:update_serial(a:pull, todo)
  508. endif
  509. call s:do(a:pull, filter(copy(todo), 'has_key(v:val, "do")'))
  510. if len(g:plugs) > len
  511. call plug#end()
  512. endif
  513. call s:finish(a:pull)
  514. endfunction
  515. function! s:extend(names)
  516. let s:extended = {}
  517. try
  518. command! -nargs=+ Plug call s:add(0, <args>)
  519. for name in a:names
  520. let plugfile = globpath(s:rtp(g:plugs[name]), s:plug_file)
  521. if filereadable(plugfile)
  522. execute "source ". s:esc(plugfile)
  523. endif
  524. endfor
  525. finally
  526. command! -nargs=+ Plug call s:add(1, <args>)
  527. endtry
  528. return s:extended
  529. endfunction
  530. function! s:update_progress(pull, cnt, bar, total)
  531. call setline(1, (a:pull ? 'Updating' : 'Installing').
  532. \ " plugins (".a:cnt."/".a:total.")")
  533. call s:progress_bar(2, a:bar, a:total)
  534. normal! 2G
  535. redraw
  536. endfunction
  537. function! s:update_serial(pull, todo)
  538. let st = reltime()
  539. let base = g:plug_home
  540. let todo = copy(a:todo)
  541. let total = len(todo)
  542. let done = {}
  543. let bar = ''
  544. while !empty(todo)
  545. for [name, spec] in items(todo)
  546. let done[name] = 1
  547. if isdirectory(spec.dir)
  548. execute 'cd '.s:esc(spec.dir)
  549. let [valid, msg] = s:git_valid(spec, 0, 0)
  550. if valid
  551. if a:pull
  552. let result =
  553. \ s:system(
  554. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  555. \ s:shellesc(spec.branch), s:shellesc(spec.branch)))
  556. let [result, error] = [result, v:shell_error != 0]
  557. else
  558. let [result, error] = ['Already installed', 0]
  559. endif
  560. else
  561. let result = msg
  562. let error = 1
  563. endif
  564. cd -
  565. else
  566. let result = s:system(
  567. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  568. \ s:shellesc(spec.uri),
  569. \ s:shellesc(spec.branch),
  570. \ s:shellesc(substitute(spec.dir, '[\/]\+$', '', '')),
  571. \ s:shellesc(spec.dir)))
  572. let error = v:shell_error != 0
  573. if !error | let s:prev_update.new[name] = 1 | endif
  574. endif
  575. let bar .= error ? 'x' : '='
  576. if error
  577. call add(s:prev_update.errors, name)
  578. endif
  579. call append(3, s:format_message(!error, name, result))
  580. call s:update_progress(a:pull, len(done), bar, total)
  581. endfor
  582. let extended = s:extend(keys(todo))
  583. if !empty(extended)
  584. let todo = filter(extended, '!has_key(done, v:key)')
  585. let total += len(todo)
  586. call s:update_progress(a:pull, len(done), bar, total)
  587. else
  588. break
  589. endif
  590. endwhile
  591. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  592. endfunction
  593. function! s:update_parallel(pull, todo, threads)
  594. ruby << EOF
  595. module PlugStream
  596. SEP = ["\r", "\n", nil]
  597. def get_line
  598. buffer = ''
  599. loop do
  600. char = readchar rescue return
  601. if SEP.include? char.chr
  602. buffer << $/
  603. break
  604. else
  605. buffer << char
  606. end
  607. end
  608. buffer
  609. end
  610. end unless defined?(PlugStream)
  611. def esc arg
  612. %["#{arg.gsub('"', '\"')}"]
  613. end
  614. require 'set'
  615. require 'thread'
  616. require 'fileutils'
  617. require 'timeout'
  618. running = true
  619. st = Time.now
  620. iswin = VIM::evaluate('s:is_win').to_i == 1
  621. pull = VIM::evaluate('a:pull').to_i == 1
  622. base = VIM::evaluate('g:plug_home')
  623. all = VIM::evaluate('copy(a:todo)')
  624. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  625. tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
  626. nthr = VIM::evaluate('a:threads').to_i
  627. maxy = VIM::evaluate('winheight(".")').to_i
  628. cd = iswin ? 'cd /d' : 'cd'
  629. tot = VIM::evaluate('len(a:todo)') || 0
  630. bar = ''
  631. skip = 'Already installed'
  632. mtx = Mutex.new
  633. take1 = proc { mtx.synchronize { running && all.shift } }
  634. logh = proc {
  635. cnt = bar.length
  636. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  637. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  638. VIM::command('normal! 2G')
  639. VIM::command('redraw') unless iswin
  640. }
  641. where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
  642. log = proc { |name, result, type|
  643. mtx.synchronize do
  644. ing = ![true, false].include?(type)
  645. bar += type ? '=' : 'x' unless ing
  646. b = case type
  647. when :install then '+' when :update then '*'
  648. when true, nil then '-' else
  649. VIM::command("call add(s:prev_update.errors, '#{name}')")
  650. 'x'
  651. end
  652. result =
  653. if type || type.nil?
  654. ["#{b} #{name}: #{result.lines.to_a.last}"]
  655. elsif result =~ /^Interrupted|^Timeout/
  656. ["#{b} #{name}: #{result}"]
  657. else
  658. ["#{b} #{name}"] + result.lines.map { |l| " " << l }
  659. end
  660. if lnum = where.call(name)
  661. $curbuf.delete lnum
  662. lnum = 4 if ing && lnum > maxy
  663. end
  664. result.each_with_index do |line, offset|
  665. $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
  666. end
  667. logh.call
  668. end
  669. }
  670. bt = proc { |cmd, name, type|
  671. tried = timeout = 0
  672. begin
  673. tried += 1
  674. timeout += limit
  675. fd = nil
  676. data = ''
  677. if iswin
  678. Timeout::timeout(timeout) do
  679. tmp = VIM::evaluate('tempname()')
  680. system("#{cmd} > #{tmp}")
  681. data = File.read(tmp).chomp
  682. File.unlink tmp rescue nil
  683. end
  684. else
  685. fd = IO.popen(cmd).extend(PlugStream)
  686. first_line = true
  687. log_prob = 1.0 / nthr
  688. while line = Timeout::timeout(timeout) { fd.get_line }
  689. data << line
  690. log.call name, line.chomp, type if name && (first_line || rand < log_prob)
  691. first_line = false
  692. end
  693. fd.close
  694. end
  695. [$? == 0, data.chomp]
  696. rescue Timeout::Error, Interrupt => e
  697. if fd && !fd.closed?
  698. pids = [fd.pid]
  699. unless `which pgrep`.empty?
  700. children = pids
  701. until children.empty?
  702. children = children.map { |pid|
  703. `pgrep -P #{pid}`.lines.map { |l| l.chomp }
  704. }.flatten
  705. pids += children
  706. end
  707. end
  708. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  709. fd.close
  710. end
  711. if e.is_a?(Timeout::Error) && tried < tries
  712. 3.downto(1) do |countdown|
  713. s = countdown > 1 ? 's' : ''
  714. log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
  715. sleep 1
  716. end
  717. log.call name, 'Retrying ...', type
  718. retry
  719. end
  720. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  721. end
  722. }
  723. main = Thread.current
  724. threads = []
  725. watcher = Thread.new {
  726. while VIM::evaluate('getchar(1)')
  727. sleep 0.1
  728. end
  729. mtx.synchronize do
  730. running = false
  731. threads.each { |t| t.raise Interrupt }
  732. end
  733. threads.each { |t| t.join rescue nil }
  734. main.kill
  735. }
  736. refresh = Thread.new {
  737. while true
  738. mtx.synchronize do
  739. break unless running
  740. VIM::command('noautocmd normal! a')
  741. end
  742. sleep 0.2
  743. end
  744. } if VIM::evaluate('s:mac_gui') == 1
  745. processed = Set.new
  746. progress = iswin ? '' : '--progress'
  747. until all.empty?
  748. names = all.keys
  749. processed.merge names
  750. [names.length, nthr].min.times do
  751. mtx.synchronize do
  752. threads << Thread.new {
  753. while pair = take1.call
  754. name = pair.first
  755. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  756. branch = esc branch
  757. subm = "git submodule update --init --recursive 2>&1"
  758. exists = File.directory? dir
  759. ok, result =
  760. if exists
  761. dir = esc dir
  762. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil
  763. current_uri = data.lines.to_a.last
  764. if !ret
  765. if data =~ /^Interrupted|^Timeout/
  766. [false, data]
  767. else
  768. [false, [data.chomp, "PlugClean required."].join($/)]
  769. end
  770. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  771. [false, ["Invalid URI: #{current_uri}",
  772. "Expected: #{uri}",
  773. "PlugClean required."].join($/)]
  774. else
  775. if pull
  776. log.call name, 'Updating ...', :update
  777. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull origin #{branch} #{progress} 2>&1 && #{subm})", name, :update
  778. else
  779. [true, skip]
  780. end
  781. end
  782. else
  783. d = esc dir.sub(%r{[\\/]+$}, '')
  784. log.call name, 'Installing ...', :install
  785. bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install
  786. end
  787. mtx.synchronize { VIM::command("let s:prev_update.new['#{name}'] = 1") } if !exists && ok
  788. log.call name, result, ok
  789. end
  790. } if running
  791. end
  792. end
  793. threads.each { |t| t.join rescue nil }
  794. mtx.synchronize { threads.clear }
  795. extended = Hash[(VIM::evaluate("s:extend(#{names.inspect})") || {}).reject { |k, _|
  796. processed.include? k
  797. }]
  798. tot += extended.length
  799. all.merge!(extended)
  800. logh.call
  801. end
  802. refresh.kill if refresh
  803. watcher.kill
  804. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  805. EOF
  806. endfunction
  807. function! s:shellesc(arg)
  808. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  809. endfunction
  810. function! s:glob_dir(path)
  811. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  812. endfunction
  813. function! s:progress_bar(line, bar, total)
  814. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  815. endfunction
  816. function! s:compare_git_uri(a, b)
  817. let a = substitute(a:a, 'git:@', '', '')
  818. let b = substitute(a:b, 'git:@', '', '')
  819. return a ==# b
  820. endfunction
  821. function! s:format_message(ok, name, message)
  822. if a:ok
  823. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  824. else
  825. let lines = map(split(a:message, '\n'), '" ".v:val')
  826. return extend([printf('x %s:', a:name)], lines)
  827. endif
  828. endfunction
  829. function! s:system(cmd)
  830. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  831. endfunction
  832. function! s:system_chomp(str)
  833. let ret = s:system(a:str)
  834. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  835. endfunction
  836. function! s:git_valid(spec, check_branch, cd)
  837. let ret = 1
  838. let msg = 'OK'
  839. if isdirectory(a:spec.dir)
  840. if a:cd | execute "cd " . s:esc(a:spec.dir) | endif
  841. let result = split(s:system("git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"), '\n')
  842. let remote = result[-1]
  843. if v:shell_error
  844. let msg = join([remote, "PlugClean required."], "\n")
  845. let ret = 0
  846. elseif !s:compare_git_uri(remote, a:spec.uri)
  847. let msg = join(['Invalid URI: '.remote,
  848. \ 'Expected: '.a:spec.uri,
  849. \ "PlugClean required."], "\n")
  850. let ret = 0
  851. elseif a:check_branch
  852. let branch = result[0]
  853. if a:spec.branch !=# branch
  854. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1')
  855. if a:spec.branch !=# tag
  856. let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
  857. \ (empty(tag) ? branch : tag), a:spec.branch)
  858. let ret = 0
  859. endif
  860. endif
  861. endif
  862. if a:cd | cd - | endif
  863. else
  864. let msg = 'Not found'
  865. let ret = 0
  866. endif
  867. return [ret, msg]
  868. endfunction
  869. function! s:clean(force)
  870. call s:prepare()
  871. call append(0, 'Searching for unused plugins in '.g:plug_home)
  872. call append(1, '')
  873. " List of valid directories
  874. let dirs = []
  875. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  876. let [cnt, total] = [0, len(managed)]
  877. for spec in values(managed)
  878. if s:git_valid(spec, 0, 1)[0]
  879. call add(dirs, spec.dir)
  880. endif
  881. let cnt += 1
  882. call s:progress_bar(2, repeat('=', cnt), total)
  883. normal! 2G
  884. redraw
  885. endfor
  886. let allowed = {}
  887. for dir in dirs
  888. let allowed[dir] = 1
  889. for child in s:glob_dir(dir)
  890. let allowed[child] = 1
  891. endfor
  892. endfor
  893. let todo = []
  894. let found = sort(s:glob_dir(g:plug_home))
  895. while !empty(found)
  896. let f = remove(found, 0)
  897. if !has_key(allowed, f) && isdirectory(f)
  898. call add(todo, f)
  899. call append(line('$'), '- ' . f)
  900. let found = filter(found, 'stridx(v:val, f) != 0')
  901. end
  902. endwhile
  903. normal! G
  904. redraw
  905. if empty(todo)
  906. call append(line('$'), 'Already clean.')
  907. else
  908. call inputsave()
  909. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  910. call inputrestore()
  911. if yes
  912. for dir in todo
  913. if isdirectory(dir)
  914. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  915. endif
  916. endfor
  917. call append(line('$'), 'Removed.')
  918. else
  919. call append(line('$'), 'Cancelled.')
  920. endif
  921. endif
  922. normal! G
  923. endfunction
  924. function! s:upgrade()
  925. if executable('curl')
  926. let mee = s:shellesc(s:me)
  927. let new = s:shellesc(s:me . '.new')
  928. echo "Downloading ". s:plug_source
  929. redraw
  930. let mv = s:is_win ? 'move /Y' : 'mv -f'
  931. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  932. call system(printf(
  933. \ "curl -fLo %s %s && ".cp." %s %s.old && ".mv." %s %s",
  934. \ new, s:plug_source, mee, mee, new, mee))
  935. if v:shell_error == 0
  936. unlet g:loaded_plug
  937. echo "Downloaded ". s:plug_source
  938. return 1
  939. else
  940. echoerr "Error upgrading vim-plug"
  941. return 0
  942. endif
  943. elseif has('ruby')
  944. echo "Downloading ". s:plug_source
  945. ruby << EOF
  946. require 'open-uri'
  947. require 'fileutils'
  948. me = VIM::evaluate('s:me')
  949. old = me + '.old'
  950. new = me + '.new'
  951. File.open(new, 'w') do |f|
  952. f << open(VIM::evaluate('s:plug_source')).read
  953. end
  954. FileUtils.cp me, old
  955. File.rename new, me
  956. EOF
  957. unlet g:loaded_plug
  958. echo "Downloaded ". s:plug_source
  959. return 1
  960. else
  961. echoerr "curl executable or ruby support not found"
  962. return 0
  963. endif
  964. endfunction
  965. function! s:status()
  966. call s:prepare()
  967. call append(0, 'Checking plugins')
  968. call append(1, '')
  969. let ecnt = 0
  970. let [cnt, total] = [0, len(g:plugs)]
  971. for [name, spec] in items(g:plugs)
  972. if has_key(spec, 'uri')
  973. if isdirectory(spec.dir)
  974. let [valid, msg] = s:git_valid(spec, 1, 1)
  975. else
  976. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  977. endif
  978. else
  979. if isdirectory(spec.dir)
  980. let [valid, msg] = [1, 'OK']
  981. else
  982. let [valid, msg] = [0, 'Not found.']
  983. endif
  984. endif
  985. let cnt += 1
  986. let ecnt += !valid
  987. call s:progress_bar(2, repeat('=', cnt), total)
  988. call append(3, s:format_message(valid, name, msg))
  989. normal! 2G
  990. redraw
  991. endfor
  992. call setline(1, 'Finished. '.ecnt.' error(s).')
  993. normal! gg
  994. endfunction
  995. function! s:is_preview_window_open()
  996. silent! wincmd P
  997. if &previewwindow
  998. wincmd p
  999. return 1
  1000. endif
  1001. return 0
  1002. endfunction
  1003. function! s:preview_commit()
  1004. if b:plug_preview < 0
  1005. let b:plug_preview = !s:is_preview_window_open()
  1006. endif
  1007. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  1008. if !empty(sha)
  1009. let lnum = line('.')
  1010. while lnum > 1
  1011. let lnum -= 1
  1012. let line = getline(lnum)
  1013. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  1014. if !empty(name)
  1015. let dir = g:plugs[name].dir
  1016. if isdirectory(dir)
  1017. execute 'cd '.s:esc(dir)
  1018. execute 'pedit '.sha
  1019. wincmd P
  1020. setlocal filetype=git buftype=nofile nobuflisted
  1021. execute 'silent read !git show '.sha
  1022. normal! ggdd
  1023. wincmd p
  1024. cd -
  1025. endif
  1026. break
  1027. endif
  1028. endwhile
  1029. endif
  1030. endfunction
  1031. function! s:section(flags)
  1032. call search('\(^- \)\@<=.', a:flags)
  1033. endfunction
  1034. function! s:diff()
  1035. call s:prepare()
  1036. call append(0, 'Collecting updated changes ...')
  1037. normal! gg
  1038. redraw
  1039. let cnt = 0
  1040. for [k, v] in items(g:plugs)
  1041. if !isdirectory(v.dir) || !s:is_managed(k)
  1042. continue
  1043. endif
  1044. execute 'cd '.s:esc(v.dir)
  1045. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"')
  1046. if !v:shell_error && !empty(diff)
  1047. call append(1, '')
  1048. call append(2, '- '.k.':')
  1049. call append(3, map(split(diff, '\n'), '" ". v:val'))
  1050. let cnt += 1
  1051. normal! gg
  1052. redraw
  1053. endif
  1054. cd -
  1055. endfor
  1056. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  1057. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  1058. normal! gg
  1059. endfunction
  1060. let s:first_rtp = s:esc(get(split(&rtp, ','), 0, ''))
  1061. let s:last_rtp = s:esc(get(split(&rtp, ','), -1, ''))
  1062. let &cpo = s:cpo_save
  1063. unlet s:cpo_save