plug.vim 33 KB

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