plug.vim 42 KB

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