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