plug.vim 34 KB

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