plug.vim 33 KB

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