plug.vim 34 KB

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