plug.vim 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255
  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 = 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 on
  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, ...)
  354. call s:update_impl(0, a:force, a:000)
  355. endfunction
  356. function! s:update(force, ...)
  357. call s:update_impl(1, a:force, a:000)
  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>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> ]] :silent! call <SID>section('')<cr>
  436. nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
  437. let b:plug_preview = -1
  438. let s:plug_buf = winbufnr(0)
  439. call s:assign_name()
  440. endif
  441. silent! unmap <buffer> <cr>
  442. silent! unmap <buffer> L
  443. silent! unmap <buffer> X
  444. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline modifiable
  445. setf vim-plug
  446. call s:syntax()
  447. endfunction
  448. function! s:assign_name()
  449. " Assign buffer name
  450. let prefix = '[Plugins]'
  451. let name = prefix
  452. let idx = 2
  453. while bufexists(name)
  454. let name = printf('%s (%s)', prefix, idx)
  455. let idx = idx + 1
  456. endwhile
  457. silent! execute 'f' fnameescape(name)
  458. endfunction
  459. function! s:do(pull, force, todo)
  460. for [name, spec] in items(a:todo)
  461. if !isdirectory(spec.dir)
  462. continue
  463. endif
  464. execute 'cd' s:esc(spec.dir)
  465. let installed = has_key(s:prev_update.new, name)
  466. let updated = installed ? 0 :
  467. \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"')))
  468. if a:force || installed || updated
  469. call append(3, '- Post-update hook for '. name .' ... ')
  470. let type = type(spec.do)
  471. if type == s:TYPE.string
  472. try
  473. " FIXME: Escaping is incomplete. We could use shellescape with eval,
  474. " but it won't work on Windows.
  475. let g:_plug_do = '!'.escape(spec.do, '#!%')
  476. execute "normal! :execute g:_plug_do\<cr>\<cr>"
  477. finally
  478. let result = v:shell_error ? ('Exit status: '.v:shell_error) : 'Done!'
  479. unlet g:_plug_do
  480. endtry
  481. elseif type == s:TYPE.funcref
  482. try
  483. let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
  484. call spec.do({ 'name': name, 'status': status, 'force': a:force })
  485. let result = 'Done!'
  486. catch
  487. let result = 'Error: ' . v:exception
  488. endtry
  489. else
  490. let result = 'Error: Invalid type!'
  491. endif
  492. call setline(4, getline(4) . result)
  493. endif
  494. cd -
  495. endfor
  496. endfunction
  497. function! s:finish(pull)
  498. call append(3, '- Finishing ... ')
  499. redraw
  500. call plug#helptags()
  501. call plug#end()
  502. call setline(4, getline(4) . 'Done!')
  503. normal! gg
  504. call s:syntax()
  505. redraw
  506. let msgs = []
  507. if !empty(s:prev_update.errors)
  508. call add(msgs, "Press 'R' to retry.")
  509. endif
  510. if a:pull
  511. call add(msgs, "Press 'D' to see the updated changes.")
  512. endif
  513. echo join(msgs, ' ')
  514. endfunction
  515. function! s:retry()
  516. if empty(s:prev_update.errors)
  517. return
  518. endif
  519. call s:update_impl(s:prev_update.pull, s:prev_update.force,
  520. \ extend(copy(s:prev_update.errors), [s:prev_update.threads]))
  521. endfunction
  522. function! s:is_managed(name)
  523. return has_key(g:plugs[a:name], 'uri')
  524. endfunction
  525. function! s:names(...)
  526. return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')
  527. endfunction
  528. function! s:update_impl(pull, force, args) abort
  529. let st = reltime()
  530. let args = copy(a:args)
  531. let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
  532. \ remove(args, -1) : get(g:, 'plug_threads', 16)
  533. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  534. let todo = empty(args) ? filter(managed, '!v:val.frozen') :
  535. \ filter(managed, 'index(args, v:key) >= 0')
  536. if empty(todo)
  537. echohl WarningMsg
  538. echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.'
  539. echohl None
  540. return
  541. endif
  542. if !isdirectory(g:plug_home)
  543. try
  544. call mkdir(g:plug_home, 'p')
  545. catch
  546. return s:err(printf('Invalid plug directory: %s.'
  547. \ 'Try to call plug#begin with a valid directory', g:plug_home))
  548. endtry
  549. endif
  550. call s:prepare()
  551. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  552. call append(1, '['. s:lpad('', len(todo)) .']')
  553. normal! 2G
  554. redraw
  555. let s:prev_update = { 'errors': [], 'pull': a:pull, 'force': a:force, 'new': {}, 'threads': threads }
  556. if has('ruby') && threads > 1
  557. try
  558. let imd = &imd
  559. if s:mac_gui
  560. set noimd
  561. endif
  562. call s:update_parallel(a:pull, todo, threads)
  563. catch
  564. let lines = getline(4, '$')
  565. let printed = {}
  566. silent 4,$d
  567. for line in lines
  568. let name = get(matchlist(line, '^. \([^:]\+\):'), 1, '')
  569. if empty(name) || !has_key(printed, name)
  570. call append('$', line)
  571. if !empty(name)
  572. let printed[name] = 1
  573. if line[0] == 'x' && index(s:prev_update.errors, name) < 0
  574. call add(s:prev_update.errors, name)
  575. end
  576. endif
  577. endif
  578. endfor
  579. finally
  580. let &imd = imd
  581. endtry
  582. else
  583. call s:update_serial(a:pull, todo)
  584. endif
  585. call s:do(a:pull, a:force, filter(copy(todo), 'has_key(v:val, "do")'))
  586. call s:finish(a:pull)
  587. call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(st)))[0] . ' sec.')
  588. endfunction
  589. function! s:update_progress(pull, cnt, bar, total)
  590. call setline(1, (a:pull ? 'Updating' : 'Installing').
  591. \ ' plugins ('.a:cnt.'/'.a:total.')')
  592. call s:progress_bar(2, a:bar, a:total)
  593. normal! 2G
  594. redraw
  595. endfunction
  596. function! s:update_serial(pull, todo)
  597. let base = g:plug_home
  598. let todo = copy(a:todo)
  599. let total = len(todo)
  600. let done = {}
  601. let bar = ''
  602. for [name, spec] in items(todo)
  603. let done[name] = 1
  604. if isdirectory(spec.dir)
  605. execute 'cd' s:esc(spec.dir)
  606. let [valid, msg] = s:git_valid(spec, 0, 0)
  607. if valid
  608. let result = a:pull ?
  609. \ s:system(
  610. \ printf('git checkout -q %s 2>&1 && git pull --no-rebase origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  611. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  612. let error = a:pull ? v:shell_error != 0 : 0
  613. else
  614. let result = msg
  615. let error = 1
  616. endif
  617. cd -
  618. else
  619. let result = s:system(
  620. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  621. \ s:shellesc(spec.uri),
  622. \ s:shellesc(spec.branch),
  623. \ s:shellesc(s:trim(spec.dir)),
  624. \ s:shellesc(spec.dir)))
  625. let error = v:shell_error != 0
  626. if !error | let s:prev_update.new[name] = 1 | endif
  627. endif
  628. let bar .= error ? 'x' : '='
  629. if error
  630. call add(s:prev_update.errors, name)
  631. endif
  632. call append(3, s:format_message(!error, name, result))
  633. call s:update_progress(a:pull, len(done), bar, total)
  634. endfor
  635. endfunction
  636. function! s:update_parallel(pull, todo, threads)
  637. ruby << EOF
  638. module PlugStream
  639. SEP = ["\r", "\n", nil]
  640. def get_line
  641. buffer = ''
  642. loop do
  643. char = readchar rescue return
  644. if SEP.include? char.chr
  645. buffer << $/
  646. break
  647. else
  648. buffer << char
  649. end
  650. end
  651. buffer
  652. end
  653. end unless defined?(PlugStream)
  654. def esc arg
  655. %["#{arg.gsub('"', '\"')}"]
  656. end
  657. def killall pid
  658. pids = [pid]
  659. unless `which pgrep`.empty?
  660. children = pids
  661. until children.empty?
  662. children = children.map { |pid|
  663. `pgrep -P #{pid}`.lines.map { |l| l.chomp }
  664. }.flatten
  665. pids += children
  666. end
  667. end
  668. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  669. end
  670. require 'thread'
  671. require 'fileutils'
  672. require 'timeout'
  673. running = true
  674. iswin = VIM::evaluate('s:is_win').to_i == 1
  675. pull = VIM::evaluate('a:pull').to_i == 1
  676. base = VIM::evaluate('g:plug_home')
  677. all = VIM::evaluate('a:todo')
  678. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  679. tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
  680. nthr = VIM::evaluate('a:threads').to_i
  681. maxy = VIM::evaluate('winheight(".")').to_i
  682. cd = iswin ? 'cd /d' : 'cd'
  683. tot = VIM::evaluate('len(a:todo)') || 0
  684. bar = ''
  685. skip = 'Already installed'
  686. mtx = Mutex.new
  687. take1 = proc { mtx.synchronize { running && all.shift } }
  688. logh = proc {
  689. cnt = bar.length
  690. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  691. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  692. VIM::command('normal! 2G')
  693. VIM::command('redraw') unless iswin
  694. }
  695. where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
  696. log = proc { |name, result, type|
  697. mtx.synchronize do
  698. ing = ![true, false].include?(type)
  699. bar += type ? '=' : 'x' unless ing
  700. b = case type
  701. when :install then '+' when :update then '*'
  702. when true, nil then '-' else
  703. VIM::command("call add(s:prev_update.errors, '#{name}')")
  704. 'x'
  705. end
  706. result =
  707. if type || type.nil?
  708. ["#{b} #{name}: #{result.lines.to_a.last}"]
  709. elsif result =~ /^Interrupted|^Timeout/
  710. ["#{b} #{name}: #{result}"]
  711. else
  712. ["#{b} #{name}"] + result.lines.map { |l| " " << l }
  713. end
  714. if lnum = where.call(name)
  715. $curbuf.delete lnum
  716. lnum = 4 if ing && lnum > maxy
  717. end
  718. result.each_with_index do |line, offset|
  719. $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
  720. end
  721. logh.call
  722. end
  723. }
  724. bt = proc { |cmd, name, type|
  725. tried = timeout = 0
  726. begin
  727. tried += 1
  728. timeout += limit
  729. fd = nil
  730. data = ''
  731. if iswin
  732. Timeout::timeout(timeout) do
  733. tmp = VIM::evaluate('tempname()')
  734. system("#{cmd} > #{tmp}")
  735. data = File.read(tmp).chomp
  736. File.unlink tmp rescue nil
  737. end
  738. else
  739. fd = IO.popen(cmd).extend(PlugStream)
  740. first_line = true
  741. log_prob = 1.0 / nthr
  742. while line = Timeout::timeout(timeout) { fd.get_line }
  743. data << line
  744. log.call name, line.chomp, type if name && (first_line || rand < log_prob)
  745. first_line = false
  746. end
  747. fd.close
  748. end
  749. [$? == 0, data.chomp]
  750. rescue Timeout::Error, Interrupt => e
  751. if fd && !fd.closed?
  752. killall fd.pid
  753. fd.close
  754. end
  755. if e.is_a?(Timeout::Error) && tried < tries
  756. 3.downto(1) do |countdown|
  757. s = countdown > 1 ? 's' : ''
  758. log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
  759. sleep 1
  760. end
  761. log.call name, 'Retrying ...', type
  762. retry
  763. end
  764. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  765. end
  766. }
  767. main = Thread.current
  768. threads = []
  769. watcher = Thread.new {
  770. while VIM::evaluate('getchar(1)')
  771. sleep 0.1
  772. end
  773. mtx.synchronize do
  774. running = false
  775. threads.each { |t| t.raise Interrupt }
  776. end
  777. threads.each { |t| t.join rescue nil }
  778. main.kill
  779. }
  780. refresh = Thread.new {
  781. while true
  782. mtx.synchronize do
  783. break unless running
  784. VIM::command('noautocmd normal! a')
  785. end
  786. sleep 0.2
  787. end
  788. } if VIM::evaluate('s:mac_gui') == 1
  789. progress = iswin ? '' : '--progress'
  790. [all.length, nthr].min.times do
  791. mtx.synchronize do
  792. threads << Thread.new {
  793. while pair = take1.call
  794. name = pair.first
  795. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  796. branch = esc branch
  797. subm = "git submodule update --init --recursive 2>&1"
  798. exists = File.directory? dir
  799. ok, result =
  800. if exists
  801. dir = esc dir
  802. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil
  803. current_uri = data.lines.to_a.last
  804. if !ret
  805. if data =~ /^Interrupted|^Timeout/
  806. [false, data]
  807. else
  808. [false, [data.chomp, "PlugClean required."].join($/)]
  809. end
  810. elsif current_uri.sub(/git::?@/, '') != uri.sub(/git::?@/, '')
  811. [false, ["Invalid URI: #{current_uri}",
  812. "Expected: #{uri}",
  813. "PlugClean required."].join($/)]
  814. else
  815. if pull
  816. log.call name, 'Updating ...', :update
  817. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull --no-rebase origin #{branch} #{progress} 2>&1 && #{subm})", name, :update
  818. else
  819. [true, skip]
  820. end
  821. end
  822. else
  823. d = esc dir.sub(%r{[\\/]+$}, '')
  824. log.call name, 'Installing ...', :install
  825. bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install
  826. end
  827. mtx.synchronize { VIM::command("let s:prev_update.new['#{name}'] = 1") } if !exists && ok
  828. log.call name, result, ok
  829. end
  830. } if running
  831. end
  832. end
  833. threads.each { |t| t.join rescue nil }
  834. logh.call
  835. refresh.kill if refresh
  836. watcher.kill
  837. EOF
  838. endfunction
  839. function! s:shellesc(arg)
  840. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  841. endfunction
  842. function! s:glob_dir(path)
  843. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  844. endfunction
  845. function! s:progress_bar(line, bar, total)
  846. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  847. endfunction
  848. function! s:compare_git_uri(a, b)
  849. let a = substitute(a:a, 'git:\{1,2}@', '', '')
  850. let b = substitute(a:b, 'git:\{1,2}@', '', '')
  851. return a ==# b
  852. endfunction
  853. function! s:format_message(ok, name, message)
  854. if a:ok
  855. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  856. else
  857. let lines = map(split(a:message, '\n'), '" ".v:val')
  858. return extend([printf('x %s:', a:name)], lines)
  859. endif
  860. endfunction
  861. function! s:system(cmd)
  862. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  863. endfunction
  864. function! s:system_chomp(str)
  865. let ret = s:system(a:str)
  866. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  867. endfunction
  868. function! s:git_valid(spec, check_branch, cd)
  869. let ret = 1
  870. let msg = 'OK'
  871. if isdirectory(a:spec.dir)
  872. if a:cd | execute 'cd' s:esc(a:spec.dir) | endif
  873. let result = split(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url'), '\n')
  874. let remote = result[-1]
  875. if v:shell_error
  876. let msg = join([remote, 'PlugClean required.'], "\n")
  877. let ret = 0
  878. elseif !s:compare_git_uri(remote, a:spec.uri)
  879. let msg = join(['Invalid URI: '.remote,
  880. \ 'Expected: '.a:spec.uri,
  881. \ 'PlugClean required.'], "\n")
  882. let ret = 0
  883. elseif a:check_branch
  884. let branch = result[0]
  885. if a:spec.branch !=# branch
  886. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1')
  887. if a:spec.branch !=# tag
  888. let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
  889. \ (empty(tag) ? branch : tag), a:spec.branch)
  890. let ret = 0
  891. endif
  892. endif
  893. endif
  894. if a:cd | cd - | endif
  895. else
  896. let msg = 'Not found'
  897. let ret = 0
  898. endif
  899. return [ret, msg]
  900. endfunction
  901. function! s:clean(force)
  902. call s:prepare()
  903. call append(0, 'Searching for unused plugins in '.g:plug_home)
  904. call append(1, '')
  905. " List of valid directories
  906. let dirs = []
  907. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  908. let [cnt, total] = [0, len(managed)]
  909. for spec in values(managed)
  910. if s:git_valid(spec, 0, 1)[0]
  911. call add(dirs, spec.dir)
  912. endif
  913. let cnt += 1
  914. call s:progress_bar(2, repeat('=', cnt), total)
  915. normal! 2G
  916. redraw
  917. endfor
  918. let allowed = {}
  919. for dir in dirs
  920. let allowed[dir] = 1
  921. for child in s:glob_dir(dir)
  922. let allowed[child] = 1
  923. endfor
  924. endfor
  925. let todo = []
  926. let found = sort(s:glob_dir(g:plug_home))
  927. while !empty(found)
  928. let f = remove(found, 0)
  929. if !has_key(allowed, f) && isdirectory(f)
  930. call add(todo, f)
  931. call append(line('$'), '- ' . f)
  932. let found = filter(found, 'stridx(v:val, f) != 0')
  933. end
  934. endwhile
  935. normal! G
  936. redraw
  937. if empty(todo)
  938. call append(line('$'), 'Already clean.')
  939. else
  940. call inputsave()
  941. let yes = a:force || (input('Proceed? (Y/N) ') =~? '^y')
  942. call inputrestore()
  943. if yes
  944. for dir in todo
  945. if isdirectory(dir)
  946. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  947. endif
  948. endfor
  949. call append(line('$'), 'Removed.')
  950. else
  951. call append(line('$'), 'Cancelled.')
  952. endif
  953. endif
  954. normal! G
  955. endfunction
  956. function! s:upgrade()
  957. if executable('curl')
  958. let mee = s:shellesc(s:me)
  959. let new = s:shellesc(s:me . '.new')
  960. echo 'Downloading '. s:plug_source
  961. redraw
  962. let mv = s:is_win ? 'move /Y' : 'mv -f'
  963. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  964. call system(printf(
  965. \ 'curl -fLo %s %s && '.cp.' %s %s.old && '.mv.' %s %s',
  966. \ new, s:plug_source, mee, mee, new, mee))
  967. if v:shell_error
  968. return s:err('Error upgrading vim-plug')
  969. endif
  970. elseif has('ruby')
  971. echo 'Downloading '. s:plug_source
  972. try
  973. ruby << EOF
  974. require 'open-uri'
  975. require 'fileutils'
  976. me = VIM::evaluate('s:me')
  977. old = me + '.old'
  978. new = me + '.new'
  979. File.open(new, 'w') do |f|
  980. f << open(VIM::evaluate('s:plug_source')).read
  981. end
  982. FileUtils.cp me, old
  983. File.rename new, me
  984. EOF
  985. catch
  986. return s:err('Error upgrading vim-plug')
  987. endtry
  988. else
  989. return s:err('curl executable or ruby support not found')
  990. endif
  991. unlet g:loaded_plug
  992. echo 'Downloaded '. s:plug_source
  993. return 1
  994. endfunction
  995. function! s:upgrade_specs()
  996. for spec in values(g:plugs)
  997. let spec.frozen = get(spec, 'frozen', 0)
  998. endfor
  999. endfunction
  1000. function! s:status()
  1001. call s:prepare()
  1002. call append(0, 'Checking plugins')
  1003. call append(1, '')
  1004. let ecnt = 0
  1005. let unloaded = 0
  1006. let [cnt, total] = [0, len(g:plugs)]
  1007. for [name, spec] in items(g:plugs)
  1008. if has_key(spec, 'uri')
  1009. if isdirectory(spec.dir)
  1010. let [valid, msg] = s:git_valid(spec, 1, 1)
  1011. else
  1012. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  1013. endif
  1014. else
  1015. if isdirectory(spec.dir)
  1016. let [valid, msg] = [1, 'OK']
  1017. else
  1018. let [valid, msg] = [0, 'Not found.']
  1019. endif
  1020. endif
  1021. let cnt += 1
  1022. let ecnt += !valid
  1023. " `s:loaded` entry can be missing if PlugUpgraded
  1024. if valid && get(s:loaded, spec.dir, -1) == 0
  1025. let unloaded = 1
  1026. let msg .= ' (not loaded)'
  1027. endif
  1028. call s:progress_bar(2, repeat('=', cnt), total)
  1029. call append(3, s:format_message(valid, name, msg))
  1030. normal! 2G
  1031. redraw
  1032. endfor
  1033. call setline(1, 'Finished. '.ecnt.' error(s).')
  1034. normal! gg
  1035. setlocal nomodifiable
  1036. if unloaded
  1037. echo "Press 'L' on each line to load plugin"
  1038. nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  1039. xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  1040. end
  1041. endfunction
  1042. function! s:status_load(lnum)
  1043. let line = getline(a:lnum)
  1044. let matches = matchlist(line, '^- \([^:]*\):.*(not loaded)$')
  1045. if !empty(matches)
  1046. let name = matches[1]
  1047. call plug#load(name)
  1048. setlocal modifiable
  1049. call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
  1050. setlocal nomodifiable
  1051. endif
  1052. endfunction
  1053. function! s:is_preview_window_open()
  1054. silent! wincmd P
  1055. if &previewwindow
  1056. wincmd p
  1057. return 1
  1058. endif
  1059. return 0
  1060. endfunction
  1061. function! s:find_name(lnum)
  1062. for lnum in reverse(range(1, a:lnum))
  1063. let line = getline(lnum)
  1064. if empty(line)
  1065. return ''
  1066. endif
  1067. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  1068. if !empty(name)
  1069. return name
  1070. endif
  1071. endfor
  1072. return ''
  1073. endfunction
  1074. function! s:preview_commit()
  1075. if b:plug_preview < 0
  1076. let b:plug_preview = !s:is_preview_window_open()
  1077. endif
  1078. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  1079. if empty(sha)
  1080. return
  1081. endif
  1082. let name = s:find_name(line('.'))
  1083. if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
  1084. return
  1085. endif
  1086. execute 'pedit' sha
  1087. wincmd P
  1088. setlocal filetype=git buftype=nofile nobuflisted
  1089. execute 'cd' s:esc(g:plugs[name].dir)
  1090. execute 'silent read !git show' sha
  1091. cd -
  1092. normal! gg"_dd
  1093. wincmd p
  1094. endfunction
  1095. function! s:section(flags)
  1096. call search('\(^- \)\@<=.', a:flags)
  1097. endfunction
  1098. function! s:diff()
  1099. call s:prepare()
  1100. call append(0, 'Collecting updated changes ...')
  1101. normal! gg
  1102. redraw
  1103. let cnt = 0
  1104. for [k, v] in items(g:plugs)
  1105. if !isdirectory(v.dir) || !s:is_managed(k)
  1106. continue
  1107. endif
  1108. execute 'cd' s:esc(v.dir)
  1109. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"')
  1110. if !v:shell_error && !empty(diff)
  1111. call append(1, '')
  1112. call append(2, '- '.k.':')
  1113. call append(3, map(split(diff, '\n'), '" ". v:val'))
  1114. let cnt += 1
  1115. normal! gg
  1116. redraw
  1117. endif
  1118. cd -
  1119. endfor
  1120. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  1121. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  1122. nnoremap <silent> <buffer> X :call <SID>revert()<cr>
  1123. normal! gg
  1124. setlocal nomodifiable
  1125. if cnt > 0
  1126. echo "Press 'X' on each block to revert the update"
  1127. endif
  1128. endfunction
  1129. function! s:revert()
  1130. let name = s:find_name(line('.'))
  1131. if empty(name) || !has_key(g:plugs, name) ||
  1132. \ input(printf('Revert the update of %s? (Y/N) ', name)) !~? '^y'
  1133. return
  1134. endif
  1135. execute 'cd' s:esc(g:plugs[name].dir)
  1136. call system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch))
  1137. cd -
  1138. setlocal modifiable
  1139. normal! "_dap
  1140. setlocal nomodifiable
  1141. echo 'Reverted.'
  1142. endfunction
  1143. let s:first_rtp = s:esc(get(split(&rtp, ','), 0, ''))
  1144. let s:last_rtp = s:esc(get(split(&rtp, ','), -1, ''))
  1145. if exists('g:plugs')
  1146. let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
  1147. call s:upgrade_specs()
  1148. call s:define_commands()
  1149. endif
  1150. let &cpo = s:cpo_save
  1151. unlet s:cpo_save