plug.vim 33 KB

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