plug.vim 35 KB

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