plug.vim 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  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 [cnt, total] = [0, len(g:plugs)]
  933. for [name, spec] in items(g:plugs)
  934. if !s:is_managed(name) || s:git_valid(spec, 0, 1)[0]
  935. call add(dirs, spec.dir)
  936. endif
  937. let cnt += 1
  938. call s:progress_bar(2, repeat('=', cnt), total)
  939. normal! 2G
  940. redraw
  941. endfor
  942. let allowed = {}
  943. for dir in dirs
  944. let allowed[dir] = 1
  945. for child in s:glob_dir(dir)
  946. let allowed[child] = 1
  947. endfor
  948. endfor
  949. let todo = []
  950. let found = sort(s:glob_dir(g:plug_home))
  951. while !empty(found)
  952. let f = remove(found, 0)
  953. if !has_key(allowed, f) && isdirectory(f)
  954. call add(todo, f)
  955. call append(line('$'), '- ' . f)
  956. let found = filter(found, 'stridx(v:val, f) != 0')
  957. end
  958. endwhile
  959. normal! G
  960. redraw
  961. if empty(todo)
  962. call append(line('$'), 'Already clean.')
  963. else
  964. call inputsave()
  965. let yes = a:force || (input('Proceed? (Y/N) ') =~? '^y')
  966. call inputrestore()
  967. if yes
  968. for dir in todo
  969. if isdirectory(dir)
  970. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  971. endif
  972. endfor
  973. call append(line('$'), 'Removed.')
  974. else
  975. call append(line('$'), 'Cancelled.')
  976. endif
  977. endif
  978. normal! G
  979. endfunction
  980. function! s:upgrade()
  981. let new = s:me . '.new'
  982. echo 'Downloading '. s:plug_source
  983. redraw
  984. try
  985. if executable('curl')
  986. let output = system(printf('curl -fLo %s %s', s:shellesc(new), s:plug_source))
  987. if v:shell_error
  988. throw get(split(output, '\n'), -1, v:shell_error)
  989. endif
  990. elseif has('ruby')
  991. ruby << EOF
  992. require 'open-uri'
  993. File.open(VIM::evaluate('new'), 'w') do |f|
  994. f << open(VIM::evaluate('s:plug_source')).read
  995. end
  996. EOF
  997. else
  998. return s:err('curl executable or ruby support not found')
  999. endif
  1000. catch
  1001. return s:err('Error upgrading vim-plug: '. v:exception)
  1002. endtry
  1003. if readfile(s:me) ==# readfile(new)
  1004. echo 'vim-plug is up-to-date'
  1005. silent! call delete(new)
  1006. return 0
  1007. else
  1008. call rename(s:me, s:me . '.old')
  1009. call rename(new, s:me)
  1010. unlet g:loaded_plug
  1011. echo 'vim-plug is upgraded'
  1012. return 1
  1013. endif
  1014. endfunction
  1015. function! s:upgrade_specs()
  1016. for spec in values(g:plugs)
  1017. let spec.frozen = get(spec, 'frozen', 0)
  1018. endfor
  1019. endfunction
  1020. function! s:status()
  1021. call s:prepare()
  1022. call append(0, 'Checking plugins')
  1023. call append(1, '')
  1024. let ecnt = 0
  1025. let unloaded = 0
  1026. let [cnt, total] = [0, len(g:plugs)]
  1027. for [name, spec] in items(g:plugs)
  1028. if has_key(spec, 'uri')
  1029. if isdirectory(spec.dir)
  1030. let [valid, msg] = s:git_valid(spec, 1, 1)
  1031. else
  1032. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  1033. endif
  1034. else
  1035. if isdirectory(spec.dir)
  1036. let [valid, msg] = [1, 'OK']
  1037. else
  1038. let [valid, msg] = [0, 'Not found.']
  1039. endif
  1040. endif
  1041. let cnt += 1
  1042. let ecnt += !valid
  1043. " `s:loaded` entry can be missing if PlugUpgraded
  1044. if valid && get(s:loaded, name, -1) == 0
  1045. let unloaded = 1
  1046. let msg .= ' (not loaded)'
  1047. endif
  1048. call s:progress_bar(2, repeat('=', cnt), total)
  1049. call append(3, s:format_message(valid, name, msg))
  1050. normal! 2G
  1051. redraw
  1052. endfor
  1053. call setline(1, 'Finished. '.ecnt.' error(s).')
  1054. normal! gg
  1055. setlocal nomodifiable
  1056. if unloaded
  1057. echo "Press 'L' on each line to load plugin, or 'U' to update"
  1058. nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  1059. xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  1060. end
  1061. endfunction
  1062. function! s:extract_name(str, prefix, suffix)
  1063. return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$')
  1064. endfunction
  1065. function! s:status_load(lnum)
  1066. let line = getline(a:lnum)
  1067. let name = s:extract_name(line, '-', '(not loaded)')
  1068. if !empty(name)
  1069. call plug#load(name)
  1070. setlocal modifiable
  1071. call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
  1072. setlocal nomodifiable
  1073. endif
  1074. endfunction
  1075. function! s:status_update() range
  1076. let lines = getline(a:firstline, a:lastline)
  1077. let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)')
  1078. if !empty(names)
  1079. echo
  1080. execute 'PlugUpdate' join(names)
  1081. endif
  1082. endfunction
  1083. function! s:is_preview_window_open()
  1084. silent! wincmd P
  1085. if &previewwindow
  1086. wincmd p
  1087. return 1
  1088. endif
  1089. return 0
  1090. endfunction
  1091. function! s:find_name(lnum)
  1092. for lnum in reverse(range(1, a:lnum))
  1093. let line = getline(lnum)
  1094. if empty(line)
  1095. return ''
  1096. endif
  1097. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  1098. if !empty(name)
  1099. return name
  1100. endif
  1101. endfor
  1102. return ''
  1103. endfunction
  1104. function! s:preview_commit()
  1105. if b:plug_preview < 0
  1106. let b:plug_preview = !s:is_preview_window_open()
  1107. endif
  1108. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  1109. if empty(sha)
  1110. return
  1111. endif
  1112. let name = s:find_name(line('.'))
  1113. if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
  1114. return
  1115. endif
  1116. execute 'pedit' sha
  1117. wincmd P
  1118. setlocal filetype=git buftype=nofile nobuflisted
  1119. execute 'cd' s:esc(g:plugs[name].dir)
  1120. execute 'silent read !git show' sha
  1121. cd -
  1122. normal! gg"_dd
  1123. wincmd p
  1124. endfunction
  1125. function! s:section(flags)
  1126. call search('\(^- \)\@<=.', a:flags)
  1127. endfunction
  1128. function! s:diff()
  1129. call s:prepare()
  1130. call append(0, 'Collecting updated changes ...')
  1131. normal! gg
  1132. redraw
  1133. let cnt = 0
  1134. for [k, v] in items(g:plugs)
  1135. if !isdirectory(v.dir) || !s:is_managed(k)
  1136. continue
  1137. endif
  1138. execute 'cd' s:esc(v.dir)
  1139. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"')
  1140. if !v:shell_error && !empty(diff)
  1141. call append(1, '')
  1142. call append(2, '- '.k.':')
  1143. call append(3, map(split(diff, '\n'), '" ". v:val'))
  1144. let cnt += 1
  1145. normal! gg
  1146. redraw
  1147. endif
  1148. cd -
  1149. endfor
  1150. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  1151. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  1152. nnoremap <silent> <buffer> X :call <SID>revert()<cr>
  1153. normal! gg
  1154. setlocal nomodifiable
  1155. if cnt > 0
  1156. echo "Press 'X' on each block to revert the update"
  1157. endif
  1158. endfunction
  1159. function! s:revert()
  1160. let name = s:find_name(line('.'))
  1161. if empty(name) || !has_key(g:plugs, name) ||
  1162. \ input(printf('Revert the update of %s? (Y/N) ', name)) !~? '^y'
  1163. return
  1164. endif
  1165. execute 'cd' s:esc(g:plugs[name].dir)
  1166. call system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch))
  1167. cd -
  1168. setlocal modifiable
  1169. normal! "_dap
  1170. setlocal nomodifiable
  1171. echo 'Reverted.'
  1172. endfunction
  1173. function! s:split_rtp()
  1174. return split(&rtp, '\\\@<!,')
  1175. endfunction
  1176. let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, ''))
  1177. let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, ''))
  1178. if exists('g:plugs')
  1179. let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
  1180. call s:upgrade_specs()
  1181. call s:define_commands()
  1182. endif
  1183. let &cpo = s:cpo_save
  1184. unlet s:cpo_save