plug.vim 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  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! plug#load(...)
  241. if a:0 == 0
  242. return s:err('Argument missing: plugin name(s) required')
  243. endif
  244. if !exists('g:plugs')
  245. return s:err('plug#begin is not called')
  246. endif
  247. let unknowns = filter(copy(a:000), '!has_key(g:plugs, v:val)')
  248. if !empty(unknowns)
  249. let s = len(unknowns) > 1 ? 's' : ''
  250. return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
  251. end
  252. for name in a:000
  253. call s:lod(g:plugs[name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  254. endfor
  255. call s:reorg_rtp()
  256. silent! doautocmd BufRead
  257. return 1
  258. endfunction
  259. function! s:lod(plug, types)
  260. let rtp = s:rtp(a:plug)
  261. call s:add_rtp(rtp)
  262. for dir in a:types
  263. call s:source(rtp, dir.'/**/*.vim')
  264. endfor
  265. endfunction
  266. function! s:lod_ft(pat, names)
  267. for name in a:names
  268. call s:lod(g:plugs[name], ['plugin', 'after/plugin'])
  269. endfor
  270. call s:reorg_rtp()
  271. execute 'autocmd! PlugLOD FileType ' . a:pat
  272. silent! doautocmd filetypeplugin FileType
  273. silent! doautocmd filetypeindent FileType
  274. endfunction
  275. function! s:lod_cmd(cmd, bang, l1, l2, args, name)
  276. execute 'delc '.a:cmd
  277. call s:lod(g:plugs[a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  278. call s:reorg_rtp()
  279. execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
  280. endfunction
  281. function! s:lod_map(map, name, prefix)
  282. execute 'unmap '.a:map
  283. execute 'iunmap '.a:map
  284. call s:lod(g:plugs[a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  285. call s:reorg_rtp()
  286. let extra = ''
  287. while 1
  288. let c = getchar(0)
  289. if c == 0
  290. break
  291. endif
  292. let extra .= nr2char(c)
  293. endwhile
  294. call feedkeys(a:prefix . substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
  295. endfunction
  296. function! s:add(repo, ...)
  297. if a:0 > 1
  298. return s:err('Invalid number of arguments (1..2)')
  299. endif
  300. try
  301. let repo = s:trim(a:repo)
  302. let name = fnamemodify(repo, ':t:s?\.git$??')
  303. let spec = extend(s:infer_properties(name, repo),
  304. \ a:0 == 1 ? s:parse_options(a:1) : s:base_spec)
  305. let g:plugs[name] = spec
  306. let g:plugs_order += [name]
  307. catch
  308. return s:err(v:exception)
  309. endtry
  310. endfunction
  311. function! s:parse_options(arg)
  312. let opts = copy(s:base_spec)
  313. let type = type(a:arg)
  314. if type == s:TYPE.string
  315. let opts.branch = a:arg
  316. elseif type == s:TYPE.dict
  317. call extend(opts, a:arg)
  318. if has_key(opts, 'tag')
  319. let opts.branch = remove(opts, 'tag')
  320. endif
  321. if has_key(opts, 'dir')
  322. let opts.dir = s:dirpath(expand(opts.dir))
  323. endif
  324. else
  325. throw 'Invalid argument type (expected: string or dictionary)'
  326. endif
  327. return opts
  328. endfunction
  329. function! s:infer_properties(name, repo)
  330. let repo = a:repo
  331. if s:is_local_plug(repo)
  332. return { 'dir': s:dirpath(expand(repo)) }
  333. else
  334. if repo =~ ':'
  335. let uri = repo
  336. else
  337. if repo !~ '/'
  338. let repo = 'vim-scripts/'. repo
  339. endif
  340. let uri = 'https://git:@github.com/' . repo . '.git'
  341. endif
  342. let dir = s:dirpath( fnamemodify(join([g:plug_home, a:name], '/'), ':p') )
  343. return { 'dir': dir, 'uri': uri }
  344. endif
  345. endfunction
  346. function! s:install(force, ...)
  347. call s:update_impl(0, a:force, a:000)
  348. endfunction
  349. function! s:update(force, ...)
  350. call s:update_impl(1, a:force, a:000)
  351. endfunction
  352. function! plug#helptags()
  353. if !exists('g:plugs')
  354. return s:err('plug#begin is not called')
  355. endif
  356. for spec in values(g:plugs)
  357. let docd = join([spec.dir, 'doc'], '/')
  358. if isdirectory(docd)
  359. silent! execute 'helptags '. s:esc(docd)
  360. endif
  361. endfor
  362. return 1
  363. endfunction
  364. function! s:syntax()
  365. syntax clear
  366. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
  367. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
  368. syn match plugNumber /[0-9]\+[0-9.]*/ contained
  369. syn match plugBracket /[[\]]/ contained
  370. syn match plugX /x/ contained
  371. syn match plugDash /^-/
  372. syn match plugPlus /^+/
  373. syn match plugStar /^*/
  374. syn match plugMessage /\(^- \)\@<=.*/
  375. syn match plugName /\(^- \)\@<=[^ ]*:/
  376. syn match plugInstall /\(^+ \)\@<=[^:]*/
  377. syn match plugUpdate /\(^* \)\@<=[^:]*/
  378. syn match plugCommit /^ [0-9a-z]\{7} .*/ contains=plugRelDate,plugSha
  379. syn match plugSha /\(^ \)\@<=[0-9a-z]\{7}/ contained
  380. syn match plugRelDate /([^)]*)$/ contained
  381. syn match plugError /^x.*/
  382. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  383. hi def link plug1 Title
  384. hi def link plug2 Repeat
  385. hi def link plugX Exception
  386. hi def link plugBracket Structure
  387. hi def link plugNumber Number
  388. hi def link plugDash Special
  389. hi def link plugPlus Constant
  390. hi def link plugStar Boolean
  391. hi def link plugMessage Function
  392. hi def link plugName Label
  393. hi def link plugInstall Function
  394. hi def link plugUpdate Type
  395. hi def link plugError Error
  396. hi def link plugRelDate Comment
  397. hi def link plugSha Identifier
  398. endfunction
  399. function! s:lpad(str, len)
  400. return a:str . repeat(' ', a:len - len(a:str))
  401. endfunction
  402. function! s:lastline(msg)
  403. let lines = split(a:msg, '\n')
  404. return get(lines, -1, '')
  405. endfunction
  406. function! s:prepare()
  407. if bufexists(s:plug_buf)
  408. let winnr = bufwinnr(s:plug_buf)
  409. if winnr < 0
  410. vertical topleft new
  411. execute 'buffer ' . s:plug_buf
  412. else
  413. execute winnr . 'wincmd w'
  414. endif
  415. silent %d _
  416. else
  417. vertical topleft new
  418. nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>q<cr>
  419. nnoremap <silent> <buffer> R :silent! call <SID>retry()<cr>
  420. nnoremap <silent> <buffer> D :PlugDiff<cr>
  421. nnoremap <silent> <buffer> S :PlugStatus<cr>
  422. nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
  423. nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
  424. let b:plug_preview = -1
  425. let s:plug_buf = winbufnr(0)
  426. call s:assign_name()
  427. endif
  428. silent! unmap <buffer> <cr>
  429. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  430. setf vim-plug
  431. call s:syntax()
  432. endfunction
  433. function! s:assign_name()
  434. " Assign buffer name
  435. let prefix = '[Plugins]'
  436. let name = prefix
  437. let idx = 2
  438. while bufexists(name)
  439. let name = printf('%s (%s)', prefix, idx)
  440. let idx = idx + 1
  441. endwhile
  442. silent! execute 'f '.fnameescape(name)
  443. endfunction
  444. function! s:do(pull, force, todo)
  445. for [name, spec] in items(a:todo)
  446. if !isdirectory(spec.dir)
  447. continue
  448. endif
  449. execute 'cd '.s:esc(spec.dir)
  450. let installed = has_key(s:prev_update.new, name)
  451. let updated = installed ? 0 :
  452. \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"')))
  453. if a:force || installed || updated
  454. call append(3, '- Post-update hook for '. name .' ... ')
  455. let type = type(spec.do)
  456. if type == s:TYPE.string
  457. try
  458. " FIXME: Escaping is incomplete. We could use shellescape with eval,
  459. " but it won't work on Windows.
  460. let g:_plug_do = '!'.escape(spec.do, '#!%')
  461. execute "normal! :execute g:_plug_do\<cr>\<cr>"
  462. finally
  463. let result = v:shell_error ? ('Exit status: '.v:shell_error) : 'Done!'
  464. unlet g:_plug_do
  465. endtry
  466. elseif type == s:TYPE.funcref
  467. try
  468. let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
  469. call spec.do({ 'name': name, 'status': status, 'force': a:force })
  470. let result = 'Done!'
  471. catch
  472. let result = 'Error: ' . v:exception
  473. endtry
  474. else
  475. let result = 'Error: Invalid type!'
  476. endif
  477. call setline(4, getline(4) . result)
  478. endif
  479. cd -
  480. endfor
  481. endfunction
  482. function! s:finish(pull)
  483. call append(3, '- Finishing ... ')
  484. redraw
  485. call plug#helptags()
  486. call plug#end()
  487. call setline(4, getline(4) . 'Done!')
  488. normal! gg
  489. call s:syntax()
  490. redraw
  491. let msgs = []
  492. if !empty(s:prev_update.errors)
  493. call add(msgs, "Press 'R' to retry.")
  494. endif
  495. if a:pull
  496. call add(msgs, "Press 'D' to see the updated changes.")
  497. endif
  498. echo join(msgs, ' ')
  499. endfunction
  500. function! s:retry()
  501. if empty(s:prev_update.errors)
  502. return
  503. endif
  504. call s:update_impl(s:prev_update.pull, s:prev_update.force,
  505. \ extend(copy(s:prev_update.errors), [s:prev_update.threads]))
  506. endfunction
  507. function! s:is_managed(name)
  508. return has_key(g:plugs[a:name], 'uri')
  509. endfunction
  510. function! s:names(...)
  511. return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')
  512. endfunction
  513. function! s:update_impl(pull, force, args) abort
  514. let st = reltime()
  515. let args = copy(a:args)
  516. let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
  517. \ remove(args, -1) : get(g:, 'plug_threads', 16)
  518. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  519. let todo = empty(args) ? filter(managed, '!v:val.frozen') :
  520. \ filter(managed, 'index(args, v:key) >= 0')
  521. if empty(todo)
  522. echohl WarningMsg
  523. echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.'
  524. echohl None
  525. return
  526. endif
  527. if !isdirectory(g:plug_home)
  528. try
  529. call mkdir(g:plug_home, 'p')
  530. catch
  531. return s:err(printf('Invalid plug directory: %s.'
  532. \ 'Try to call plug#begin with a valid directory', g:plug_home))
  533. endtry
  534. endif
  535. call s:prepare()
  536. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  537. call append(1, '['. s:lpad('', len(todo)) .']')
  538. normal! 2G
  539. redraw
  540. let s:prev_update = { 'errors': [], 'pull': a:pull, 'force': a:force, 'new': {}, 'threads': threads }
  541. if has('ruby') && threads > 1
  542. try
  543. let imd = &imd
  544. if s:mac_gui
  545. set noimd
  546. endif
  547. call s:update_parallel(a:pull, todo, threads)
  548. catch
  549. let lines = getline(4, '$')
  550. let printed = {}
  551. silent 4,$d
  552. for line in lines
  553. let name = get(matchlist(line, '^. \([^:]\+\):'), 1, '')
  554. if empty(name) || !has_key(printed, name)
  555. call append('$', line)
  556. if !empty(name)
  557. let printed[name] = 1
  558. if line[0] == 'x' && index(s:prev_update.errors, name) < 0
  559. call add(s:prev_update.errors, name)
  560. end
  561. endif
  562. endif
  563. endfor
  564. finally
  565. let &imd = imd
  566. endtry
  567. else
  568. call s:update_serial(a:pull, todo)
  569. endif
  570. call s:do(a:pull, a:force, filter(copy(todo), 'has_key(v:val, "do")'))
  571. call s:finish(a:pull)
  572. call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(st)))[0] . ' sec.')
  573. endfunction
  574. function! s:update_progress(pull, cnt, bar, total)
  575. call setline(1, (a:pull ? 'Updating' : 'Installing').
  576. \ ' plugins ('.a:cnt.'/'.a:total.')')
  577. call s:progress_bar(2, a:bar, a:total)
  578. normal! 2G
  579. redraw
  580. endfunction
  581. function! s:update_serial(pull, todo)
  582. let base = g:plug_home
  583. let todo = copy(a:todo)
  584. let total = len(todo)
  585. let done = {}
  586. let bar = ''
  587. for [name, spec] in items(todo)
  588. let done[name] = 1
  589. if isdirectory(spec.dir)
  590. execute 'cd '.s:esc(spec.dir)
  591. let [valid, msg] = s:git_valid(spec, 0, 0)
  592. if valid
  593. let result = a:pull ?
  594. \ s:system(
  595. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  596. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  597. let error = a:pull ? v:shell_error != 0 : 0
  598. else
  599. let result = msg
  600. let error = 1
  601. endif
  602. cd -
  603. else
  604. let result = s:system(
  605. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  606. \ s:shellesc(spec.uri),
  607. \ s:shellesc(spec.branch),
  608. \ s:shellesc(s:trim(spec.dir)),
  609. \ s:shellesc(spec.dir)))
  610. let error = v:shell_error != 0
  611. if !error | let s:prev_update.new[name] = 1 | endif
  612. endif
  613. let bar .= error ? 'x' : '='
  614. if error
  615. call add(s:prev_update.errors, name)
  616. endif
  617. call append(3, s:format_message(!error, name, result))
  618. call s:update_progress(a:pull, len(done), bar, total)
  619. endfor
  620. endfunction
  621. function! s:update_parallel(pull, todo, threads)
  622. ruby << EOF
  623. module PlugStream
  624. SEP = ["\r", "\n", nil]
  625. def get_line
  626. buffer = ''
  627. loop do
  628. char = readchar rescue return
  629. if SEP.include? char.chr
  630. buffer << $/
  631. break
  632. else
  633. buffer << char
  634. end
  635. end
  636. buffer
  637. end
  638. end unless defined?(PlugStream)
  639. def esc arg
  640. %["#{arg.gsub('"', '\"')}"]
  641. end
  642. def killall pid
  643. pids = [pid]
  644. unless `which pgrep`.empty?
  645. children = pids
  646. until children.empty?
  647. children = children.map { |pid|
  648. `pgrep -P #{pid}`.lines.map { |l| l.chomp }
  649. }.flatten
  650. pids += children
  651. end
  652. end
  653. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  654. end
  655. require 'thread'
  656. require 'fileutils'
  657. require 'timeout'
  658. running = true
  659. iswin = VIM::evaluate('s:is_win').to_i == 1
  660. pull = VIM::evaluate('a:pull').to_i == 1
  661. base = VIM::evaluate('g:plug_home')
  662. all = VIM::evaluate('a:todo')
  663. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  664. tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
  665. nthr = VIM::evaluate('a:threads').to_i
  666. maxy = VIM::evaluate('winheight(".")').to_i
  667. cd = iswin ? 'cd /d' : 'cd'
  668. tot = VIM::evaluate('len(a:todo)') || 0
  669. bar = ''
  670. skip = 'Already installed'
  671. mtx = Mutex.new
  672. take1 = proc { mtx.synchronize { running && all.shift } }
  673. logh = proc {
  674. cnt = bar.length
  675. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  676. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  677. VIM::command('normal! 2G')
  678. VIM::command('redraw') unless iswin
  679. }
  680. where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
  681. log = proc { |name, result, type|
  682. mtx.synchronize do
  683. ing = ![true, false].include?(type)
  684. bar += type ? '=' : 'x' unless ing
  685. b = case type
  686. when :install then '+' when :update then '*'
  687. when true, nil then '-' else
  688. VIM::command("call add(s:prev_update.errors, '#{name}')")
  689. 'x'
  690. end
  691. result =
  692. if type || type.nil?
  693. ["#{b} #{name}: #{result.lines.to_a.last}"]
  694. elsif result =~ /^Interrupted|^Timeout/
  695. ["#{b} #{name}: #{result}"]
  696. else
  697. ["#{b} #{name}"] + result.lines.map { |l| " " << l }
  698. end
  699. if lnum = where.call(name)
  700. $curbuf.delete lnum
  701. lnum = 4 if ing && lnum > maxy
  702. end
  703. result.each_with_index do |line, offset|
  704. $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
  705. end
  706. logh.call
  707. end
  708. }
  709. bt = proc { |cmd, name, type|
  710. tried = timeout = 0
  711. begin
  712. tried += 1
  713. timeout += limit
  714. fd = nil
  715. data = ''
  716. if iswin
  717. Timeout::timeout(timeout) do
  718. tmp = VIM::evaluate('tempname()')
  719. system("#{cmd} > #{tmp}")
  720. data = File.read(tmp).chomp
  721. File.unlink tmp rescue nil
  722. end
  723. else
  724. fd = IO.popen(cmd).extend(PlugStream)
  725. first_line = true
  726. log_prob = 1.0 / nthr
  727. while line = Timeout::timeout(timeout) { fd.get_line }
  728. data << line
  729. log.call name, line.chomp, type if name && (first_line || rand < log_prob)
  730. first_line = false
  731. end
  732. fd.close
  733. end
  734. [$? == 0, data.chomp]
  735. rescue Timeout::Error, Interrupt => e
  736. if fd && !fd.closed?
  737. killall fd.pid
  738. fd.close
  739. end
  740. if e.is_a?(Timeout::Error) && tried < tries
  741. 3.downto(1) do |countdown|
  742. s = countdown > 1 ? 's' : ''
  743. log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
  744. sleep 1
  745. end
  746. log.call name, 'Retrying ...', type
  747. retry
  748. end
  749. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  750. end
  751. }
  752. main = Thread.current
  753. threads = []
  754. watcher = Thread.new {
  755. while VIM::evaluate('getchar(1)')
  756. sleep 0.1
  757. end
  758. mtx.synchronize do
  759. running = false
  760. threads.each { |t| t.raise Interrupt }
  761. end
  762. threads.each { |t| t.join rescue nil }
  763. main.kill
  764. }
  765. refresh = Thread.new {
  766. while true
  767. mtx.synchronize do
  768. break unless running
  769. VIM::command('noautocmd normal! a')
  770. end
  771. sleep 0.2
  772. end
  773. } if VIM::evaluate('s:mac_gui') == 1
  774. progress = iswin ? '' : '--progress'
  775. [all.length, nthr].min.times do
  776. mtx.synchronize do
  777. threads << Thread.new {
  778. while pair = take1.call
  779. name = pair.first
  780. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  781. branch = esc branch
  782. subm = "git submodule update --init --recursive 2>&1"
  783. exists = File.directory? dir
  784. ok, result =
  785. if exists
  786. dir = esc dir
  787. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil
  788. current_uri = data.lines.to_a.last
  789. if !ret
  790. if data =~ /^Interrupted|^Timeout/
  791. [false, data]
  792. else
  793. [false, [data.chomp, "PlugClean required."].join($/)]
  794. end
  795. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  796. [false, ["Invalid URI: #{current_uri}",
  797. "Expected: #{uri}",
  798. "PlugClean required."].join($/)]
  799. else
  800. if pull
  801. log.call name, 'Updating ...', :update
  802. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull origin #{branch} #{progress} 2>&1 && #{subm})", name, :update
  803. else
  804. [true, skip]
  805. end
  806. end
  807. else
  808. d = esc dir.sub(%r{[\\/]+$}, '')
  809. log.call name, 'Installing ...', :install
  810. bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install
  811. end
  812. mtx.synchronize { VIM::command("let s:prev_update.new['#{name}'] = 1") } if !exists && ok
  813. log.call name, result, ok
  814. end
  815. } if running
  816. end
  817. end
  818. threads.each { |t| t.join rescue nil }
  819. logh.call
  820. refresh.kill if refresh
  821. watcher.kill
  822. EOF
  823. endfunction
  824. function! s:shellesc(arg)
  825. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  826. endfunction
  827. function! s:glob_dir(path)
  828. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  829. endfunction
  830. function! s:progress_bar(line, bar, total)
  831. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  832. endfunction
  833. function! s:compare_git_uri(a, b)
  834. let a = substitute(a:a, 'git:@', '', '')
  835. let b = substitute(a:b, 'git:@', '', '')
  836. return a ==# b
  837. endfunction
  838. function! s:format_message(ok, name, message)
  839. if a:ok
  840. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  841. else
  842. let lines = map(split(a:message, '\n'), '" ".v:val')
  843. return extend([printf('x %s:', a:name)], lines)
  844. endif
  845. endfunction
  846. function! s:system(cmd)
  847. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  848. endfunction
  849. function! s:system_chomp(str)
  850. let ret = s:system(a:str)
  851. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  852. endfunction
  853. function! s:git_valid(spec, check_branch, cd)
  854. let ret = 1
  855. let msg = 'OK'
  856. if isdirectory(a:spec.dir)
  857. if a:cd | execute 'cd ' . s:esc(a:spec.dir) | endif
  858. let result = split(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url'), '\n')
  859. let remote = result[-1]
  860. if v:shell_error
  861. let msg = join([remote, 'PlugClean required.'], "\n")
  862. let ret = 0
  863. elseif !s:compare_git_uri(remote, a:spec.uri)
  864. let msg = join(['Invalid URI: '.remote,
  865. \ 'Expected: '.a:spec.uri,
  866. \ 'PlugClean required.'], "\n")
  867. let ret = 0
  868. elseif a:check_branch
  869. let branch = result[0]
  870. if a:spec.branch !=# branch
  871. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1')
  872. if a:spec.branch !=# tag
  873. let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
  874. \ (empty(tag) ? branch : tag), a:spec.branch)
  875. let ret = 0
  876. endif
  877. endif
  878. endif
  879. if a:cd | cd - | endif
  880. else
  881. let msg = 'Not found'
  882. let ret = 0
  883. endif
  884. return [ret, msg]
  885. endfunction
  886. function! s:clean(force)
  887. call s:prepare()
  888. call append(0, 'Searching for unused plugins in '.g:plug_home)
  889. call append(1, '')
  890. " List of valid directories
  891. let dirs = []
  892. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  893. let [cnt, total] = [0, len(managed)]
  894. for spec in values(managed)
  895. if s:git_valid(spec, 0, 1)[0]
  896. call add(dirs, spec.dir)
  897. endif
  898. let cnt += 1
  899. call s:progress_bar(2, repeat('=', cnt), total)
  900. normal! 2G
  901. redraw
  902. endfor
  903. let allowed = {}
  904. for dir in dirs
  905. let allowed[dir] = 1
  906. for child in s:glob_dir(dir)
  907. let allowed[child] = 1
  908. endfor
  909. endfor
  910. let todo = []
  911. let found = sort(s:glob_dir(g:plug_home))
  912. while !empty(found)
  913. let f = remove(found, 0)
  914. if !has_key(allowed, f) && isdirectory(f)
  915. call add(todo, f)
  916. call append(line('$'), '- ' . f)
  917. let found = filter(found, 'stridx(v:val, f) != 0')
  918. end
  919. endwhile
  920. normal! G
  921. redraw
  922. if empty(todo)
  923. call append(line('$'), 'Already clean.')
  924. else
  925. call inputsave()
  926. let yes = a:force || (input('Proceed? (Y/N) ') =~? '^y')
  927. call inputrestore()
  928. if yes
  929. for dir in todo
  930. if isdirectory(dir)
  931. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  932. endif
  933. endfor
  934. call append(line('$'), 'Removed.')
  935. else
  936. call append(line('$'), 'Cancelled.')
  937. endif
  938. endif
  939. normal! G
  940. endfunction
  941. function! s:upgrade()
  942. if executable('curl')
  943. let mee = s:shellesc(s:me)
  944. let new = s:shellesc(s:me . '.new')
  945. echo 'Downloading '. s:plug_source
  946. redraw
  947. let mv = s:is_win ? 'move /Y' : 'mv -f'
  948. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  949. call system(printf(
  950. \ 'curl -fLo %s %s && '.cp.' %s %s.old && '.mv.' %s %s',
  951. \ new, s:plug_source, mee, mee, new, mee))
  952. if v:shell_error
  953. return s:err('Error upgrading vim-plug')
  954. endif
  955. elseif has('ruby')
  956. echo 'Downloading '. s:plug_source
  957. try
  958. ruby << EOF
  959. require 'open-uri'
  960. require 'fileutils'
  961. me = VIM::evaluate('s:me')
  962. old = me + '.old'
  963. new = me + '.new'
  964. File.open(new, 'w') do |f|
  965. f << open(VIM::evaluate('s:plug_source')).read
  966. end
  967. FileUtils.cp me, old
  968. File.rename new, me
  969. EOF
  970. catch
  971. return s:err('Error upgrading vim-plug')
  972. endtry
  973. else
  974. return s:err('curl executable or ruby support not found')
  975. endif
  976. unlet g:loaded_plug
  977. echo 'Downloaded '. s:plug_source
  978. return 1
  979. endfunction
  980. function! s:upgrade_specs()
  981. for spec in values(g:plugs)
  982. let spec.frozen = get(spec, 'frozen', 0)
  983. endfor
  984. endfunction
  985. function! s:status()
  986. call s:prepare()
  987. call append(0, 'Checking plugins')
  988. call append(1, '')
  989. let ecnt = 0
  990. let [cnt, total] = [0, len(g:plugs)]
  991. for [name, spec] in items(g:plugs)
  992. if has_key(spec, 'uri')
  993. if isdirectory(spec.dir)
  994. let [valid, msg] = s:git_valid(spec, 1, 1)
  995. else
  996. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  997. endif
  998. else
  999. if isdirectory(spec.dir)
  1000. let [valid, msg] = [1, 'OK']
  1001. else
  1002. let [valid, msg] = [0, 'Not found.']
  1003. endif
  1004. endif
  1005. let cnt += 1
  1006. let ecnt += !valid
  1007. call s:progress_bar(2, repeat('=', cnt), total)
  1008. call append(3, s:format_message(valid, name, msg))
  1009. normal! 2G
  1010. redraw
  1011. endfor
  1012. call setline(1, 'Finished. '.ecnt.' error(s).')
  1013. normal! gg
  1014. endfunction
  1015. function! s:is_preview_window_open()
  1016. silent! wincmd P
  1017. if &previewwindow
  1018. wincmd p
  1019. return 1
  1020. endif
  1021. return 0
  1022. endfunction
  1023. function! s:preview_commit()
  1024. if b:plug_preview < 0
  1025. let b:plug_preview = !s:is_preview_window_open()
  1026. endif
  1027. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  1028. if !empty(sha)
  1029. let lnum = line('.')
  1030. while lnum > 1
  1031. let lnum -= 1
  1032. let line = getline(lnum)
  1033. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  1034. if !empty(name)
  1035. let dir = g:plugs[name].dir
  1036. if isdirectory(dir)
  1037. execute 'cd '.s:esc(dir)
  1038. execute 'pedit '.sha
  1039. wincmd P
  1040. setlocal filetype=git buftype=nofile nobuflisted
  1041. execute 'silent read !git show '.sha
  1042. normal! ggdd
  1043. wincmd p
  1044. cd -
  1045. endif
  1046. break
  1047. endif
  1048. endwhile
  1049. endif
  1050. endfunction
  1051. function! s:section(flags)
  1052. call search('\(^- \)\@<=.', a:flags)
  1053. endfunction
  1054. function! s:diff()
  1055. call s:prepare()
  1056. call append(0, 'Collecting updated changes ...')
  1057. normal! gg
  1058. redraw
  1059. let cnt = 0
  1060. for [k, v] in items(g:plugs)
  1061. if !isdirectory(v.dir) || !s:is_managed(k)
  1062. continue
  1063. endif
  1064. execute 'cd '.s:esc(v.dir)
  1065. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"')
  1066. if !v:shell_error && !empty(diff)
  1067. call append(1, '')
  1068. call append(2, '- '.k.':')
  1069. call append(3, map(split(diff, '\n'), '" ". v:val'))
  1070. let cnt += 1
  1071. normal! gg
  1072. redraw
  1073. endif
  1074. cd -
  1075. endfor
  1076. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  1077. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  1078. normal! gg
  1079. endfunction
  1080. let s:first_rtp = s:esc(get(split(&rtp, ','), 0, ''))
  1081. let s:last_rtp = s:esc(get(split(&rtp, ','), -1, ''))
  1082. if exists('g:plugs')
  1083. let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
  1084. call s:upgrade_specs()
  1085. call s:define_commands()
  1086. endif
  1087. let &cpo = s:cpo_save
  1088. unlet s:cpo_save