plug.vim 41 KB

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