plug.vim 32 KB

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