plug.vim 36 KB

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