plug.vim 33 KB

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