plug.vim 36 KB

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